]> wimlib.net Git - wimlib/blobdiff - src/write.c
Tweak XML element order and time format
[wimlib] / src / write.c
index 773de0b7b6553e61ead5ef8b726e00f218b54f6a..653c212d30a2122609fa8e7b99f3f591a810d6ca 100644 (file)
  * along with wimlib; if not, see http://www.gnu.org/licenses/.
  */
 
+#include "config.h"
+
+#if defined(HAVE_SYS_FILE_H) && defined(HAVE_FLOCK)
+/* On BSD, this should be included before "list.h" so that "list.h" can
+ * overwrite the LIST_HEAD macro. */
+#include <sys/file.h>
+#endif
+
+#include "list.h"
 #include "wimlib_internal.h"
 #include "io.h"
 #include "dentry.h"
 #include "xml.h"
 #include "lzx.h"
 #include "xpress.h"
-#include <unistd.h>
 
 #ifdef ENABLE_MULTITHREADED_COMPRESSION
-#include <semaphore.h>
 #include <pthread.h>
 #endif
 
+#include <unistd.h>
 #include <errno.h>
 
 #ifdef WITH_NTFS_3G
 #include <ntfs-3g/dir.h>
 #endif
 
-
 #ifdef HAVE_ALLOCA_H
 #include <alloca.h>
 #else
 #include <stdlib.h>
 #endif
 
