X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fwin32_apply.c;h=d9bf63c2fd305b93cf932b1bbff9d0dc996f8c83;hp=3a2c077dab34436c79732ac2be0e9ade33452b8b;hb=8a1b5f46145cc8be56ce09eec1513db079b86bc7;hpb=9f25365263682b2b8cb7ef760d9668157830f566 diff --git a/src/win32_apply.c b/src/win32_apply.c index 3a2c077d..d9bf63c2 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.c @@ -30,14 +30,15 @@ #include "wimlib/apply.h" #include "wimlib/assert.h" #include "wimlib/blob_table.h" -#include "wimlib/capture.h" /* for mangle_pat() and match_pattern_list() */ #include "wimlib/dentry.h" #include "wimlib/encoding.h" #include "wimlib/error.h" #include "wimlib/metadata.h" +#include "wimlib/object_id.h" #include "wimlib/paths.h" #include "wimlib/pattern.h" #include "wimlib/reparse.h" +#include "wimlib/scan.h" /* for mangle_pat() and match_pattern_list() */ #include "wimlib/textfile.h" #include "wimlib/xml.h" #include "wimlib/wimboot.h" @@ -68,7 +69,7 @@ struct win32_apply_ctx { } wimboot; /* External backing information */ - struct string_set *prepopulate_pats; + struct string_list *prepopulate_pats; void *mem_prepopulate_pats; bool tried_to_load_prepopulate_list; @@ -158,6 +159,9 @@ struct win32_apply_ctx { * [PrepopulateList]. */ unsigned long num_system_compression_exclusions; + /* Number of files for which we couldn't set the object ID. */ + unsigned long num_object_id_failures; + /* The Windows build number of the image being applied, or 0 if unknown. */ u64 windows_build_number; @@ -299,6 +303,9 @@ win32_get_supported_features(const wchar_t *target, if (short_names_supported) supported_features->short_names = 1; + if (vol_flags & FILE_SUPPORTS_OBJECT_IDS) + supported_features->object_ids = 1; + supported_features->timestamps = 1; /* Note: Windows does not support case sensitive filenames! At least @@ -331,7 +338,7 @@ load_prepopulate_pats(struct win32_apply_ctx *ctx) const struct blob_descriptor *blob; int ret; void *buf; - struct string_set *s; + struct string_list *strings; void *mem; struct text_file_section sec; @@ -359,14 +366,14 @@ load_prepopulate_pats(struct win32_apply_ctx *ctx) if (ret) return ret; - s = CALLOC(1, sizeof(struct string_set)); - if (!s) { + strings = CALLOC(1, sizeof(struct string_list)); + if (!strings) { FREE(buf); return WIMLIB_ERR_NOMEM; } sec.name = T("PrepopulateList"); - sec.strings = s; + sec.strings = strings; ret = do_load_text_file(path, buf, blob->size, &mem, &sec, 1, LOAD_TEXT_FILE_REMOVE_QUOTES | @@ -375,10 +382,10 @@ load_prepopulate_pats(struct win32_apply_ctx *ctx) STATIC_ASSERT(OS_PREFERRED_PATH_SEPARATOR == WIM_PATH_SEPARATOR); FREE(buf); if (ret) { - FREE(s); + FREE(strings); return ret; } - ctx->prepopulate_pats = s; + ctx->prepopulate_pats = strings; ctx->mem_prepopulate_pats = mem; return 0; } @@ -995,18 +1002,17 @@ open_target_directory(struct win32_apply_ctx *ctx) /* Don't use FILE_OPEN_REPARSE_POINT here; we want the extraction to * happen at the directory "pointed to" by the reparse point. */ - status = (*func_NtCreateFile)(&ctx->h_target, - FILE_TRAVERSE, - &ctx->attr, - &ctx->iosb, - NULL, - 0, - FILE_SHARE_VALID_FLAGS, - FILE_OPEN_IF, - FILE_DIRECTORY_FILE | - FILE_OPEN_FOR_BACKUP_INTENT, - NULL, - 0); + status = NtCreateFile(&ctx->h_target, + FILE_TRAVERSE, + &ctx->attr, + &ctx->iosb, + NULL, + 0, + FILE_SHARE_VALID_FLAGS, + FILE_OPEN_IF, + FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT, + NULL, + 0); if (!NT_SUCCESS(status)) { winnt_error(status, L"Can't open or create directory \"%ls\"", ctx->common.target); @@ -1021,7 +1027,7 @@ static void close_target_directory(struct win32_apply_ctx *ctx) { if (ctx->h_target) { - (*func_NtClose)(ctx->h_target); + NtClose(ctx->h_target); ctx->h_target = NULL; ctx->attr.RootDirectory = NULL; } @@ -1092,6 +1098,9 @@ adjust_compression_attribute(HANDLE h, const struct wim_dentry *dentry, { const bool compressed = (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_COMPRESSED); + FILE_BASIC_INFORMATION info; + USHORT compression_state; + NTSTATUS status; if (ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES) return 0; @@ -1099,15 +1108,10 @@ adjust_compression_attribute(HANDLE h, const struct wim_dentry *dentry, if (!ctx->common.supported_features.compressed_files) return 0; - FILE_BASIC_INFORMATION info; - NTSTATUS status; - USHORT compression_state; - DWORD bytes_returned; /* Get current attributes */ - status = (*func_NtQueryInformationFile)(h, &ctx->iosb, - &info, sizeof(info), - FileBasicInformation); + status = NtQueryInformationFile(h, &ctx->iosb, &info, sizeof(info), + FileBasicInformation); if (NT_SUCCESS(status) && compressed == !!(info.FileAttributes & FILE_ATTRIBUTE_COMPRESSED)) { @@ -1122,14 +1126,12 @@ adjust_compression_attribute(HANDLE h, const struct wim_dentry *dentry, else compression_state = COMPRESSION_FORMAT_NONE; - /* Note: don't use NtFsControlFile() here unless prepared to handle - * STATUS_PENDING. */ - if (DeviceIoControl(h, FSCTL_SET_COMPRESSION, - &compression_state, sizeof(USHORT), NULL, 0, - &bytes_returned, NULL)) + status = winnt_fsctl(h, FSCTL_SET_COMPRESSION, + &compression_state, sizeof(USHORT), NULL, 0, NULL); + if (NT_SUCCESS(status)) return 0; - win32_error(GetLastError(), L"Can't %s compression attribute on \"%ls\"", + winnt_error(status, L"Can't %s compression attribute on \"%ls\"", (compressed ? "set" : "clear"), current_path(ctx)); return WIMLIB_ERR_SET_ATTRIBUTES; } @@ -1195,10 +1197,10 @@ remove_conflicting_short_name(const struct wim_dentry *dentry, struct win32_appl ctx->pathbuf.Length = ((u8 *)end - (u8 *)ctx->pathbuf.Buffer); /* Open the conflicting file (by short name). */ - status = (*func_NtOpenFile)(&h, GENERIC_WRITE | DELETE, - &ctx->attr, &ctx->iosb, - FILE_SHARE_VALID_FLAGS, - FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT); + status = NtOpenFile(&h, GENERIC_WRITE | DELETE, + &ctx->attr, &ctx->iosb, + FILE_SHARE_VALID_FLAGS, + FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT); if (!NT_SUCCESS(status)) { winnt_warning(status, L"Can't open \"%ls\"", current_path(ctx)); goto out; @@ -1212,8 +1214,8 @@ remove_conflicting_short_name(const struct wim_dentry *dentry, struct win32_appl /* Try to remove the short name on the conflicting file. */ retry: - status = (*func_NtSetInformationFile)(h, &ctx->iosb, info, bufsize, - FileShortNameInformation); + status = NtSetInformationFile(h, &ctx->iosb, info, bufsize, + FileShortNameInformation); if (status == STATUS_INVALID_PARAMETER && !retried) { @@ -1231,7 +1233,7 @@ retry: retried = true; goto retry; } - (*func_NtClose)(h); + NtClose(h); out: build_extraction_path(dentry, ctx); return status; @@ -1281,8 +1283,8 @@ set_short_name(HANDLE h, const struct wim_dentry *dentry, memcpy(info->FileName, dentry->d_short_name, dentry->d_short_name_nbytes); retry: - status = (*func_NtSetInformationFile)(h, &ctx->iosb, info, bufsize, - FileShortNameInformation); + status = NtSetInformationFile(h, &ctx->iosb, info, bufsize, + FileShortNameInformation); if (NT_SUCCESS(status)) return 0; @@ -1347,7 +1349,7 @@ retry: * A wrapper around NtCreateFile() to make it slightly more usable... * This uses the path currently constructed in ctx->pathbuf. * - * Also, we always specify FILE_OPEN_FOR_BACKUP_INTENT and + * Also, we always specify SYNCHRONIZE access, FILE_OPEN_FOR_BACKUP_INTENT, and * FILE_OPEN_REPARSE_POINT. */ static NTSTATUS @@ -1359,19 +1361,19 @@ do_create_file(PHANDLE FileHandle, ULONG CreateOptions, struct win32_apply_ctx *ctx) { - return (*func_NtCreateFile)(FileHandle, - DesiredAccess, - &ctx->attr, - &ctx->iosb, - AllocationSize, - FileAttributes, - FILE_SHARE_VALID_FLAGS, - CreateDisposition, - CreateOptions | - FILE_OPEN_FOR_BACKUP_INTENT | - FILE_OPEN_REPARSE_POINT, - NULL, - 0); + return NtCreateFile(FileHandle, + DesiredAccess | SYNCHRONIZE, + &ctx->attr, + &ctx->iosb, + AllocationSize, + FileAttributes, + FILE_SHARE_VALID_FLAGS, + CreateDisposition, + CreateOptions | + FILE_OPEN_FOR_BACKUP_INTENT | + FILE_OPEN_REPARSE_POINT, + NULL, + 0); } /* Like do_create_file(), but builds the extraction path of the @dentry first. @@ -1432,33 +1434,31 @@ retry: FILE_BASIC_INFORMATION basic_info = { .FileAttributes = FILE_ATTRIBUTE_NORMAL }; - status = (*func_NtSetInformationFile)(h, &ctx->iosb, - &basic_info, - sizeof(basic_info), - FileBasicInformation); + status = NtSetInformationFile(h, &ctx->iosb, &basic_info, + sizeof(basic_info), + FileBasicInformation); if (!NT_SUCCESS(status)) { winnt_error(status, L"Can't reset attributes of \"%ls\" " "to prepare for deletion", current_path(ctx)); - (*func_NtClose)(h); + NtClose(h); return WIMLIB_ERR_SET_ATTRIBUTES; } FILE_DISPOSITION_INFORMATION disp_info = { .DoDeleteFile = TRUE }; - status = (*func_NtSetInformationFile)(h, &ctx->iosb, - &disp_info, - sizeof(disp_info), - FileDispositionInformation); + status = NtSetInformationFile(h, &ctx->iosb, &disp_info, + sizeof(disp_info), + FileDispositionInformation); if (!NT_SUCCESS(status)) { winnt_error(status, L"Can't set delete-on-close " "disposition on \"%ls\"", current_path(ctx)); - (*func_NtClose)(h); + NtClose(h); return WIMLIB_ERR_SET_ATTRIBUTES; } } - status = (*func_NtClose)(h); + status = NtClose(h); if (unlikely(!NT_SUCCESS(status))) { winnt_error(status, L"Error closing \"%ls\" after setting " "delete-on-close disposition", current_path(ctx)); @@ -1471,10 +1471,11 @@ retry: /* * Create a nondirectory file or named data stream at the current path, * superseding any that already exists at that path. If successful, return an - * open handle to the file or named data stream. + * open handle to the file or named data stream with the requested permissions. */ static int -supersede_file_or_stream(struct win32_apply_ctx *ctx, HANDLE *h_ret) +supersede_file_or_stream(struct win32_apply_ctx *ctx, DWORD perms, + HANDLE *h_ret) { NTSTATUS status; bool retried = false; @@ -1483,7 +1484,7 @@ supersede_file_or_stream(struct win32_apply_ctx *ctx, HANDLE *h_ret) * FILE_ATTRIBUTE_ENCRYPTED doesn't get set before we want it to be. */ retry: status = do_create_file(h_ret, - GENERIC_READ | GENERIC_WRITE | DELETE, + perms, NULL, FILE_ATTRIBUTE_SYSTEM, FILE_CREATE, @@ -1525,11 +1526,9 @@ do_set_reparse_point(const struct wim_dentry *dentry, if (!NT_SUCCESS(status)) goto fail; - status = (*func_NtFsControlFile)(h, NULL, NULL, NULL, - &ctx->iosb, FSCTL_SET_REPARSE_POINT, - (void *)rpbuf, rpbuflen, - NULL, 0); - (*func_NtClose)(h); + status = winnt_fsctl(h, FSCTL_SET_REPARSE_POINT, + rpbuf, rpbuflen, NULL, 0, NULL); + NtClose(h); if (NT_SUCCESS(status)) return 0; @@ -1597,13 +1596,19 @@ create_empty_streams(const struct wim_dentry *dentry, build_extraction_path_with_ads(dentry, ctx, strm->stream_name, utf16le_len_chars(strm->stream_name)); - ret = supersede_file_or_stream(ctx, &h); + /* + * Note: do not request any permissions on the handle. + * Otherwise, we may encounter a Windows bug where the + * parent directory DACL denies read access to the new + * named data stream, even when using backup semantics! + */ + ret = supersede_file_or_stream(ctx, 0, &h); build_extraction_path(dentry, ctx); if (ret) return ret; - (*func_NtClose)(h); + NtClose(h); } } @@ -1648,8 +1653,24 @@ retry: goto retry; } } - winnt_error(status, L"Can't create directory \"%ls\"", - current_path(ctx)); + const wchar_t *path = current_path(ctx); + winnt_error(status, L"Can't create directory \"%ls\"", path); + + /* Check for known issue with WindowsApps directory. */ + if (status == STATUS_ACCESS_DENIED && + (wcsstr(path, L"\\WindowsApps\\") || + wcsstr(path, L"\\InfusedApps\\"))) { + ERROR( +"You seem to be trying to extract files to the WindowsApps directory.\n" +" Windows 8.1 and later use new file permissions in this directory that\n" +" cannot be overridden, even by backup/restore programs. To extract your\n" +" files anyway, you need to choose a different target directory, delete\n" +" the WindowsApps directory entirely, reformat the volume, do the\n" +" extraction from a non-broken operating system such as Windows 7 or\n" +" Linux, or wait for Microsoft to fix the design flaw in their operating\n" +" system. This is *not* a bug in wimlib. See this thread for more\n" +" information: https://wimlib.net/forums/viewtopic.php?f=1&t=261"); + } return WIMLIB_ERR_MKDIR; } @@ -1665,28 +1686,9 @@ retry: if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)) { FILE_BASIC_INFORMATION basic_info = { .FileAttributes = FILE_ATTRIBUTE_NORMAL }; - (*func_NtSetInformationFile)(h, &ctx->iosb, &basic_info, - sizeof(basic_info), - FileBasicInformation); - } - - /* Also try to remove the directory's DACL. This isn't supposed - * to be necessary because we *always* use backup semantics. - * However, there is a case where NtCreateFile() fails with - * STATUS_ACCESS_DENIED when creating a named data stream that - * was just deleted, using a directory-relative open. I have no - * idea why Windows is broken in this case. */ - if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)) { - static const SECURITY_DESCRIPTOR_RELATIVE desc = { - .Revision = SECURITY_DESCRIPTOR_REVISION1, - .Control = SE_SELF_RELATIVE | SE_DACL_PRESENT, - .Owner = 0, - .Group = 0, - .Sacl = 0, - .Dacl = 0, - }; - (*func_NtSetSecurityObject)(h, DACL_SECURITY_INFORMATION, - (void *)&desc); + NtSetInformationFile(h, &ctx->iosb, &basic_info, + sizeof(basic_info), + FileBasicInformation); } } @@ -1698,7 +1700,7 @@ retry: ret = adjust_compression_attribute(h, dentry, ctx); out: - (*func_NtClose)(h); + NtClose(h); return ret; } @@ -1757,7 +1759,9 @@ create_nondirectory_inode(HANDLE *h_ret, const struct wim_dentry *dentry, build_extraction_path(dentry, ctx); - ret = supersede_file_or_stream(ctx, &h); + ret = supersede_file_or_stream(ctx, + GENERIC_READ | GENERIC_WRITE | DELETE, + &h); if (ret) goto out; @@ -1773,7 +1777,7 @@ create_nondirectory_inode(HANDLE *h_ret, const struct wim_dentry *dentry, return 0; out_close: - (*func_NtClose)(h); + NtClose(h); out: return ret; } @@ -1806,9 +1810,8 @@ create_link(HANDLE h, const struct wim_dentry *dentry, * STATUS_INFO_LENGTH_MISMATCH when FileNameLength * happens to be 2 */ - status = (*func_NtSetInformationFile)(h, &ctx->iosb, - info, bufsize, - FileLinkInformation); + status = NtSetInformationFile(h, &ctx->iosb, info, bufsize, + FileLinkInformation); if (NT_SUCCESS(status)) return 0; winnt_error(status, L"Failed to create link \"%ls\"", @@ -1822,7 +1825,7 @@ create_link(HANDLE h, const struct wim_dentry *dentry, if (ret) return ret; - (*func_NtClose)(h2); + NtClose(h2); return 0; } } @@ -1879,7 +1882,7 @@ create_nondirectory(struct wim_inode *inode, struct win32_apply_ctx *ctx) if (!ret && unlikely(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_WIMBOOT)) ret = set_backed_from_wim(h, inode, ctx); - (*func_NtClose)(h); + NtClose(h); return ret; } @@ -1914,7 +1917,7 @@ static void close_handles(struct win32_apply_ctx *ctx) { for (unsigned i = 0; i < ctx->num_open_handles; i++) - (*func_NtClose)(ctx->open_handles[i]); + NtClose(ctx->open_handles[i]); } /* Prepare to read the next blob, which has size @blob_size, into an in-memory @@ -2016,9 +2019,8 @@ begin_extract_blob_instance(const struct blob_descriptor *blob, /* Allocate space for the data. */ alloc_info.AllocationSize.QuadPart = blob->size; - (*func_NtSetInformationFile)(h, &ctx->iosb, - &alloc_info, sizeof(alloc_info), - FileAllocationInformation); + NtSetInformationFile(h, &ctx->iosb, &alloc_info, sizeof(alloc_info), + FileAllocationInformation); return 0; } @@ -2120,6 +2122,15 @@ try_rpfix(struct reparse_buffer_disk *rpbuf, u16 *rpbuflen_p, ctx->target_ntpath.Buffer[target_ntpath_nchars - 1] == L'\\') target_ntpath_nchars--; + /* Also remove extra slashes from the beginning of 'relpath'. Normally + * this isn't needed, but this is here to make the extra slash(es) added + * by wimlib pre-v1.9.1 get removed automatically. */ + while (relpath_nchars >= 2 && + relpath[0] == L'\\' && relpath[1] == L'\\') { + relpath++; + relpath_nchars--; + } + fixed_subst_name_nchars = target_ntpath_nchars + relpath_nchars; wchar_t fixed_subst_name[fixed_subst_name_nchars]; @@ -2298,10 +2309,10 @@ extract_chunk(const void *chunk, size_t size, void *_ctx) while (bytes_remaining) { ULONG count = min(0xFFFFFFFF, bytes_remaining); - status = (*func_NtWriteFile)(ctx->open_handles[i], - NULL, NULL, NULL, - &ctx->iosb, bufptr, count, - NULL, NULL); + status = NtWriteFile(ctx->open_handles[i], + NULL, NULL, NULL, + &ctx->iosb, bufptr, count, + NULL, NULL); if (!NT_SUCCESS(status)) { winnt_error(status, L"Error writing data to target volume"); return WIMLIB_ERR_WRITE; @@ -2353,7 +2364,6 @@ static NTSTATUS set_system_compression(HANDLE h, int format) { NTSTATUS status; - IO_STATUS_BLOCK iosb; struct { struct wof_external_info wof_info; struct file_provider_external_info file_info; @@ -2374,9 +2384,8 @@ set_system_compression(HANDLE h, int format) * versions of Windows (before Windows 10?). This can be a problem if * the WOFADK driver is being used rather than the regular WOF, since * WOFADK can be used on older versions of Windows. */ - status = (*func_NtFsControlFile)(h, NULL, NULL, NULL, &iosb, - FSCTL_SET_EXTERNAL_BACKING, - &in, sizeof(in), NULL, 0); + status = winnt_fsctl(h, FSCTL_SET_EXTERNAL_BACKING, + &in, sizeof(in), NULL, 0, NULL); if (status == 0xC000046F) /* "Compressing this object would not save space." */ return STATUS_SUCCESS; @@ -2417,7 +2426,7 @@ static wchar_t *bootloader_pattern_strings[] = { L"\\Windows\\System32\\CodeIntegrity\\driver.stl", }; -static const struct string_set bootloader_patterns = { +static const struct string_list bootloader_patterns = { .strings = bootloader_pattern_strings, .num_strings = ARRAY_LEN(bootloader_pattern_strings), }; @@ -2509,7 +2518,7 @@ retry: } } - (*func_NtClose)(h); + NtClose(h); return status; } @@ -2647,6 +2656,46 @@ end_extract_blob(struct blob_descriptor *blob, int status, void *_ctx) FILE_ATTRIBUTE_SPARSE_FILE | \ FILE_ATTRIBUTE_COMPRESSED) +static void +set_object_id(HANDLE h, const struct wim_inode *inode, + struct win32_apply_ctx *ctx) +{ + const void *object_id; + u32 len; + NTSTATUS status; + + if (!ctx->common.supported_features.object_ids) + return; + + object_id = inode_get_object_id(inode, &len); + if (likely(object_id == NULL)) /* No object ID? */ + return; + + status = winnt_fsctl(h, FSCTL_SET_OBJECT_ID, + object_id, len, NULL, 0, NULL); + if (NT_SUCCESS(status)) + return; + + /* Object IDs must be unique within the filesystem. A duplicate might + * occur if an image containing object IDs is applied twice to the same + * filesystem. Arguably, the user should be warned in this case; but + * the reality seems to be that nothing important cares about object IDs + * except the Distributed Link Tracking Service... so for now these + * failures are just ignored. */ + if (status == STATUS_DUPLICATE_NAME || + status == STATUS_OBJECT_NAME_COLLISION) + return; + + ctx->num_object_id_failures++; + if (ctx->num_object_id_failures < 10) { + winnt_warning(status, L"Can't set object ID on \"%ls\"", + current_path(ctx)); + } else if (ctx->num_object_id_failures == 10) { + WARNING("Suppressing further warnings about failure to set " + "object IDs."); + } +} + /* Set the security descriptor @desc, of @desc_size bytes, on the file with open * handle @h. */ static NTSTATUS @@ -2740,7 +2789,7 @@ set_security_descriptor(HANDLE h, const void *_desc, */ retry: - status = (*func_NtSetSecurityObject)(h, info, desc); + status = NtSetSecurityObject(h, info, desc); if (NT_SUCCESS(status)) goto out_maybe_free_desc; @@ -2791,7 +2840,12 @@ do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode, FILE_BASIC_INFORMATION info; NTSTATUS status; - /* Set security descriptor if present and not in NO_ACLS mode */ + /* Set the file's object ID if present and object IDs are supported by + * the filesystem. */ + set_object_id(h, inode, ctx); + + /* Set the file's security descriptor if present and we're not in + * NO_ACLS mode */ if (inode_has_security_descriptor(inode) && !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)) { @@ -2827,9 +2881,8 @@ do_apply_metadata_to_file(HANDLE h, const struct wim_inode *inode, info.FileAttributes = FILE_ATTRIBUTE_NORMAL; } - status = (*func_NtSetInformationFile)(h, &ctx->iosb, - &info, sizeof(info), - FileBasicInformation); + status = NtSetInformationFile(h, &ctx->iosb, &info, sizeof(info), + FileBasicInformation); /* On FAT volumes we get STATUS_INVALID_PARAMETER if we try to set * attributes on the root directory. (Apparently because FAT doesn't * actually have a place to store those attributes!) */ @@ -2887,7 +2940,7 @@ apply_metadata_to_file(const struct wim_dentry *dentry, ret = do_apply_metadata_to_file(h, inode, ctx); - (*func_NtClose)(h); + NtClose(h); return ret; }