]> wimlib.net Git - wimlib/blobdiff - src/add_image.c
Windows native build
[wimlib] / src / add_image.c
index 9d88916e638357517c1abf4cbcecadc0c267eed6..a57541f99711d94b474df35e69400269cbaf45e2 100644 (file)
 #      include <windows.h>
 #      include <ntdef.h>
 #      include <wchar.h>
-#      include <sys/cygwin.h>
-#      include <fcntl.h>
 #      ifdef ERROR
 #              undef ERROR
 #      endif
 #      include "security.h"
+#else
+#      include <dirent.h>
+#      include <sys/stat.h>
+#      include "timestamp.h"
+#endif
+
+#ifdef __WIN32__
+#include <shlwapi.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>
+
+#ifndef __WIN32__
+#include <fnmatch.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
 #ifdef HAVE_ALLOCA_H
 #include <alloca.h>
-#else
-#include <stdlib.h>
 #endif
 
 #define WIMLIB_ADD_IMAGE_FLAG_ROOT     0x80000000
@@ -146,8 +151,7 @@ static int win32_get_short_name(struct wim_dentry *dentry,
 
 static int win32_get_security_descriptor(struct wim_dentry *dentry,
                                         struct sd_set *sd_set,
-                                        const wchar_t *path_utf16,
-                                        const char *path)
+                                        const wchar_t *path_utf16)
 {
        SECURITY_INFORMATION requestedInformation;
        DWORD lenNeeded = 0;
@@ -162,14 +166,11 @@ static int win32_get_security_descriptor(struct wim_dentry *dentry,
        status = GetFileSecurityW(path_utf16, requestedInformation,
                                  NULL, 0, &lenNeeded);
        err = GetLastError();
-
-       /* Error code appears to be ERROR_INSUFFICIENT_BUFFER but
-        * GetFileSecurity is poorly documented... */
-       if (err == ERROR_INSUFFICIENT_BUFFER || err == NO_ERROR) {
+       if (!status && err == ERROR_INSUFFICIENT_BUFFER) {
                DWORD len = lenNeeded;
                char buf[len];
                if (GetFileSecurityW(path_utf16, requestedInformation,
-                                    buf, len, &lenNeeded))
+                                    (PSECURITY_DESCRIPTOR)buf, len, &lenNeeded))
                {
                        int security_id = sd_set_add_sd(sd_set, buf, len);
                        if (security_id < 0)
@@ -182,13 +183,14 @@ static int win32_get_security_descriptor(struct wim_dentry *dentry,
                        err = GetLastError();
                }
        }
-       ERROR("Win32 API: Failed to read security descriptor of \"%s\"",
-             path);
+       ERROR("Win32 API: Failed to read security descriptor of \"%ls\"",
+             path_utf16);
        win32_error(err);
        return WIMLIB_ERR_READ;
 }
 
-
+/* Reads the directory entries of directory using a Win32 API and recursively
+ * calls build_dentry_tree() on them. */
 static int win32_recurse_directory(struct wim_dentry *root,
                                   const char *root_disk_path,
                                   struct wim_lookup_table *lookup_table,
@@ -206,6 +208,10 @@ static int win32_recurse_directory(struct wim_dentry *root,
        int ret;
 
        {
+               /* Begin reading the directory by calling FindFirstFileW.
+                * Unlike UNIX opendir(), FindFirstFileW has file globbing built
+                * into it.  But this isn't what we actually want, so just add a
+                * dummy glob to get all entries. */
                wchar_t pattern_buf[path_utf16_nchars + 3];
                memcpy(pattern_buf, path_utf16,
                       path_utf16_nchars * sizeof(wchar_t));
@@ -227,6 +233,7 @@ static int win32_recurse_directory(struct wim_dentry *root,
        }
        ret = 0;
        do {
+               /* Skip . and .. entries */
                if (!(dat.cFileName[0] == L'.' &&
                      (dat.cFileName[1] == L'\0' ||
                       (dat.cFileName[1] == L'.' && dat.cFileName[2] == L'\0'))))
@@ -242,7 +249,7 @@ static int win32_recurse_directory(struct wim_dentry *root,
                        if (ret)
                                goto out_find_close;
 
-                       char name[strlen(root_disk_path) + utf8_name_nbytes + 1];
+                       char name[strlen(root_disk_path) + 1 + utf8_name_nbytes + 1];
                        sprintf(name, "%s/%s", root_disk_path, utf8_name);
                        FREE(utf8_name);
                        ret = build_dentry_tree(&child, name, lookup_table,
@@ -266,30 +273,61 @@ out_find_close:
        return ret;
 }
 
-static int win32_capture_reparse_point(const char *path,
-                                      HANDLE hFile,
+/* Load a reparse point into a WIM inode.  It is just stored in memory.
+ *
+ * @hFile:  Open handle to a reparse point, with permission to read the reparse
+ *          data.
+ *
+ * @inode:  WIM inode for the reparse point.
+ *
+ * @lookup_table:  Stream lookup table for the WIM; an entry will be added to it
+ *                 for the reparse point unless an entry already exists for
+ *                 the exact same data stream.
+ *
+ * @path:  External path to the parse point (UTF-8).  Used for error messages
+ *         only.
+ *
+ * Returns 0 on success; nonzero on failure. */
+static int win32_capture_reparse_point(HANDLE hFile,
                                       struct wim_inode *inode,
-                                      struct wim_lookup_table *lookup_table)
+                                      struct wim_lookup_table *lookup_table,
+                                      const char *path)
 {
        /* "Reparse point data, including the tag and optional GUID,
         * cannot exceed 16 kilobytes." - MSDN  */
        char reparse_point_buf[16 * 1024];
        DWORD bytesReturned;
-       const REPARSE_DATA_BUFFER *buf;
 
        if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT,
                             NULL, 0, reparse_point_buf,
                             sizeof(reparse_point_buf), &bytesReturned, NULL))
        {
+               DWORD err = GetLastError();
                ERROR("Win32 API: Failed to get reparse data of \"%s\"", path);
+               win32_error(err);
                return WIMLIB_ERR_READ;
        }
-       buf = (const REPARSE_DATA_BUFFER*)reparse_point_buf;
-       inode->i_reparse_tag = buf->ReparseTag;
-       return inode_add_ads_with_data(inode, "", (const u8*)buf + 8,
+       if (bytesReturned < 8) {
+               ERROR("Reparse data on \"%s\" is invalid", path);
+               return WIMLIB_ERR_READ;
+       }
+       inode->i_reparse_tag = *(u32*)reparse_point_buf;
+       return inode_add_ads_with_data(inode, "",
+                                      (const u8*)reparse_point_buf + 8,
                                       bytesReturned - 8, lookup_table);
 }
 
+/* Calculate the SHA1 message digest of a Win32 data stream, which may be either
+ * an unnamed or named data stream.
+ *
+ * @path:      Path to the file, with the stream noted at the end for named
+ *              streams.  UTF-16LE encoding.
+ *
+ * @hash:       On success, the SHA1 message digest of the stream is written to
+ *              this location.
+ *
+ * Returns 0 on success; nonzero on failure.
+ */
 static int win32_sha1sum(const wchar_t *path, u8 hash[SHA1_HASH_SIZE])
 {
        HANDLE hFile;
@@ -298,7 +336,7 @@ static int win32_sha1sum(const wchar_t *path, u8 hash[SHA1_HASH_SIZE])
        DWORD bytesRead;
        int ret;
 
-       hFile = win32_open_file(path);
+       hFile = win32_open_file_readonly(path);
        if (hFile == INVALID_HANDLE_VALUE)
                return WIMLIB_ERR_OPEN;
 
@@ -319,8 +357,25 @@ out_close_handle:
        return ret;
 }
 
-static int win32_capture_stream(const char *path,
-                               const wchar_t *path_utf16,
+/* Scans an unnamed or named stream of a Win32 file (not a reparse point
+ * stream); calculates its SHA1 message digest and either creates a `struct
+ * wim_lookup_table_entry' in memory for it, or uses an existing 'struct
+ * wim_lookup_table_entry' for an identical stream.
+ *
+ * @path_utf16:         Path to the file (UTF-16LE).
+ *
+ * @path_utf16_nchars:  Number of 2-byte characters in @path_utf16.
+ *
+ * @inode:              WIM inode to save the stream into.
+ *
+ * @lookup_table:       Stream lookup table for the WIM.
+ *
+ * @dat:                A `WIN32_FIND_STREAM_DATA' structure that specifies the
+ *                      stream name.
+ *
+ * Returns 0 on success; nonzero on failure.
+ */
+static int win32_capture_stream(const wchar_t *path_utf16,
                                size_t path_utf16_nchars,
                                struct wim_inode *inode,
                                struct wim_lookup_table *lookup_table,
@@ -336,11 +391,14 @@ static int win32_capture_stream(const char *path,
        size_t spath_nchars;
        DWORD err;
 
+       /* The stream name should be returned as :NAME:TYPE */
        p = dat->cStreamName;
-       wimlib_assert(*p == L':');
+       if (*p != L':')
+               goto out_invalid_stream_name;
        p += 1;
        colon = wcschr(p, L':');
-       wimlib_assert(colon != NULL);
+       if (colon == NULL)
+               goto out_invalid_stream_name;
 
        if (wcscmp(colon + 1, L"$DATA")) {
                /* Not a DATA stream */
@@ -349,8 +407,8 @@ static int win32_capture_stream(const char *path,
        }
 
        is_named_stream = (p != colon);
-
        if (is_named_stream) {
+               /* Allocate an ADS entry for the named stream. */
                char *utf8_stream_name;
                size_t utf8_stream_name_len;
                ret = utf16_to_utf8((const char *)p,
@@ -367,6 +425,10 @@ static int win32_capture_stream(const char *path,
                }
        }
 
+       /* Create a UTF-16 string @spath that gives the filename, then a colon,
+        * then the stream name.  Or, if it's an unnamed stream, just the
+        * filename.  It is MALLOC()'ed so that it can be saved in the
+        * wim_lookup_table_entry if needed. */
        *colon = '\0';
        spath_nchars = path_utf16_nchars;
        if (is_named_stream)
@@ -383,15 +445,19 @@ static int win32_capture_stream(const char *path,
        ret = win32_sha1sum(spath, hash);
        if (ret) {
                err = GetLastError();
-               ERROR("Win32 API: Failed to read \"%s\" to calculate SHA1sum", path);
+               ERROR("Win32 API: Failed to read \"%ls\" to calculate SHA1sum",
+                     path_utf16);
                win32_error(err);
                goto out_free_spath;
        }
 
        lte = __lookup_resource(lookup_table, hash);
        if (lte) {
+               /* Use existing wim_lookup_table_entry that has the same SHA1
+                * message digest */
                lte->refcnt++;
        } else {
+               /* Make a new wim_lookup_table_entry */
                lte = new_lookup_table_entry();
                if (!lte) {
                        ret = WIMLIB_ERR_NOMEM;
@@ -413,10 +479,26 @@ out_free_spath:
        FREE(spath);
 out:
        return ret;
+out_invalid_stream_name:
+       ERROR("Invalid stream name: \"%ls:%ls\"", path_utf16, dat->cStreamName);
+       ret = WIMLIB_ERR_READ;
+       goto out;
 }
 
-static int win32_capture_streams(const char *path,
-                                const wchar_t *path_utf16,
+/* Scans a Win32 file for unnamed and named data streams (not reparse point
+ * streams).
+ *
+ * @path_utf16:         Path to the file (UTF-16LE).
+ *
+ * @path_utf16_nchars:  Number of 2-byte characters in @path_utf16.
+ *
+ * @inode:              WIM inode to save the stream into.
+ *
+ * @lookup_table:       Stream lookup table for the WIM.
+ *
+ * Returns 0 on success; nonzero on failure.
+ */
+static int win32_capture_streams(const wchar_t *path_utf16,
                                 size_t path_utf16_nchars,
                                 struct wim_inode *inode,
                                 struct wim_lookup_table *lookup_table)
@@ -438,14 +520,14 @@ static int win32_capture_streams(const char *path,
                {
                        return 0;
                } else {
-                       ERROR("Win32 API: Failed to look up data streams of \"%s\"",
-                             path);
+                       ERROR("Win32 API: Failed to look up data streams of \"%ls\"",
+                             path_utf16);
                        win32_error(err);
                        return WIMLIB_ERR_READ;
                }
        }
        do {
-               ret = win32_capture_stream(path, path_utf16,
+               ret = win32_capture_stream(path_utf16,
                                           path_utf16_nchars,
                                           inode, lookup_table,
                                           &dat);
@@ -454,7 +536,7 @@ static int win32_capture_streams(const char *path,
        } while (FindNextStreamW(hFind, &dat));
        err = GetLastError();
        if (err != ERROR_HANDLE_EOF) {
-               ERROR("Win32 API: Error reading data streams from \"%s\"", path);
+               ERROR("Win32 API: Error reading data streams from \"%ls\"", path_utf16);
                win32_error(err);
                ret = WIMLIB_ERR_READ;
        }
@@ -462,6 +544,7 @@ out_find_close:
        FindClose(hFind);
        return ret;
 }
+
 #endif
 
 /*
@@ -473,7 +556,7 @@ out_find_close:
  *             modified if successful.  Set to NULL if the file or directory was
  *             excluded from capture.
  *
- * @root_disk_path:  The path to the root of the directory tree on disk.
+ * @root_disk_path:  The path to the root of the directory tree on disk (UTF-8).
  *
  * @lookup_table: The lookup table for the WIM file.  For each file added to the
  *             dentry tree being built, an entry is added to the lookup table,
@@ -488,7 +571,8 @@ out_find_close:
  *
  * @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
@@ -599,11 +683,15 @@ static int build_dentry_tree(struct wim_dentry **root_ret,
        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,
@@ -775,7 +863,7 @@ static int build_dentry_tree(struct wim_dentry **root_ret,
                goto out_destroy_sd_set;
        path_utf16_nchars /= sizeof(wchar_t);
 
-       HANDLE hFile = win32_open_file(path_utf16);
+       HANDLE hFile = win32_open_file_readonly(path_utf16);
        if (hFile == INVALID_HANDLE_VALUE) {
                err = GetLastError();
                ERROR("Win32 API: Failed to open \"%s\"", root_disk_path);
@@ -803,7 +891,7 @@ static int build_dentry_tree(struct wim_dentry **root_ret,
                        ret = WIMLIB_ERR_NOMEM;
                else
                        ret = WIMLIB_ERR_ICONV_NOT_AVAILABLE;
-               goto out_free_path_utf16;
+               goto out_close_handle;
        }
 
        /* Start preparing the associated WIM inode */
@@ -823,8 +911,7 @@ static int build_dentry_tree(struct wim_dentry **root_ret,
        ret = win32_get_short_name(root, path_utf16);
        if (ret)
                goto out_close_handle;
-       ret = win32_get_security_descriptor(root, sd_set, path_utf16,
-                                           root_disk_path);
+       ret = win32_get_security_descriptor(root, sd_set, path_utf16);
        if (ret)
                goto out_close_handle;
 
@@ -832,9 +919,8 @@ static int build_dentry_tree(struct wim_dentry **root_ret,
                /* Directory (not a reparse point) --- recurse to children */
 
                /* But first... directories may have alternate data streams that
-                * need to be captured (maybe?) */
-               ret = win32_capture_streams(root_disk_path,
-                                           path_utf16,
+                * need to be captured. */
+               ret = win32_capture_streams(path_utf16,
                                            path_utf16_nchars,
                                            inode,
                                            lookup_table);
@@ -852,28 +938,29 @@ static int build_dentry_tree(struct wim_dentry **root_ret,
                                              path_utf16_nchars);
        } else if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
                /* Reparse point: save the reparse tag and data */
-
-               ret = win32_capture_reparse_point(root_disk_path,
-                                                 hFile,
+               ret = win32_capture_reparse_point(hFile,
                                                  inode,
-                                                 lookup_table);
-
+                                                 lookup_table,
+                                                 root_disk_path);
        } else {
-               /* Not a directory, not a reparse point */
-               ret = win32_capture_streams(root_disk_path,
-                                           path_utf16,
+               /* Not a directory, not a reparse point; capture the default
+                * file contents and any alternate data streams. */
+               ret = win32_capture_streams(path_utf16,
                                            path_utf16_nchars,
                                            inode,
                                            lookup_table);
        }
 out_close_handle:
        CloseHandle(hFile);
+out_free_path_utf16:
+       FREE(path_utf16);
 out_destroy_sd_set:
        if (extra_arg == NULL)
                destroy_sd_set(sd_set);
-out_free_path_utf16:
-       FREE(path_utf16);
 #endif
+       /* The below lines of code are common to both UNIX and Win32 builds.  It
+        * simply returns the captured directory tree if the capture was
+        * successful, or frees it if the capture was unsuccessful. */
 out:
        if (ret == 0)
                *root_ret = root;
@@ -1059,6 +1146,19 @@ static int capture_config_set_prefix(struct capture_config *config,
        return 0;
 }
 
+static bool path_matches_pattern(const char *path, const char *pattern)
+{
+#ifdef __WIN32__
+       return PathMatchSpecA(path, pattern);
+#else
+       return fnmatch(pattern, path, FNM_PATHNAME
+                       #ifdef FNM_CASEFOLD
+                                       | FNM_CASEFOLD
+                       #endif
+               ) == 0;
+#endif
+}
+
 static bool match_pattern(const char *path, const char *path_basename,
                          const struct pattern_list *list)
 {
@@ -1076,12 +1176,8 @@ static bool match_pattern(const char *path, const char *path_basename,
                                /* A file name pattern */
                                string = path_basename;
                }
-               if (fnmatch(pat, string, FNM_PATHNAME
-                       #ifdef FNM_CASEFOLD
-                                       | FNM_CASEFOLD
-                       #endif
-                       ) == 0)
-               {
+
+               if (path_matches_pattern(string, pat)) {
                        DEBUG("`%s' matches the pattern \"%s\"",
                              string, pat);
                        return true;
@@ -1120,7 +1216,7 @@ 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;
@@ -1136,6 +1232,17 @@ static const char *canonicalize_target_path(char *target_path)
        return target_path;
 }
 
+#if defined(__CYGWIN__) || defined(__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)
@@ -1144,6 +1251,14 @@ static void canonicalize_targets(struct wimlib_capture_source *sources,
                DEBUG("Canonicalizing { source: \"%s\", target=\"%s\"}",
                      sources->fs_source_path,
                      sources->wim_target_path);
+#if defined(__CYGWIN__) || defined(__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);
@@ -1161,7 +1276,7 @@ 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. */
+ * target paths are added after the containing target paths. */
 static void sort_sources(struct wimlib_capture_source *sources,
                         size_t num_sources)
 {
@@ -1230,9 +1345,8 @@ new_filler_directory(const char *name)
        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
+               /* 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;
        }
@@ -1246,6 +1360,9 @@ static int do_overlay(struct wim_dentry *target, struct wim_dentry *branch)
 {
        struct rb_root *rb_root;
 
+       DEBUG("Doing overlay %s => %s",
+             branch->file_name_utf8, target->file_name_utf8);
+
        if (!dentry_is_directory(target)) {
                ERROR("Cannot overlay directory `%s' over non-directory",
                      branch->file_name_utf8);
@@ -1267,6 +1384,7 @@ static int do_overlay(struct wim_dentry *target, struct wim_dentry *branch)
                        return WIMLIB_ERR_INVALID_OVERLAY;
                }
        }
+       free_dentry(branch);
        return 0;
 
 }
@@ -1289,6 +1407,9 @@ static int attach_branch(struct wim_dentry **root_p,
        char *slash;
        struct wim_dentry *dentry, *parent, *target;
 
+       DEBUG("Attaching branch \"%s\" => \"%s\"",
+             branch->file_name_utf8, target_path);
+
        if (*target_path == '\0') {
                /* Target: root directory */
                if (*root_p) {
@@ -1444,11 +1565,18 @@ WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w,
        DEBUG("Building dentry tree.");
        if (num_sources == 0) {
                root_dentry = new_filler_directory("");
-               if (!root_dentry)
+               if (!root_dentry) {
+                       ret = WIMLIB_ERR_NOMEM;
                        goto out_free_security_data;
+               }
        } else {
                size_t i;
 
+#if defined(__CYGWIN__) || defined(__WIN32__)
+               win32_acquire_privilege(SE_BACKUP_NAME);
+               win32_acquire_privilege(SE_SECURITY_NAME);
+               win32_acquire_privilege(SE_TAKE_OWNERSHIP_NAME);
+#endif
                root_dentry = NULL;
                i = 0;
                do {
@@ -1527,7 +1655,7 @@ WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w,
        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);
@@ -1542,6 +1670,11 @@ out_free_security_data:
 out_destroy_capture_config:
        destroy_capture_config(&config);
 out:
+#if defined(__CYGWIN__) || defined(__WIN32__)
+       win32_release_privilege(SE_BACKUP_NAME);
+       win32_release_privilege(SE_SECURITY_NAME);
+       win32_release_privilege(SE_TAKE_OWNERSHIP_NAME);
+#endif
        return ret;
 }