-#if defined(HAVE_SYS_FILE_H) && defined(HAVE_FLOCK)
-#include <sys/file.h>
-#endif
-
-
-static int do_fflush(FILE *fp)
+static int fflush_and_ftruncate(FILE *fp, off_t size)
 {
-       int ret = fflush(fp);
+       int ret;
+
+       ret = fflush(fp);
        if (ret != 0) {
                ERROR_WITH_ERRNO("Failed to flush data to output WIM file");
                return WIMLIB_ERR_WRITE;
        }
-       return 0;
-}
-
-static int fflush_and_ftruncate(FILE *fp, off_t size)
-{
-       int ret;
-
-       ret = do_fflush(fp);
-       if (ret != 0)
-               return ret;
        ret = ftruncate(fileno(fp), size);
        if (ret != 0) {
                ERROR_WITH_ERRNO("Failed to truncate output WIM file to "
@@ -543,13 +537,14 @@ out:
 
 #ifdef ENABLE_MULTITHREADED_COMPRESSION
 struct shared_queue {
-       sem_t filled_slots;
-       sem_t empty_slots;
-       pthread_mutex_t lock;
+       unsigned size;
        unsigned front;
        unsigned back;
+       unsigned filled_slots;
        void **array;
-       unsigned size;
+       pthread_mutex_t lock;
+       pthread_cond_t msg_avail_cond;
+       pthread_cond_t space_avail_cond;
 };
 
 static int shared_queue_init(struct shared_queue *q, unsigned size)
@@ -557,46 +552,52 @@ static int shared_queue_init(struct shared_queue *q, unsigned size)
        q->array = CALLOC(sizeof(q->array[0]), size);
        if (!q->array)
                return WIMLIB_ERR_NOMEM;
-
-       sem_init(&q->filled_slots, 0, 0);
-       sem_init(&q->empty_slots, 0, size);
-       pthread_mutex_init(&q->lock, NULL);
+       q->filled_slots = 0;
        q->front = 0;
        q->back = size - 1;
        q->size = size;
+       pthread_mutex_init(&q->lock, NULL);
+       pthread_cond_init(&q->msg_avail_cond, NULL);
+       pthread_cond_init(&q->space_avail_cond, NULL);
        return 0;
 }
 
 static void shared_queue_destroy(struct shared_queue *q)
 {
-       sem_destroy(&q->filled_slots);
-       sem_destroy(&q->empty_slots);
-       pthread_mutex_destroy(&q->lock);
        FREE(q->array);
+       pthread_mutex_destroy(&q->lock);
+       pthread_cond_destroy(&q->msg_avail_cond);
+       pthread_cond_destroy(&q->space_avail_cond);
 }
 
 static void shared_queue_put(struct shared_queue *q, void *obj)
 {
-       sem_wait(&q->empty_slots);
        pthread_mutex_lock(&q->lock);
+       while (q->filled_slots == q->size)
+               pthread_cond_wait(&q->space_avail_cond, &q->lock);
 
        q->back = (q->back + 1) % q->size;
        q->array[q->back] = obj;
+       q->filled_slots++;
 
-       sem_post(&q->filled_slots);
+       pthread_cond_broadcast(&q->msg_avail_cond);
        pthread_mutex_unlock(&q->lock);
 }
 
 static void *shared_queue_get(struct shared_queue *q)
 {
-       sem_wait(&q->filled_slots);
+       void *obj;
+
        pthread_mutex_lock(&q->lock);
+       while (q->filled_slots == 0)
+               pthread_cond_wait(&q->msg_avail_cond, &q->lock);
 
-       void *obj = q->array[q->front];
+       obj = q->array[q->front];
        q->array[q->front] = NULL;
        q->front = (q->front + 1) % q->size;
+       q->filled_slots--;
 
-       sem_post(&q->empty_slots);
+       pthread_cond_broadcast(&q->space_avail_cond);
        pthread_mutex_unlock(&q->lock);
        return obj;
 }
@@ -823,7 +824,12 @@ static int main_writer_thread_proc(struct list_head *stream_list,
        for (size_t i = 0; i < ARRAY_LEN(msgs); i++) {
                for (size_t j = 0; j < MAX_CHUNKS_PER_MSG; j++) {
                        msgs[i].compressed_chunks[j] = MALLOC(WIM_CHUNK_SIZE);
-                       msgs[i].uncompressed_chunks[j] = MALLOC(WIM_CHUNK_SIZE);
+
+                       // The extra 8 bytes is because longest_match() in lz.c
+                       // may read a little bit off the end of the uncompressed
+                       // data.  It doesn't need to be initialized--- we really
+                       // just need to avoid accessing an unmapped page.
+                       msgs[i].uncompressed_chunks[j] = MALLOC(WIM_CHUNK_SIZE + 8);
                        if (msgs[i].compressed_chunks[j] == NULL ||
                            msgs[i].uncompressed_chunks[j] == NULL)
                        {
@@ -1492,11 +1498,33 @@ out:
        return ret;
 }
 
+#if defined(HAVE_SYS_FILE_H) && defined(HAVE_FLOCK)
+int lock_wim(FILE *fp, const char *path)
+{
+       int ret = 0;
+       if (fp) {
+               ret = flock(fileno(fp), LOCK_EX | LOCK_NB);
+               if (ret != 0) {
+                       if (errno == EWOULDBLOCK) {
+                               ERROR("`%s' is already being modified or has been "
+                                     "mounted read-write\n"
+                                     "        by another process!", path);
+                               ret = WIMLIB_ERR_ALREADY_LOCKED;
+                       } else {
+                               WARNING("Failed to lock `%s': %s",
+                                       path, strerror(errno));
+                               ret = 0;
+                       }
+               }
+       }
+       return ret;
+}
+#endif
+
 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";
@@ -1505,37 +1533,18 @@ static int open_wim_writable(WIMStruct *w, const char *path,
        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) {
+       if (w->out_fp) {
+               return 0;
+       } else {
                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)
+void close_wim_writable(WIMStruct *w)
 {
        if (w->out_fp) {
                if (fclose(w->out_fp) != 0) {
@@ -1550,12 +1559,8 @@ static void close_wim_writable(WIMStruct *w)
 int begin_write(WIMStruct *w, const char *path, int write_flags)
 {
        int ret;
-       bool need_readable = false;
-       bool trunc = true;
-       if (write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY)
-               need_readable = true;
-
-       ret = open_wim_writable(w, path, trunc, need_readable);
+       ret = open_wim_writable(w, path, true,
+                               (write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) != 0);
        if (ret != 0)
                return ret;
        /* Write dummy header. It will be overwritten later. */
@@ -1729,10 +1734,6 @@ static int overwrite_wim_inplace(WIMStruct *w, int write_flags,
        INIT_LIST_HEAD(&stream_list);
        for (int i = modified_image_idx; i < w->hdr.image_count; i++) {
                DEBUG("Identifiying streams in image %d", i + 1);
-               wimlib_assert(w->image_metadata[i].modified);
-               wimlib_assert(!w->image_metadata[i].has_been_mounted_rw);
-               wimlib_assert(w->image_metadata[i].root_dentry != NULL);
-               wimlib_assert(w->image_metadata[i].metadata_lte != NULL);
                w->private = &stream_list;
                for_dentry_in_tree(w->image_metadata[i].root_dentry,
                                   dentry_find_streams_to_write, w);
@@ -1751,7 +1752,6 @@ static int overwrite_wim_inplace(WIMStruct *w, int write_flags,
        if (modified_image_idx == w->hdr.image_count && !w->deletion_occurred) {
                /* If no images have been modified and no images have been
                 * deleted, a new lookup table does not need to be written. */
-               wimlib_assert(list_empty(&stream_list));
                old_wim_end = w->hdr.lookup_table_res_entry.offset +
                              w->hdr.lookup_table_res_entry.size;
                write_flags |= WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE |
@@ -1767,9 +1767,17 @@ static int overwrite_wim_inplace(WIMStruct *w, int write_flags,
        if (ret != 0)
                return ret;
 
+       ret = lock_wim(w->out_fp, w->filename);
+       if (ret != 0) {
+               fclose(w->out_fp);
+               w->out_fp = NULL;
+               return ret;
+       }
+
        if (fseeko(w->out_fp, old_wim_end, SEEK_SET) != 0) {
                ERROR_WITH_ERRNO("Can't seek to end of WIM");
-               return WIMLIB_ERR_WRITE;
+               ret = WIMLIB_ERR_WRITE;
+               goto out_ftruncate;
        }
 
        if (!list_empty(&stream_list)) {
@@ -1829,12 +1837,6 @@ static int overwrite_wim_via_tmpfile(WIMStruct *w, int write_flags,
                goto err;
        }
 
-       /* Close the original WIM file that was opened for reading. */
-       if (w->fp != NULL) {
-               fclose(w->fp);
-               w->fp = NULL;
-       }
-
        DEBUG("Renaming `%s' to `%s'", tmpfile, w->filename);
 
        /* Rename the new file to the old file .*/
@@ -1852,12 +1854,20 @@ static int overwrite_wim_via_tmpfile(WIMStruct *w, int write_flags,
                progress_func(WIMLIB_PROGRESS_MSG_RENAME, &progress);
        }
 
+       /* Close the original WIM file that was opened for reading. */
+       if (w->fp != NULL) {
+               fclose(w->fp);
+               w->fp = NULL;
+       }
+
        /* Re-open the WIM read-only. */
        w->fp = fopen(w->filename, "rb");
        if (w->fp == NULL) {
                ret = WIMLIB_ERR_REOPEN;
                WARNING("Failed to re-open `%s' read-only: %s",
                        w->filename, strerror(errno));
+               FREE(w->filename);
+               w->filename = NULL;
        }
        return ret;
 err: