From: Eric Biggers Date: Sun, 24 Mar 2013 03:57:14 +0000 (-0500) Subject: Win32 updates X-Git-Tag: v1.3.2~15 X-Git-Url: https://wimlib.net/git/?p=wimlib;a=commitdiff_plain;h=f2db311f527e6037c836cf1b14debb841e09b440 Win32 updates Add --strict-acls option (WIMLIB_ADD_IMAGE_FLAG_STRICT_ACLS, WIMLIB_EXTRACT_FLAG_STRICT_ACLS) Better warnings when failing to get/set security data; also return to using SetFileSecurity() to avoid problems with specifying descriptor in CreateDirectory() or CreateFile(). --- diff --git a/doc/imagex-apply.1.in b/doc/imagex-apply.1.in index 4c6a1b45..adf6d035 100644 --- a/doc/imagex-apply.1.in +++ b/doc/imagex-apply.1.in @@ -252,10 +252,17 @@ the \fB--unix-data\fR flag. By passing \fB--unix-data\fR to \fB@IMAGEX_PROGNAME@ apply\fR instead, this causes this UNIX-specific data to be restored when available. .TP -\fB--noacls\fR +\fB--no-acls\fR In the NTFS apply mode, do not apply security descriptors. This flag is also available in the native Win32 build of wimlib and may be useful when running \fB@IMAGEX_PROGNAME@\fR as a non-administrator. +.TP +\fB--strict-acls\fR +Fail immediately if the full security descriptor of any file or directory cannot +be set exactly as specified in the WIM file. The default behavior without this +option is to fall back to setting a security descriptor with the SACL omitted, +then only the default inherited security descriptor, if we do not have +permission to set the desired one. .SH NOTES diff --git a/doc/imagex-capture.1.in b/doc/imagex-capture.1.in index 95fdd763..5822d164 100644 --- a/doc/imagex-capture.1.in +++ b/doc/imagex-capture.1.in @@ -234,10 +234,19 @@ for convenience only, in case you want to use \fB@IMAGEX_PROGNAME@\fR to archive UNIX. Microsoft's software will not understand this special information. .TP -\fB--noacls\fR +\fB--no-acls\fR In the NTFS capture mode, do not capture security descriptors. This flag is -also available in the native Win32 build of wimlib and may be useful when -running \fB@IMAGEX_PROGNAME@\fR as a non-administrator. +also available in the native Win32 build of wimlib. +.TP +\fB--strict-acls\fR +In the Win32 native build of wimlib, fail immediately if the full security +descriptor of any file or directory cannot be read. The default behavior +without this option is to first try omitting the SACL from the security +descriptor, then to try omitting the security descriptor entirely. The purpose +of this is to capture as much data as possible without always requiring +Administrator privileges. However, if you desire that all security descriptors +be captured exactly, you may with to provide this option, although the +Administrator should have permission to read everything anyway. .TP \fB--source-list\fR \fB@IMAGEX_PROGNAME@ capture\fR and \fB@IMAGEX_PROGNAME@ append\fR, as of wimlib 1.3.0, support a new diff --git a/programs/imagex.c b/programs/imagex.c index a2e9e5ea..b35cea90 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -88,14 +88,14 @@ IMAGEX_PROGNAME" append (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n" " [DESCRIPTION] [--boot] [--check] [--flags EDITION_ID]\n" " [--verbose] [--dereference] [--config=FILE]\n" " [--threads=NUM_THREADS] [--rebuild] [--unix-data]\n" -" [--source-list] [--noacls]\n" +" [--source-list] [--no-acls] [--strict-acls]\n" ), [APPLY] = T( IMAGEX_PROGNAME" apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all]\n" " (DIRECTORY | NTFS_VOLUME) [--check] [--hardlink]\n" " [--symlink] [--verbose] [--ref=\"GLOB\"] [--unix-data]\n" -" [--noacls]\n" +" [--no-acls] [--strict-acls]\n" ), [CAPTURE] = T( @@ -103,7 +103,7 @@ IMAGEX_PROGNAME" capture (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n" " [DESCRIPTION] [--boot] [--check] [--compress=TYPE]\n" " [--flags EDITION_ID] [--verbose] [--dereference]\n" " [--config=FILE] [--threads=NUM_THREADS] [--unix-data]\n" -" [--source-list] [--noacls]\n" +" [--source-list] [--no-acls] [--strict-acls]\n" ), [DELETE] = T( @@ -164,6 +164,8 @@ static const struct option apply_options[] = { {T("ref"), required_argument, NULL, 'r'}, {T("unix-data"), no_argument, NULL, 'U'}, {T("noacls"), no_argument, NULL, 'N'}, + {T("no-acls"), no_argument, NULL, 'N'}, + {T("strict-acls"), no_argument, NULL, 'A'}, {NULL, 0, NULL, 0}, }; static const struct option capture_or_append_options[] = { @@ -179,6 +181,8 @@ static const struct option capture_or_append_options[] = { {T("unix-data"), no_argument, NULL, 'U'}, {T("source-list"), no_argument, NULL, 'S'}, {T("noacls"), no_argument, NULL, 'N'}, + {T("no-acls"), no_argument, NULL, 'N'}, + {T("strict-acls"), no_argument, NULL, 'A'}, {NULL, 0, NULL, 0}, }; static const struct option delete_options[] = { @@ -978,7 +982,10 @@ imagex_apply(int argc, tchar **argv) extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA; break; case 'N': - extract_flags |= WIMLIB_EXTRACT_FLAG_NOACLS; + extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS; + break; + case 'A': + extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS; break; default: usage(APPLY); @@ -1143,6 +1150,9 @@ imagex_capture_or_append(int argc, tchar **argv) case 'N': add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_NO_ACLS; break; + case 'A': + add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_STRICT_ACLS; + break; default: usage(cmd); return -1; diff --git a/src/encoding.c b/src/encoding.c index f5b978ab..822e9f53 100644 --- a/src/encoding.c +++ b/src/encoding.c @@ -254,7 +254,7 @@ DEFINE_CHAR_CONVERSION_FUNCTIONS(utf16le, "UTF-16LE", utf16lechar, /* tchar to UTF-8 and back */ #if TCHAR_IS_UTF16LE -DEFINE_CHAR_CONVERSION_FUNCTIONS(tstr, "UTF16-LE", tchar, +DEFINE_CHAR_CONVERSION_FUNCTIONS(tstr, "UTF-16LE", tchar, utf8, "UTF-8", char, false, in_nbytes * 2, @@ -264,7 +264,7 @@ DEFINE_CHAR_CONVERSION_FUNCTIONS(tstr, "UTF16-LE", tchar, static) DEFINE_CHAR_CONVERSION_FUNCTIONS(utf8, "UTF-8", char, - tstr, "UTF16-LE", tchar, + tstr, "UTF-16LE", tchar, false, in_nbytes * 2, WIMLIB_ERR_INVALID_UTF8_STRING, @@ -300,7 +300,8 @@ int tstr_to_utf8_simple(const tchar *tstr, char **out) { size_t out_nbytes; - return tstr_to_utf8(tstr, tstrlen(tstr), out, &out_nbytes); + return tstr_to_utf8(tstr, tstrlen(tstr) * sizeof(tchar), + out, &out_nbytes); } int diff --git a/src/extract_image.c b/src/extract_image.c index 0f9252a3..5f311766 100644 --- a/src/extract_image.c +++ b/src/extract_image.c @@ -800,11 +800,11 @@ extract_single_image(WIMStruct *w, int image, struct apply_args args; const struct apply_operations *ops; + memset(&args, 0, sizeof(args)); + args.w = w; args.target = target; args.extract_flags = extract_flags; - args.num_utime_warnings = 0; - args.stream_list = &stream_list; args.progress_func = progress_func; if (progress_func) { diff --git a/src/ntfs-apply.c b/src/ntfs-apply.c index d28dbfac..ca73db2f 100644 --- a/src/ntfs-apply.c +++ b/src/ntfs-apply.c @@ -302,7 +302,7 @@ apply_file_attributes_and_security_data(ntfs_inode *ni, return WIMLIB_ERR_NTFS_3G; } if (inode->i_security_id != -1 && - !(extract_flags & WIMLIB_EXTRACT_FLAG_NOACLS)) + !(extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)) { const char *desc; const struct wim_security_data *sd; diff --git a/src/verify.c b/src/verify.c index decd90c0..fc566633 100644 --- a/src/verify.c +++ b/src/verify.c @@ -132,7 +132,7 @@ verify_dentry(struct wim_dentry *dentry, void *wim) /* Verify the associated inode, but only one time no matter how many * dentries it has (unless we are doing a full verification of the WIM, * in which case we need to force the inode to be verified again.) */ - if (!dentry->d_inode->i_verified || w->full_verification_in_progress) { + if (!dentry->d_inode->i_verified) { ret = verify_inode(dentry->d_inode, w); if (ret != 0) return ret; @@ -173,7 +173,14 @@ verify_dentry(struct wim_dentry *dentry, void *wim) static int image_run_full_verifications(WIMStruct *w) { - return for_dentry_in_tree(wim_root_dentry(w), verify_dentry, w); + struct wim_image_metadata *imd; + struct wim_inode *inode; + struct hlist_node *cur; + + imd = wim_get_current_image_metadata(w); + hlist_for_each_entry(inode, cur, &imd->inode_list, i_hlist) + inode->i_verified = 0; + return for_dentry_in_tree(imd->root_dentry, verify_dentry, w); } static int @@ -207,11 +214,10 @@ wim_run_full_verifications(WIMStruct *w) int ret; for_lookup_table_entry(w->lookup_table, lte_zero_real_refcnt, NULL); - w->all_images_verified = 1; - w->full_verification_in_progress = 1; ret = for_image(w, WIMLIB_ALL_IMAGES, image_run_full_verifications); - w->full_verification_in_progress = 0; if (ret == 0) { + w->all_images_verified = 1; + unsigned long num_ltes_with_bogus_refcnt = 0; for (int i = 0; i < w->hdr.image_count; i++) w->image_metadata[i].metadata_lte->real_refcnt++; @@ -223,8 +229,6 @@ wim_run_full_verifications(WIMStruct *w) " their reference counts fixed.", num_ltes_with_bogus_refcnt); } - } else { - w->all_images_verified = 0; } return ret; } diff --git a/src/wimlib.h b/src/wimlib.h index 4308561c..b23bb405 100644 --- a/src/wimlib.h +++ b/src/wimlib.h @@ -638,6 +638,13 @@ struct wimlib_capture_source { * mode, or in Win32 native builds. */ #define WIMLIB_ADD_IMAGE_FLAG_NO_ACLS 0x00000020 +/** Fail immediately if the full security descriptor of any file or directory + * cannot be accessed. Only has an effect in Win32 native builds. The default + * behavior without this flag is to first try omitting the SACL from the + * security descriptor, then to try omitting the security descriptor entirely. + * */ +#define WIMLIB_ADD_IMAGE_FLAG_STRICT_ACLS 0x00000040 + /****************************** * WIMLIB_EXPORT_FLAG_* * ******************************/ @@ -676,7 +683,14 @@ struct wimlib_capture_source { /** Do not extract security descriptors. Only has an effect in NTFS apply mode, * or in Win32 native builds. */ -#define WIMLIB_EXTRACT_FLAG_NOACLS 0x00000040 +#define WIMLIB_EXTRACT_FLAG_NO_ACLS 0x00000040 + +/** Fail immediately if the full security descriptor of any file or directory + * cannot be set exactly as specified in the WIM file. The default behavior + * without this flag is to fall back to setting the security descriptor with the + * SACL omitted, then only the default inherited security descriptor, if we do + * not have permission to set the desired one. */ +#define WIMLIB_EXTRACT_FLAG_STRICT_ACLS 0x00000080 /****************************** * WIMLIB_MOUNT_FLAG_* * diff --git a/src/wimlib_internal.h b/src/wimlib_internal.h index 8b696b52..a5564b48 100644 --- a/src/wimlib_internal.h +++ b/src/wimlib_internal.h @@ -316,7 +316,6 @@ struct WIMStruct { u8 deletion_occurred : 1; u8 all_images_verified : 1; - u8 full_verification_in_progress : 1; u8 wim_locked : 1; }; @@ -441,14 +440,27 @@ struct apply_args { WIMStruct *w; const tchar *target; int extract_flags; - unsigned num_utime_warnings; - struct list_head *stream_list; union wimlib_progress_info progress; -#ifdef WITH_NTFS_3G - struct _ntfs_volume *vol; -#endif wimlib_progress_func_t progress_func; int (*apply_dentry)(struct wim_dentry *, void *); + union { + #ifdef WITH_NTFS_3G + struct { + /* NTFS apply only */ + struct _ntfs_volume *vol; + }; + #endif + struct { + /* Normal apply only (UNIX) */ + unsigned long num_utime_warnings; + }; + + struct { + /* Normal apply only (Win32) */ + unsigned long num_set_sacl_priv_notheld; + unsigned long num_set_sd_access_denied; + }; + }; }; extern int diff --git a/src/win32.c b/src/win32.c index 21225e2f..3a914943 100644 --- a/src/win32.c +++ b/src/win32.c @@ -44,7 +44,15 @@ #include +#define MAX_GET_SD_ACCESS_DENIED_WARNINGS 1 +#define MAX_GET_SACL_PRIV_NOTHELD_WARNINGS 1 +struct win32_capture_state { + unsigned long num_get_sd_access_denied; + unsigned long num_get_sacl_priv_notheld; +}; +#define MAX_SET_SD_ACCESS_DENIED_WARNINGS 1 +#define MAX_SET_SACL_PRIV_NOTHELD_WARNINGS 1 /* Pointers to functions that are not available on all targetted versions of * Windows (XP and later). NOTE: The WINAPI annotations seem to be important; I @@ -106,13 +114,21 @@ win32_global_cleanup() } } -static const wchar_t *access_denied_msg = +static const wchar_t *capture_access_denied_msg = L" If you are not running this program as the administrator, you may\n" " need to do so, so that all data and metadata can be backed up.\n" " Otherwise, there may be no way to access the desired data or\n" " metadata without taking ownership of the file or directory.\n" ; +static const wchar_t *apply_access_denied_msg = +L"If you are not running this program as the administrator, you may\n" + " need to do so, so that all data and metadata can be extracted\n" + " exactly as the origignal copy. However, if you do not care that\n" + " the security descriptors are extracted correctly, you could run\n" + " `wimlib-imagex apply' with the --no-acls flag instead.\n" + ; + #ifdef ENABLE_ERROR_MESSAGES void win32_error(u32 err_code) @@ -210,17 +226,21 @@ win32_get_short_name(struct wim_dentry *dentry, const wchar_t *path) static int win32_get_security_descriptor(struct wim_dentry *dentry, struct sd_set *sd_set, - const wchar_t *path) + const wchar_t *path, + struct win32_capture_state *state, + int add_image_flags) { SECURITY_INFORMATION requestedInformation; DWORD lenNeeded = 0; BOOL status; DWORD err; + unsigned long n; requestedInformation = DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION; +again: /* Request length of security descriptor */ status = GetFileSecurityW(path, requestedInformation, NULL, 0, &lenNeeded); @@ -243,11 +263,39 @@ win32_get_security_descriptor(struct wim_dentry *dentry, } } - if (err == ERROR_ACCESS_DENIED) { - WARNING("Failed to read security descriptor of \"%ls\": " - "Access denied!\n%ls", path, access_denied_msg); + if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_STRICT_ACLS) + goto fail; + + switch (err) { + case ERROR_PRIVILEGE_NOT_HELD: + if (requestedInformation & SACL_SECURITY_INFORMATION) { + n = state->num_get_sacl_priv_notheld++; + requestedInformation &= ~SACL_SECURITY_INFORMATION; + if (n < MAX_GET_SACL_PRIV_NOTHELD_WARNINGS) { + WARNING( +"We don't have enough privileges to read the full security\n" +" descriptor of \"%ls\"!\n" +" Re-trying with SACL omitted.\n", path); + } else if (n == MAX_GET_SACL_PRIV_NOTHELD_WARNINGS) { + WARNING( +"Suppressing further privileges not held error messages when reading\n" +" security descriptors."); + } + goto again; + } + /* Fall through */ + case ERROR_ACCESS_DENIED: + n = state->num_get_sd_access_denied++; + if (n < MAX_GET_SD_ACCESS_DENIED_WARNINGS) { + WARNING("Failed to read security descriptor of \"%ls\": " + "Access denied!\n%ls", path, capture_access_denied_msg); + } else if (n == MAX_GET_SD_ACCESS_DENIED_WARNINGS) { + WARNING("Suppressing further access denied errors messages i" + "when reading security descriptors"); + } return 0; - } else { + default: +fail: ERROR("Failed to read security descriptor of \"%ls\"", path); win32_error(err); return WIMLIB_ERR_READ; @@ -256,40 +304,43 @@ win32_get_security_descriptor(struct wim_dentry *dentry, static int win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, - const wchar_t *path, - const size_t path_num_chars, + wchar_t *path, + size_t path_num_chars, struct wim_lookup_table *lookup_table, struct sd_set *sd_set, const struct capture_config *config, int add_image_flags, - wimlib_progress_func_t progress_func); + wimlib_progress_func_t progress_func, + struct win32_capture_state *state); /* Reads the directory entries of directory using a Win32 API and recursively * calls win32_build_dentry_tree() on them. */ static int win32_recurse_directory(struct wim_dentry *root, - const wchar_t *dir_path, - const size_t dir_path_num_chars, + wchar_t *dir_path, + size_t dir_path_num_chars, struct wim_lookup_table *lookup_table, struct sd_set *sd_set, const struct capture_config *config, int add_image_flags, - wimlib_progress_func_t progress_func) + wimlib_progress_func_t progress_func, + struct win32_capture_state *state) { WIN32_FIND_DATAW dat; HANDLE hFind; DWORD err; 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[dir_path_num_chars + 3]; - swprintf(pattern_buf, L"%ls/*", dir_path); - hFind = FindFirstFileW(pattern_buf, &dat); - } + /* 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. */ + dir_path[dir_path_num_chars] = L'/'; + dir_path[dir_path_num_chars + 1] = L'*'; + dir_path[dir_path_num_chars + 2] = L'\0'; + hFind = FindFirstFileW(dir_path, &dat); + dir_path[dir_path_num_chars] = L'\0'; + if (hFind == INVALID_HANDLE_VALUE) { err = GetLastError(); if (err == ERROR_FILE_NOT_FOUND) { @@ -303,29 +354,34 @@ win32_recurse_directory(struct wim_dentry *root, ret = 0; do { /* Skip . and .. entries */ - if (wcscmp(dat.cFileName, L".") && wcscmp(dat.cFileName, L"..")) - { - size_t filename_num_chars = wcslen(dat.cFileName); - size_t new_path_num_chars = dir_path_num_chars + 1 + - filename_num_chars; - wchar_t new_path[new_path_num_chars + 1]; - - swprintf(new_path, L"%ls/%ls", dir_path, dat.cFileName); - - struct wim_dentry *child; - ret = win32_build_dentry_tree_recursive(&child, - new_path, - new_path_num_chars, - lookup_table, - sd_set, - config, - add_image_flags, - progress_func); - if (ret) - goto out_find_close; - if (child) - dentry_add_child(root, child); - } + if (dat.cFileName[0] == L'.' && + (dat.cFileName[1] == L'\0' || + (dat.cFileName[1] == L'.' && + dat.cFileName[2] == L'\0'))) + continue; + size_t filename_len = wcslen(dat.cFileName); + + dir_path[dir_path_num_chars] = L'/'; + wmemcpy(dir_path + dir_path_num_chars + 1, + dat.cFileName, + filename_len + 1); + + struct wim_dentry *child; + size_t path_len = dir_path_num_chars + 1 + filename_len; + ret = win32_build_dentry_tree_recursive(&child, + dir_path, + path_len, + lookup_table, + sd_set, + config, + add_image_flags, + progress_func, + state); + dir_path[dir_path_num_chars] = L'\0'; + if (ret) + goto out_find_close; + if (child) + dentry_add_child(root, child); } while (FindNextFileW(hFind, &dat)); err = GetLastError(); if (err != ERROR_NO_MORE_FILES) { @@ -621,7 +677,7 @@ win32_capture_streams(const wchar_t *path, if (err == ERROR_ACCESS_DENIED) { WARNING("Failed to look up data streams " "of \"%ls\": Access denied!\n%ls", - path, access_denied_msg); + path, capture_access_denied_msg); return 0; } else { ERROR("Failed to look up data streams " @@ -666,13 +722,14 @@ unnamed_only: static int win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, - const wchar_t *path, - const size_t path_num_chars, + wchar_t *path, + size_t path_num_chars, struct wim_lookup_table *lookup_table, struct sd_set *sd_set, const struct capture_config *config, int add_image_flags, - wimlib_progress_func_t progress_func) + wimlib_progress_func_t progress_func, + struct win32_capture_state *state) { struct wim_dentry *root = NULL; struct wim_inode *inode; @@ -751,7 +808,8 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, goto out_close_handle; if (!(add_image_flags & WIMLIB_ADD_IMAGE_FLAG_NO_ACLS)) { - ret = win32_get_security_descriptor(root, sd_set, path); + ret = win32_get_security_descriptor(root, sd_set, path, state, + add_image_flags); if (ret) goto out_close_handle; } @@ -778,7 +836,8 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, sd_set, config, add_image_flags, - progress_func); + progress_func, + state); } else if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) { /* Reparse point: save the reparse tag and data */ ret = win32_capture_reparse_point(hFile, @@ -804,6 +863,38 @@ out: return ret; } +static void +win32_do_capture_warnings(const struct win32_capture_state *state, + int add_image_flags) +{ + if (state->num_get_sacl_priv_notheld == 0 && + state->num_get_sd_access_denied == 0) + return; + + WARNING(""); + WARNING("Built dentry tree successfully, but with the following problem(s):"); + if (state->num_get_sacl_priv_notheld != 0) { + WARNING("Could not capture SACL (System Access Control List)\n" + " on %lu files or directories.", + state->num_get_sacl_priv_notheld); + } + if (state->num_get_sd_access_denied != 0) { + WARNING("Could not capture security descriptor at all\n" + " on %lu files or directories.", + state->num_get_sd_access_denied); + } + WARNING( + "Try running the program as the Administrator to make sure all the\n" +" desired metadata has been captured exactly. However, if you\n" +" do not care about capturing security descriptors correctly, then\n" +" nothing more needs to be done%ls\n", + (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_NO_ACLS) ? L"." : + L", although you might consider\n" +" passing the --no-acls flag to `wimlib-imagex capture' or\n" +" `wimlib-imagex append' to explicitly capture no security\n" +" descriptors.\n"); +} + /* Win32 version of capturing a directory tree */ int win32_build_dentry_tree(struct wim_dentry **root_ret, @@ -818,6 +909,7 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, size_t path_nchars; wchar_t *path; int ret; + struct win32_capture_state state; path_nchars = wcslen(root_disk_path); if (path_nchars > 32767) @@ -833,6 +925,7 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, wmemcpy(path, root_disk_path, path_nchars + 1); + memset(&state, 0, sizeof(state)); ret = win32_build_dentry_tree_recursive(root_ret, path, path_nchars, @@ -840,21 +933,14 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, sd_set, config, add_image_flags, - progress_func); + progress_func, + &state); FREE(path); + if (ret == 0) + win32_do_capture_warnings(&state, add_image_flags); return ret; } -/* Replacement for POSIX fnmatch() (partial functionality only) */ -int -fnmatch(const wchar_t *pattern, const wchar_t *string, int flags) -{ - if (PathMatchSpecW(string, pattern)) - return 0; - else - return FNM_NOMATCH; -} - static int win32_set_reparse_data(HANDLE h, u32 reparse_tag, @@ -922,6 +1008,82 @@ win32_set_reparse_data(HANDLE h, return 0; } +/* + * Sets the security descriptor on an extracted file. + */ +static int +win32_set_security_data(const struct wim_inode *inode, + const wchar_t *path, + struct apply_args *args) +{ + PSECURITY_DESCRIPTOR descriptor; + unsigned long n; + DWORD err; + + descriptor = wim_const_security_data(args->w)->descriptors[inode->i_security_id]; + + SECURITY_INFORMATION securityInformation = DACL_SECURITY_INFORMATION | + SACL_SECURITY_INFORMATION | + OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION; +again: + if (SetFileSecurityW(path, securityInformation, descriptor)) + return 0; + + err = GetLastError(); + + if (args->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS) + goto fail; + + switch (err) { + case ERROR_PRIVILEGE_NOT_HELD: + if (securityInformation & SACL_SECURITY_INFORMATION) { + n = args->num_set_sacl_priv_notheld++; + securityInformation &= ~SACL_SECURITY_INFORMATION; + if (n < MAX_SET_SACL_PRIV_NOTHELD_WARNINGS) { + WARNING( +"We don't have enough privileges to set the full security\n" +" descriptor on \"%ls\"!\n", path); + if (args->num_set_sd_access_denied + + args->num_set_sacl_priv_notheld == 1) + { + WARNING("%ls", apply_access_denied_msg); + } + WARNING("Re-trying with SACL omitted.\n", path); + } else if (n == MAX_GET_SACL_PRIV_NOTHELD_WARNINGS) { + WARNING( +"Suppressing further 'privileges not held' error messages when setting\n" +" security descriptors."); + } + goto again; + } + /* Fall through */ + case ERROR_INVALID_OWNER: + case ERROR_ACCESS_DENIED: + n = args->num_set_sd_access_denied++; + if (n < MAX_SET_SD_ACCESS_DENIED_WARNINGS) { + WARNING("Failed to set security descriptor on \"%ls\": " + "Access denied!\n", path); + if (args->num_set_sd_access_denied + + args->num_set_sacl_priv_notheld == 1) + { + WARNING("%ls", apply_access_denied_msg); + } + } else if (n == MAX_SET_SD_ACCESS_DENIED_WARNINGS) { + WARNING( +"Suppressing further access denied error messages when setting\n" +" security descriptors"); + } + return 0; + default: +fail: + ERROR("Failed to set security descriptor on \"%ls\"", path); + win32_error(err); + return WIMLIB_ERR_WRITE; + } + return 0; +} + static int win32_extract_chunk(const void *buf, size_t len, u64 offset, void *arg) @@ -970,8 +1132,7 @@ static int win32_extract_stream(const struct wim_inode *inode, const wchar_t *path, const wchar_t *stream_name_utf16, - struct wim_lookup_table_entry *lte, - const struct wim_security_data *security_data) + struct wim_lookup_table_entry *lte) { wchar_t *stream_path; HANDLE h; @@ -979,17 +1140,6 @@ win32_extract_stream(const struct wim_inode *inode, DWORD err; DWORD creationDisposition = CREATE_ALWAYS; - SECURITY_ATTRIBUTES *secattr; - - if (security_data && inode->i_security_id != -1) { - secattr = alloca(sizeof(*secattr)); - secattr->nLength = sizeof(*secattr); - secattr->lpSecurityDescriptor = security_data->descriptors[inode->i_security_id]; - secattr->bInheritHandle = FALSE; - } else { - secattr = NULL; - } - if (stream_name_utf16) { /* Named stream. Create a buffer that contains the UTF-16LE * string [.\]@path:@stream_name_utf16. This is needed to @@ -1028,7 +1178,7 @@ win32_extract_stream(const struct wim_inode *inode, * the call to CreateFileW() will merely open the directory that * was already created rather than creating a new file. */ if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) { - if (!CreateDirectoryW(stream_path, secattr)) { + if (!CreateDirectoryW(stream_path, NULL)) { err = GetLastError(); switch (err) { case ERROR_ALREADY_EXISTS: @@ -1058,7 +1208,7 @@ win32_extract_stream(const struct wim_inode *inode, h = CreateFileW(stream_path, GENERIC_WRITE, 0, - secattr, + NULL, creationDisposition, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS | @@ -1119,15 +1269,13 @@ out: */ static int win32_extract_streams(const struct wim_inode *inode, - const wchar_t *path, u64 *completed_bytes_p, - const struct wim_security_data *security_data) + const wchar_t *path, u64 *completed_bytes_p) { struct wim_lookup_table_entry *unnamed_lte; int ret; unnamed_lte = inode_unnamed_lte_resolved(inode); - ret = win32_extract_stream(inode, path, NULL, unnamed_lte, - security_data); + ret = win32_extract_stream(inode, path, NULL, unnamed_lte); if (ret) goto out; if (unnamed_lte) @@ -1145,8 +1293,7 @@ win32_extract_streams(const struct wim_inode *inode, ret = win32_extract_stream(inode, path, ads_entry->stream_name, - ads_entry->lte, - NULL); + ads_entry->lte); if (ret) break; if (ads_entry->lte) @@ -1159,10 +1306,11 @@ out: /* Extract a file, directory, reparse point, or hard link to an * already-extracted file using the Win32 API */ -int win32_do_apply_dentry(const wchar_t *output_path, - size_t output_path_num_chars, - struct wim_dentry *dentry, - struct apply_args *args) +int +win32_do_apply_dentry(const wchar_t *output_path, + size_t output_path_num_chars, + struct wim_dentry *dentry, + struct apply_args *args) { int ret; struct wim_inode *inode = dentry->d_inode; @@ -1173,28 +1321,29 @@ int win32_do_apply_dentry(const wchar_t *output_path, * hard link. */ DEBUG("Creating hard link \"%ls => %ls\"", output_path, inode->i_extracted_file); - if (CreateHardLinkW(output_path, inode->i_extracted_file, NULL)) { - ret = 0; - } else { + if (!CreateHardLinkW(output_path, inode->i_extracted_file, NULL)) { err = GetLastError(); ERROR("Can't create hard link \"%ls => %ls\"", output_path, inode->i_extracted_file); - ret = WIMLIB_ERR_LINK; win32_error(err); + return WIMLIB_ERR_LINK; } } else { /* Create the file, directory, or reparse point, and extract the * data streams. */ - const struct wim_security_data *security_data; - if (args->extract_flags & WIMLIB_EXTRACT_FLAG_NOACLS) - security_data = NULL; - else - security_data = wim_const_security_data(args->w); - ret = win32_extract_streams(inode, output_path, - &args->progress.extract.completed_bytes, - security_data); - if (ret == 0 && inode->i_nlink > 1) { + &args->progress.extract.completed_bytes); + if (ret) + return ret; + + if (inode->i_security_id >= 0 && + !(args->extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)) + { + ret = win32_set_security_data(inode, output_path, args); + if (ret) + return ret; + } + if (inode->i_nlink > 1) { /* Save extracted path for a later call to * CreateHardLinkW() if this inode has multiple links. * */ @@ -1203,7 +1352,7 @@ int win32_do_apply_dentry(const wchar_t *output_path, ret = WIMLIB_ERR_NOMEM; } } - return ret; + return 0; } /* Set timestamps on an extracted file using the Win32 API */ @@ -1335,6 +1484,16 @@ win32_rename_replacement(const wchar_t *oldpath, const wchar_t *newpath) } } +/* Replacement for POSIX fnmatch() (partial functionality only) */ +int +fnmatch(const wchar_t *pattern, const wchar_t *string, int flags) +{ + if (PathMatchSpecW(string, pattern)) + return 0; + else + return FNM_NOMATCH; +} + /* truncate() replacement */ int win32_truncate_replacement(const wchar_t *path, off_t size)