Implement advisory locking for WIM overwrites
authorEric Biggers <ebiggers3@gmail.com>
Thu, 22 Nov 2012 04:25:04 +0000 (22:25 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Thu, 22 Nov 2012 04:25:04 +0000 (22:25 -0600)
config.h.in
configure.ac
src/util.c
src/wim.c
src/wimlib.h
src/wimlib_internal.h
src/write.c

index 05d6092..7b52011 100644 (file)
@@ -48,6 +48,9 @@
 /* Define to 1 if you have the <errno.h> header file. */
 #undef HAVE_ERRNO_H
 
+/* Define to 1 if you have the `flock' function. */
+#undef HAVE_FLOCK
+
 /* Define if you have the iconv() function and it works. */
 #undef HAVE_ICONV
 
@@ -87,6 +90,9 @@
 /* Define to 1 if you have the <sys/endian.h> header file. */
 #undef HAVE_SYS_ENDIAN_H
 
+/* Define to 1 if you have the <sys/file.h> header file. */
+#undef HAVE_SYS_FILE_H
+
 /* Define to 1 if you have the <sys/param.h> header file. */
 #undef HAVE_SYS_PARAM_H
 
index 03b44e6..c35b62a 100644 (file)
@@ -29,10 +29,10 @@ AC_CONFIG_FILES([Makefile
 AC_PROG_CC
 AM_PROG_CC_C_O
 
-AC_CHECK_FUNCS([utimensat lutimes utime])
+AC_CHECK_FUNCS([utimensat lutimes utime flock])
 AC_CHECK_HEADERS([endian.h byteswap.h sys/byteorder.h sys/endian.h \
                  sys/param.h machine/endian.h alloca.h stdlib.h stdarg.h \
-                 errno.h attr/xattr.h utime.h])
+                 errno.h attr/xattr.h utime.h sys/file.h])
 
 
 AM_ICONV
index faa0d0b..3dbb5cd 100644 (file)
@@ -108,6 +108,8 @@ WIMLIBAPI int wimlib_set_print_errors(bool show_error_messages)
 static const char *error_strings[] = {
        [WIMLIB_ERR_SUCCESS]
                = "Success",
+       [WIMLIB_ERR_ALREADY_LOCKED]
+               = "The WIM is already locked for writing",
        [WIMLIB_ERR_COMPRESSED_LOOKUP_TABLE]
                = "Lookup table is compressed",
        [WIMLIB_ERR_DECOMPRESSION]
@@ -379,17 +381,19 @@ ssize_t full_write(int fd, const void *buf, size_t n)
 
 static bool seeded = false;
 
+static void seed_random()
+{
+       srand(time(NULL) * getpid());
+       seeded = true;
+}
+
 /* Fills @n bytes pointed to by @p with random alphanumeric characters. */
 void randomize_char_array_with_alnum(char p[], size_t n)
 {
-       int r;
-
-       if (!seeded) {
-               srand(time(NULL));
-               seeded = true;
-       }
+       if (!seeded)
+               seed_random();
        while (n--) {
-               r = rand() % 62;
+               int r = rand() % 62;
                if (r < 26)
                        *p++ = r + 'a';
                else if (r < 52)
@@ -402,10 +406,8 @@ void randomize_char_array_with_alnum(char p[], size_t n)
 /* Fills @n bytes pointer to by @p with random numbers. */
 void randomize_byte_array(u8 *p, size_t n)
 {
-       if (!seeded) {
-               srand(time(NULL));
-               seeded = true;
-       }
+       if (!seeded)
+               seed_random();
        while (n--)
                *p++ = rand();
 }
index 500fc1e..44653f1 100644 (file)
--- a/src/wim.c
+++ b/src/wim.c
@@ -43,7 +43,6 @@
 #include "lookup_table.h"
 #include "xml.h"
 
-
 static int print_metadata(WIMStruct *w)
 {
        DEBUG("Printing metadata for image %d", w->current_image);
@@ -421,7 +420,7 @@ WIMLIBAPI int wimlib_get_boot_idx(const WIMStruct *w)
 }
 
 /* Opens a WIM readable */
-int open_wim_readable(WIMStruct *w, const char *path)
+static int open_wim_readable(WIMStruct *w, const char *path)
 {
        if (w->fp != NULL)
                fclose(w->fp);
@@ -435,30 +434,6 @@ int open_wim_readable(WIMStruct *w, const char *path)
        return 0;
 }
 
-/* Opens a WIM writable */
-int open_wim_writable(WIMStruct *w, const char *path,
-                     bool trunc, bool readable)
-{
-       const char *mode;
-       if (trunc)
-               if (readable)
-                       mode = "w+b";
-               else
-                       mode = "wb";
-       else
-               mode = "r+b";
-
-       DEBUG("Opening `%s' read-write", path);
-       wimlib_assert(w->out_fp == NULL);
-       wimlib_assert(path != NULL);
-       w->out_fp = fopen(path, mode);
-       if (!w->out_fp) {
-               ERROR_WITH_ERRNO("Failed to open `%s' for writing", path);
-               return WIMLIB_ERR_OPEN;
-       }
-       return 0;
-}
-
 /*
  * Begins the reading of a WIM file; opens the file and reads its header and
  * lookup table, and optionally checks the integrity.
index 690f9de..0b0f04b 100644 (file)
@@ -667,6 +667,7 @@ typedef int (*wimlib_progress_func_t)(enum wimlib_progress_msg msg_type,
  */
 enum wimlib_error_code {
        WIMLIB_ERR_SUCCESS = 0,
+       WIMLIB_ERR_ALREADY_LOCKED,
        WIMLIB_ERR_COMPRESSED_LOOKUP_TABLE,
        WIMLIB_ERR_DECOMPRESSION,
        WIMLIB_ERR_DELETE_STAGING_DIR,
@@ -1575,6 +1576,11 @@ extern int wimlib_open_wim(const char *wim_file, int open_flags,
  *
  * @return 0 on success; nonzero on error.  This function may return any value
  * returned by wimlib_write() as well as the following error codes:
+ * @retval ::WIMLIB_ERR_ALREADY_LOCKED
+ *     The append-only overwrite mode was going to be used, but an exclusive
+ *     advisory lock on the on-disk WIM file could not be acquired, probably
+ *     because another thread or process was calling wimlib_overwrite() on the
+ *     same underlying on-disk file at the same time.
  * @retval ::WIMLIB_ERR_NO_FILENAME
  *     @a wim corresponds to a WIM created with wimlib_create_new_wim() rather
  *     than a WIM read with wimlib_open_wim().
@@ -1744,8 +1750,8 @@ extern void wimlib_print_wim_information(const WIMStruct *wim);
  *     image, indexed starting at 1, is returned.  If the keyword "all" or "*"
  *     was specified, ::WIMLIB_ALL_IMAGES is returned.  Otherwise,
  *     ::WIMLIB_NO_IMAGE is returned.  If @a image_name_or_num was @c NULL or
- *     the empty string, ::WIM_NO_IMAGE is returned, even if one or more images
- *     in @a wim has no name.
+ *     the empty string, ::WIMLIB_NO_IMAGE is returned, even if one or more
+ *     images in @a wim has no name.
  */
 extern int wimlib_resolve_image(WIMStruct *wim, const char *image_name_or_num);
 
index ac35c4f..1949783 100644 (file)
@@ -526,9 +526,6 @@ extern WIMStruct *new_wim_struct();
 extern int select_wim_image(WIMStruct *w, int image);
 extern int wim_hdr_flags_compression_type(int wim_hdr_flags);
 extern int for_image(WIMStruct *w, int image, int (*visitor)(WIMStruct *));
-extern int open_wim_readable(WIMStruct *w, const char *path);
-extern int open_wim_writable(WIMStruct *w, const char *path,
-                            bool trunc, bool readable);
 
 /* Internal use only */
 #define WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE      0x80000000
index b143f16..773de0b 100644 (file)
 #include <stdlib.h>
 #endif
 
+#if defined(HAVE_SYS_FILE_H) && defined(HAVE_FLOCK)
+#include <sys/file.h>
+#endif
+
+
 static int do_fflush(FILE *fp)
 {
        int ret = fflush(fp);
@@ -1487,6 +1492,49 @@ out:
        return ret;
 }
 
+static int open_wim_writable(WIMStruct *w, const char *path,
+                            bool trunc, bool readable)
+{
+       const char *mode;
+       int ret = 0;
+       if (trunc)
+               if (readable)
+                       mode = "w+b";
+               else
+                       mode = "wb";
+       else
+               mode = "r+b";
+
+       DEBUG("Opening `%s' read-write", path);
+       wimlib_assert(w->out_fp == NULL);
+       wimlib_assert(path != NULL);
+       w->out_fp = fopen(path, mode);
+       if (!w->out_fp) {
+               ERROR_WITH_ERRNO("Failed to open `%s' for writing", path);
+               return WIMLIB_ERR_OPEN;
+       }
+#if defined(HAVE_SYS_FILE_H) && defined(HAVE_FLOCK)
+       if (!trunc) {
+               ret = flock(fileno(w->out_fp), LOCK_EX | LOCK_NB);
+               if (ret != 0) {
+                       if (errno == EWOULDBLOCK) {
+                               ERROR("`%s' is already being modified "
+                                     "by another process", path);
+                               ret = WIMLIB_ERR_ALREADY_LOCKED;
+                               fclose(w->out_fp);
+                               w->out_fp = NULL;
+                       } else {
+                               WARNING("Failed to lock `%s': %s",
+                                       path, strerror(errno));
+                               ret = 0;
+                       }
+               }
+       }
+#endif
+       return ret;
+}
+
+
 static void close_wim_writable(WIMStruct *w)
 {
        if (w->out_fp) {