]> wimlib.net Git - wimlib/blobdiff - src/add_image.c
Fixes
[wimlib] / src / add_image.c
index fc2f6a200ca314e486942d66378bdd8ae1665833..ab4adc97176e459bc38f27406a4d186b6b92df29 100644 (file)
  * along with wimlib; if not, see http://www.gnu.org/licenses/.
  */
 
+#include "config.h"
+
+#ifdef __WIN32__
+#  include "win32.h"
+#else
+#  include <dirent.h>
+#  include <sys/stat.h>
+#  include <fnmatch.h>
+#  include "timestamp.h"
+#endif
+
 #include "wimlib_internal.h"
 #include "dentry.h"
-#include "timestamp.h"
 #include "lookup_table.h"
 #include "xml.h"
-#include <string.h>
-#include <fnmatch.h>
-#include <stdlib.h>
+
 #include <ctype.h>
-#include <sys/stat.h>
-#include <dirent.h>
 #include <errno.h>
+#include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
-#define WIMLIB_ADD_IMAGE_FLAG_ROOT     0x80000000
-#define WIMLIB_ADD_IMAGE_FLAG_SOURCE    0x40000000
+#ifdef HAVE_ALLOCA_H
+#  include <alloca.h>
+#endif
 
 /*
  * Adds the dentry tree and security data for a new image to the image metadata
  * array of the WIMStruct.
  */
-int add_new_dentry_tree(WIMStruct *w, struct wim_dentry *root_dentry,
-                       struct wim_security_data *sd)
+int
+add_new_dentry_tree(WIMStruct *w, struct wim_dentry *root_dentry,
+                   struct wim_security_data *sd)
 {
        struct wim_lookup_table_entry *metadata_lte;
        struct wim_image_metadata *imd;
@@ -89,7 +99,7 @@ err:
 
 }
 
-
+#ifndef __WIN32__
 /*
  * build_dentry_tree():
  *     Recursively builds a tree of WIM dentries from an on-disk directory
@@ -114,7 +124,8 @@ err:
  *
  * @add_flags:  Bitwise or of WIMLIB_ADD_IMAGE_FLAG_*
  *
- * @extra_arg: Ignored. (Only used in NTFS mode.)
+ * @extra_arg: Ignored in UNIX builds; used to pass sd_set pointer in Windows
+ *             builds.
  *
  * @return:    0 on success, nonzero on failure.  It is a failure if any of
  *             the files cannot be `stat'ed, or if any of the needed
@@ -123,25 +134,25 @@ err:
  *             the on-disk files during a call to wimlib_write() or
  *             wimlib_overwrite().
  */
-static int build_dentry_tree(struct wim_dentry **root_ret,
-                            const char *root_disk_path,
-                            struct wim_lookup_table *lookup_table,
-                            struct wim_security_data *sd,
-                            const struct capture_config *config,
-                            int add_image_flags,
-                            wimlib_progress_func_t progress_func,
-                            void *extra_arg)
+static int
+unix_build_dentry_tree(struct wim_dentry **root_ret,
+                      const mbchar *root_disk_path,
+                      struct wim_lookup_table *lookup_table,
+                      struct wim_security_data *sd,
+                      const struct capture_config *config,
+                      int add_image_flags,
+                      wimlib_progress_func_t progress_func,
+                      void *extra_arg)
 {
-       struct stat root_stbuf;
+       struct wim_dentry *root = NULL;
        int ret = 0;
-       int (*stat_fn)(const char *restrict, struct stat *restrict);
-       struct wim_dentry *root;
        struct wim_inode *inode;
 
        if (exclude_path(root_disk_path, config, true)) {
                if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) {
                        ERROR("Cannot exclude the root directory from capture");
-                       return WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
+                       ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
+                       goto out;
                }
                if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE)
                    && progress_func)
@@ -151,8 +162,7 @@ static int build_dentry_tree(struct wim_dentry **root_ret,
                        info.scan.excluded = true;
                        progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info);
                }
-               *root_ret = NULL;
-               return 0;
+               goto out;
        }
 
        if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE)
@@ -164,6 +174,9 @@ static int build_dentry_tree(struct wim_dentry **root_ret,
                progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info);
        }
 
+       /* UNIX version of capturing a directory tree */
+       struct stat root_stbuf;
+       int (*stat_fn)(const char *restrict, struct stat *restrict);
        if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE)
                stat_fn = stat;
        else
@@ -172,7 +185,7 @@ static int build_dentry_tree(struct wim_dentry **root_ret,
        ret = (*stat_fn)(root_disk_path, &root_stbuf);
        if (ret != 0) {
                ERROR_WITH_ERRNO("Failed to stat `%s'", root_disk_path);
-               return WIMLIB_ERR_STAT;
+               goto out;
        }
 
        if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) &&
@@ -184,46 +197,48 @@ static int build_dentry_tree(struct wim_dentry **root_ret,
                ret = stat(root_disk_path, &root_stbuf);
                if (ret != 0) {
                        ERROR_WITH_ERRNO("Failed to stat `%s'", root_disk_path);
-                       return WIMLIB_ERR_STAT;
+                       ret = WIMLIB_ERR_STAT;
+                       goto out;
                }
                if (!S_ISDIR(root_stbuf.st_mode)) {
                        ERROR("`%s' is not a directory", root_disk_path);
-                       return WIMLIB_ERR_NOTDIR;
+                       ret = WIMLIB_ERR_NOTDIR;
+                       goto out;
                }
        }
        if (!S_ISREG(root_stbuf.st_mode) && !S_ISDIR(root_stbuf.st_mode)
            && !S_ISLNK(root_stbuf.st_mode)) {
                ERROR("`%s' is not a regular file, directory, or symbolic link.",
                      root_disk_path);
-               return WIMLIB_ERR_SPECIAL_FILE;
+               ret = WIMLIB_ERR_SPECIAL_FILE;
+               goto out;
        }
 
-       root = new_dentry_with_timeless_inode(path_basename(root_disk_path));
-       if (!root) {
-               if (errno == EILSEQ)
-                       return WIMLIB_ERR_INVALID_UTF8_STRING;
-               else if (errno == ENOMEM)
-                       return WIMLIB_ERR_NOMEM;
-               else
-                       return WIMLIB_ERR_ICONV_NOT_AVAILABLE;
-       }
+       ret = new_dentry_with_timeless_inode(path_basename(root_disk_path),
+                                            &root);
+       if (ret)
+               goto out;
 
        inode = root->d_inode;
 
 #ifdef HAVE_STAT_NANOSECOND_PRECISION
-       inode->i_creation_time = timespec_to_wim_timestamp(&root_stbuf.st_mtim);
-       inode->i_last_write_time = timespec_to_wim_timestamp(&root_stbuf.st_mtim);
-       inode->i_last_access_time = timespec_to_wim_timestamp(&root_stbuf.st_atim);
+       inode->i_creation_time = timespec_to_wim_timestamp(root_stbuf.st_mtim);
+       inode->i_last_write_time = timespec_to_wim_timestamp(root_stbuf.st_mtim);
+       inode->i_last_access_time = timespec_to_wim_timestamp(root_stbuf.st_atim);
 #else
        inode->i_creation_time = unix_timestamp_to_wim(root_stbuf.st_mtime);
        inode->i_last_write_time = unix_timestamp_to_wim(root_stbuf.st_mtime);
        inode->i_last_access_time = unix_timestamp_to_wim(root_stbuf.st_atime);
 #endif
-       if (sizeof(ino_t) >= 8)
-               inode->i_ino = (u64)root_stbuf.st_ino;
-       else
-               inode->i_ino = (u64)root_stbuf.st_ino |
-                                  ((u64)root_stbuf.st_dev << ((sizeof(ino_t) * 8) & 63));
+       /* Leave the inode number at 0 for directories. */
+       if (!S_ISDIR(root_stbuf.st_mode)) {
+               if (sizeof(ino_t) >= 8)
+                       inode->i_ino = (u64)root_stbuf.st_ino;
+               else
+                       inode->i_ino = (u64)root_stbuf.st_ino |
+                                          ((u64)root_stbuf.st_dev <<
+                                               ((sizeof(ino_t) * 8) & 63));
+       }
        inode->i_resolved = 1;
        if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) {
                ret = inode_set_unix_data(inode, root_stbuf.st_uid,
@@ -261,7 +276,7 @@ static int build_dentry_tree(struct wim_dentry **root_ret,
                        DEBUG("Add lte reference %u for `%s'", lte->refcnt,
                              root_disk_path);
                } else {
-                       char *file_on_disk = STRDUP(root_disk_path);
+                       mbchar *file_on_disk = STRDUP(root_disk_path);
                        if (!file_on_disk) {
                                ERROR("Failed to allocate memory for file path");
                                ret = WIMLIB_ERR_NOMEM;
@@ -299,7 +314,7 @@ static int build_dentry_tree(struct wim_dentry **root_ret,
 
                /* Buffer for names of files in directory. */
                size_t len = strlen(root_disk_path);
-               char name[len + 1 + FILENAME_MAX + 1];
+               mbchar name[len + 1 + FILENAME_MAX + 1];
                memcpy(name, root_disk_path, len);
                name[len] = '/';
 
@@ -321,9 +336,11 @@ static int build_dentry_tree(struct wim_dentry **root_ret,
                              || (result->d_name[1] == '.' && result->d_name[2] == '\0')))
                                        continue;
                        strcpy(name + len + 1, result->d_name);
-                       ret = build_dentry_tree(&child, name, lookup_table,
-                                               NULL, config, add_image_flags,
-                                               progress_func, NULL);
+                       ret = unix_build_dentry_tree(&child, name,
+                                                    lookup_table,
+                                                    NULL, config,
+                                                    add_image_flags,
+                                                    progress_func, NULL);
                        if (ret != 0)
                                break;
                        if (child)
@@ -341,7 +358,7 @@ static int build_dentry_tree(struct wim_dentry **root_ret,
                 * drive letter).
                 */
 
-               char deref_name_buf[4096];
+               mbchar deref_name_buf[4096];
                ssize_t deref_name_len;
 
                deref_name_len = readlink(root_disk_path, deref_name_buf,
@@ -380,7 +397,7 @@ out:
                free_dentry_tree(root, lookup_table);
        return ret;
 }
-
+#endif /* !__WIN32__ */
 
 enum pattern_type {
        NONE = 0,
@@ -393,7 +410,7 @@ enum pattern_type {
 #define COMPAT_DEFAULT_CONFIG
 
 /* Default capture configuration file when none is specified. */
-static const char *default_config =
+static const mbchar *default_config =
 #ifdef COMPAT_DEFAULT_CONFIG /* XXX: This policy is being moved to library
                                users.  The next ABI-incompatible library
                                version will default to the empty string here. */
@@ -414,12 +431,14 @@ static const char *default_config =
 "";
 #endif
 
-static void destroy_pattern_list(struct pattern_list *list)
+static void
+destroy_pattern_list(struct pattern_list *list)
 {
        FREE(list->pats);
 }
 
-static void destroy_capture_config(struct capture_config *config)
+static void
+destroy_capture_config(struct capture_config *config)
 {
        destroy_pattern_list(&config->exclusion_list);
        destroy_pattern_list(&config->exclusion_exception);
@@ -430,8 +449,8 @@ static void destroy_capture_config(struct capture_config *config)
        memset(config, 0, sizeof(*config));
 }
 
-static int pattern_list_add_pattern(struct pattern_list *list,
-                                   const char *pattern)
+static int
+pattern_list_add_pattern(struct pattern_list *list, const mbchar *pattern)
 {
        const char **pats;
        if (list->num_pats >= list->num_allocated_pats) {
@@ -448,13 +467,14 @@ static int pattern_list_add_pattern(struct pattern_list *list,
 
 /* Parses the contents of the image capture configuration file and fills in a
  * `struct capture_config'. */
-static int init_capture_config(struct capture_config *config,
-                              const char *_config_str, size_t config_len)
+static int
+init_capture_config(struct capture_config *config,
+                   const mbchar *_config_str, size_t config_len)
 {
-       char *config_str;
-       char *p;
-       char *eol;
-       char *next_p;
+       mbchar *config_str;
+       mbchar *p;
+       mbchar *eol;
+       mbchar *next_p;
        size_t bytes_remaining;
        enum pattern_type type = NONE;
        int ret;
@@ -493,7 +513,7 @@ static int init_capture_config(struct capture_config *config,
                *eol = '\0';
 
                /* Translate backslash to forward slash */
-               for (char *pp = p; pp != eol; pp++)
+               for (mbchar *pp = p; pp != eol; pp++)
                        if (*pp == '\\')
                                *pp = '/';
 
@@ -547,9 +567,9 @@ out_destroy:
 }
 
 static int capture_config_set_prefix(struct capture_config *config,
-                                    const char *_prefix)
+                                    const mbchar *_prefix)
 {
-       char *prefix = STRDUP(_prefix);
+       mbchar *prefix = STRDUP(_prefix);
 
        if (!prefix)
                return WIMLIB_ERR_NOMEM;
@@ -559,7 +579,8 @@ static int capture_config_set_prefix(struct capture_config *config,
        return 0;
 }
 
-static bool match_pattern(const char *path, const char *path_basename,
+static bool match_pattern(const mbchar *path,
+                         const mbchar *path_basename,
                          const struct pattern_list *list)
 {
        for (size_t i = 0; i < list->num_pats; i++) {
@@ -576,11 +597,14 @@ static bool match_pattern(const char *path, const char *path_basename,
                                /* A file name pattern */
                                string = path_basename;
                }
+
+               /* Warning: on Windows native builds, fnmatch() calls the
+                * replacement function in win32.c. */
                if (fnmatch(pat, string, FNM_PATHNAME
-                       #ifdef FNM_CASEFOLD
-                                       | FNM_CASEFOLD
-                       #endif
-                       ) == 0)
+                               #ifdef FNM_CASEFOLD
+                                       | FNM_CASEFOLD
+                               #endif
+                           ) == 0)
                {
                        DEBUG("`%s' matches the pattern \"%s\"",
                              string, pat);
@@ -599,10 +623,11 @@ static bool match_pattern(const char *path, const char *path_basename,
  * file /mnt/windows7/hiberfil.sys if we are capturing the /mnt/windows7
  * directory.
  */
-bool exclude_path(const char *path, const struct capture_config *config,
-                 bool exclude_prefix)
+bool
+exclude_path(const mbchar *path, const struct capture_config *config,
+            bool exclude_prefix)
 {
-       const char *basename = path_basename(path);
+       const mbchar *basename = path_basename(path);
        if (exclude_prefix) {
                wimlib_assert(strlen(path) >= config->prefix_len);
                if (memcmp(config->prefix, path, config->prefix_len) == 0
@@ -616,11 +641,12 @@ bool exclude_path(const char *path, const struct capture_config *config,
 
 /* Strip leading and trailing forward slashes from a string.  Modifies it in
  * place and returns the stripped string. */
-static const char *canonicalize_target_path(char *target_path)
+static const char *
+canonicalize_target_path(char *target_path)
 {
        char *p;
        if (target_path == NULL)
-               target_path = "";
+               return "";
        for (;;) {
                if (*target_path == '\0')
                        return target_path;
@@ -636,14 +662,34 @@ static const char *canonicalize_target_path(char *target_path)
        return target_path;
 }
 
+#ifdef __WIN32__
+static void
+zap_backslashes(char *s)
+{
+       while (*s) {
+               if (*s == '\\')
+                       *s = '/';
+               s++;
+       }
+}
+#endif
+
 /* Strip leading and trailing slashes from the target paths */
-static void canonicalize_targets(struct wimlib_capture_source *sources,
-                                size_t num_sources)
+static void
+canonicalize_targets(struct wimlib_capture_source *sources, size_t num_sources)
 {
        while (num_sources--) {
                DEBUG("Canonicalizing { source: \"%s\", target=\"%s\"}",
                      sources->fs_source_path,
                      sources->wim_target_path);
+#ifdef __WIN32__
+               /* The Windows API can handle forward slashes.  Just get rid of
+                * backslashes to avoid confusing other parts of the library
+                * code. */
+               zap_backslashes(sources->fs_source_path);
+               if (sources->wim_target_path)
+                       zap_backslashes(sources->wim_target_path);
+#endif
                sources->wim_target_path =
                        (char*)canonicalize_target_path(sources->wim_target_path);
                DEBUG("Canonical target: \"%s\"", sources->wim_target_path);
@@ -651,7 +697,8 @@ static void canonicalize_targets(struct wimlib_capture_source *sources,
        }
 }
 
-static int capture_source_cmp(const void *p1, const void *p2)
+static int
+capture_source_cmp(const void *p1, const void *p2)
 {
        const struct wimlib_capture_source *s1 = p1, *s2 = p2;
        return strcmp(s1->wim_target_path, s2->wim_target_path);
@@ -661,15 +708,16 @@ static int capture_source_cmp(const void *p1, const void *p2)
  * after leading and trailing forward slashes are stripped.
  *
  * One purpose of this is to make sure that target paths that are inside other
- * target paths are extracted after the containing target paths. */
-static void sort_sources(struct wimlib_capture_source *sources,
-                        size_t num_sources)
+ * target paths are added after the containing target paths. */
+static void
+sort_sources(struct wimlib_capture_source *sources, size_t num_sources)
 {
        qsort(sources, num_sources, sizeof(sources[0]), capture_source_cmp);
 }
 
-static int check_sorted_sources(struct wimlib_capture_source *sources,
-                               size_t num_sources, int add_image_flags)
+static int
+check_sorted_sources(struct wimlib_capture_source *sources, size_t num_sources,
+                    int add_image_flags)
 {
        if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_NTFS) {
                if (num_sources != 1) {
@@ -723,32 +771,37 @@ static int check_sorted_sources(struct wimlib_capture_source *sources,
 
 /* Creates a new directory to place in the WIM image.  This is to create parent
  * directories that are not part of any target as needed.  */
-static struct wim_dentry *
-new_filler_directory(const char *name)
+static int
+new_filler_directory(const mbchar *name, struct wim_dentry **dentry_ret)
 {
+       int ret;
        struct wim_dentry *dentry;
+
        DEBUG("Creating filler directory \"%s\"", name);
-       dentry = new_dentry_with_inode(name);
-       if (dentry) {
-               /* Set the inode number to 0 for now.  The final inode number
+       ret = new_dentry_with_inode(name, &dentry);
+       if (ret == 0) {
+               /* Leave the inode number as 0 for now.  The final inode number
                 * will be assigned later by assign_inode_numbers(). */
-               dentry->d_inode->i_ino = 0;
                dentry->d_inode->i_resolved = 1;
                dentry->d_inode->i_attributes = FILE_ATTRIBUTE_DIRECTORY;
        }
-       return dentry;
+       return ret;
 }
 
 /* Transfers the children of @branch to @target.  It is an error if @target is
  * not a directory or if both @branch and @target contain a child dentry with
  * the same name. */
-static int do_overlay(struct wim_dentry *target, struct wim_dentry *branch)
+static int
+do_overlay(struct wim_dentry *target, struct wim_dentry *branch)
 {
        struct rb_root *rb_root;
 
+       DEBUG("Doing overlay \"%W\" => \"%W\"",
+             branch->file_name, target->file_name);
+
        if (!dentry_is_directory(target)) {
-               ERROR("Cannot overlay directory `%s' over non-directory",
-                     branch->file_name_utf8);
+               ERROR("Cannot overlay directory \"%W\" over non-directory",
+                     branch->file_name);
                return WIMLIB_ERR_INVALID_OVERLAY;
        }
 
@@ -761,12 +814,13 @@ static int do_overlay(struct wim_dentry *target, struct wim_dentry *branch)
                        /* Revert the change to avoid leaking the directory tree
                         * rooted at @child */
                        dentry_add_child(branch, child);
-                       ERROR("Overlay error: file `%s' already exists "
-                             "as a child of `%s'",
-                             child->file_name_utf8, target->file_name_utf8);
+                       ERROR("Overlay error: file \"%W\" already exists "
+                             "as a child of \"%W\"",
+                             child->file_name, target->file_name);
                        return WIMLIB_ERR_INVALID_OVERLAY;
                }
        }
+       free_dentry(branch);
        return 0;
 
 }
@@ -782,12 +836,16 @@ static int do_overlay(struct wim_dentry *target, struct wim_dentry *branch)
  *     Path in the WIM image to add the branch, with leading and trailing
  *     slashes stripped.
  */
-static int attach_branch(struct wim_dentry **root_p,
-                        struct wim_dentry *branch,
-                        char *target_path)
+static int
+attach_branch(struct wim_dentry **root_p, struct wim_dentry *branch,
+             mbchar *target_path)
 {
        char *slash;
        struct wim_dentry *dentry, *parent, *target;
+       int ret;
+
+       DEBUG("Attaching branch \"%W\" => \"%s\"",
+             branch->file_name, target_path);
 
        if (*target_path == '\0') {
                /* Target: root directory */
@@ -804,9 +862,9 @@ static int attach_branch(struct wim_dentry **root_p,
        /* Adding a non-root branch.  Create root if it hasn't been created
         * already. */
        if (!*root_p) {
-               *root_p = new_filler_directory("");
-               if (!*root_p)
-                       return WIMLIB_ERR_NOMEM;
+               ret  = new_filler_directory("", root_p);
+               if (ret)
+                       return ret;
        }
 
        /* Walk the path to the branch, creating filler directories as needed.
@@ -816,9 +874,9 @@ static int attach_branch(struct wim_dentry **root_p,
                *slash = '\0';
                dentry = get_dentry_child_with_name(parent, target_path);
                if (!dentry) {
-                       dentry = new_filler_directory(target_path);
-                       if (!dentry)
-                               return WIMLIB_ERR_NOMEM;
+                       ret = new_filler_directory(target_path, &dentry);
+                       if (ret)
+                               return ret;
                        dentry_add_child(parent, dentry);
                }
                parent = dentry;
@@ -833,7 +891,8 @@ static int attach_branch(struct wim_dentry **root_p,
 
        /* If the target path already existed, overlay the branch onto it.
         * Otherwise, set the branch as the target path. */
-       target = get_dentry_child_with_name(parent, branch->file_name_utf8);
+       target = get_dentry_child_with_utf16le_name(parent, branch->file_name,
+                                                   branch->file_name_nbytes);
        if (target) {
                return do_overlay(target, branch);
        } else {
@@ -842,20 +901,24 @@ static int attach_branch(struct wim_dentry **root_p,
        }
 }
 
-WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w,
-                                          struct wimlib_capture_source *sources,
-                                          size_t num_sources,
-                                          const char *name,
-                                          const char *config_str,
-                                          size_t config_len,
-                                          int add_image_flags,
-                                          wimlib_progress_func_t progress_func)
+WIMLIBAPI int
+wimlib_add_image_multisource(WIMStruct *w,
+                            struct wimlib_capture_source *sources,
+                            size_t num_sources,
+                            const utf8char *name,
+                            const mbchar *config_str,
+                            size_t config_len,
+                            int add_image_flags,
+                            wimlib_progress_func_t progress_func)
 {
-       int (*capture_tree)(struct wim_dentry **, const char *,
+       int (*capture_tree)(struct wim_dentry **,
+                           const mbchar *,
                            struct wim_lookup_table *,
                            struct wim_security_data *,
                            const struct capture_config *,
-                           int, wimlib_progress_func_t, void *);
+                           int,
+                           wimlib_progress_func_t,
+                           void *);
        void *extra_arg;
        struct wim_dentry *root_dentry;
        struct wim_dentry *branch;
@@ -883,10 +946,25 @@ WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w,
                return WIMLIB_ERR_UNSUPPORTED;
 #endif
        } else {
-               capture_tree = build_dentry_tree;
+       #ifdef __WIN32__
+               capture_tree = win32_build_dentry_tree;
+       #else
+               capture_tree = unix_build_dentry_tree;
+       #endif
                extra_arg = NULL;
        }
 
+#ifdef __WIN32__
+       if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) {
+               ERROR("Capturing UNIX-specific data is not supported on Windows");
+               return WIMLIB_ERR_INVALID_PARAM;
+       }
+       if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE) {
+               ERROR("Dereferencing symbolic links is not supported on Windows");
+               return WIMLIB_ERR_INVALID_PARAM;
+       }
+#endif
+
        if (!name || !*name) {
                ERROR("Must specify a non-empty string for the image name");
                return WIMLIB_ERR_INVALID_PARAM;
@@ -898,8 +976,8 @@ WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w,
        }
 
        if (wimlib_image_name_in_use(w, name)) {
-               ERROR("There is already an image named \"%s\" in `%s'",
-                     name, w->filename);
+               ERROR("There is already an image named \"%U\" in the WIM!",
+                     name);
                return WIMLIB_ERR_IMAGE_NAME_COLLISION;
        }
 
@@ -932,12 +1010,16 @@ WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w,
 
        DEBUG("Building dentry tree.");
        if (num_sources == 0) {
-               root_dentry = new_filler_directory("");
-               if (!root_dentry)
+               ret = new_filler_directory("", &root_dentry);
+               if (ret)
                        goto out_free_security_data;
        } else {
                size_t i;
 
+       #ifdef __WIN32__
+               win32_acquire_capture_privileges();
+       #endif
+
                root_dentry = NULL;
                i = 0;
                do {
@@ -993,30 +1075,30 @@ WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w,
 
        DEBUG("Calculating full paths of dentries.");
        ret = for_dentry_in_tree(root_dentry, calculate_dentry_full_path, NULL);
-       if (ret != 0)
+       if (ret)
                goto out_free_dentry_tree;
 
        ret = add_new_dentry_tree(w, root_dentry, sd);
-       if (ret != 0)
+       if (ret)
                goto out_free_dentry_tree;
 
        imd = &w->image_metadata[w->hdr.image_count - 1];
 
        ret = dentry_tree_fix_inodes(root_dentry, &imd->inode_list);
-       if (ret != 0)
+       if (ret)
                goto out_destroy_imd;
 
        DEBUG("Assigning hard link group IDs");
        assign_inode_numbers(&imd->inode_list);
 
        ret = xml_add_image(w, name);
-       if (ret != 0)
+       if (ret)
                goto out_destroy_imd;
 
        if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_BOOT)
                wimlib_set_boot_idx(w, w->hdr.image_count);
        ret = 0;
-       goto out;
+       goto out_destroy_capture_config;
 out_destroy_imd:
        destroy_image_metadata(&w->image_metadata[w->hdr.image_count - 1],
                               w->lookup_table);
@@ -1031,13 +1113,20 @@ out_free_security_data:
 out_destroy_capture_config:
        destroy_capture_config(&config);
 out:
+#ifdef __WIN32__
+       win32_release_capture_privileges();
+#endif
        return ret;
 }
 
-WIMLIBAPI int wimlib_add_image(WIMStruct *w, const char *source,
-                              const char *name, const char *config_str,
-                              size_t config_len, int add_image_flags,
-                              wimlib_progress_func_t progress_func)
+WIMLIBAPI int
+wimlib_add_image(WIMStruct *w,
+                const mbchar *source,
+                const utf8char *name,
+                const mbchar *config_str,
+                size_t config_len,
+                int add_image_flags,
+                wimlib_progress_func_t progress_func)
 {
        if (!source || !*source)
                return WIMLIB_ERR_INVALID_PARAM;