From: Eric Biggers Date: Thu, 18 Feb 2016 02:21:38 +0000 (-0600) Subject: win32_common: add winnt_fsctl() helper method X-Git-Tag: v1.9.1~29 X-Git-Url: https://wimlib.net/git/?p=wimlib;a=commitdiff_plain;h=686c407415c8e6ec022bd64678db65004631b194 win32_common: add winnt_fsctl() helper method This is needed to handle STATUS_PENDING. --- diff --git a/include/wimlib/win32_common.h b/include/wimlib/win32_common.h index d3649ace..e669d522 100644 --- a/include/wimlib/win32_common.h +++ b/include/wimlib/win32_common.h @@ -101,17 +101,6 @@ extern NTSTATUS (WINAPI *func_NtSetSecurityObject)(HANDLE Handle, SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR SecurityDescriptor); -extern NTSTATUS (WINAPI *func_NtFsControlFile) (HANDLE FileHandle, - HANDLE Event, - PIO_APC_ROUTINE ApcRoutine, - PVOID ApcContext, - PIO_STATUS_BLOCK IoStatusBlock, - ULONG FsControlCode, - PVOID InputBuffer, - ULONG InputBufferLength, - PVOID OutputBuffer, - ULONG OutputBufferLength); - extern NTSTATUS (WINAPI *func_NtClose) (HANDLE Handle); extern DWORD (WINAPI *func_RtlNtStatusToDosError)(NTSTATUS status); @@ -174,4 +163,8 @@ winnt_warning(NTSTATUS status, const wchar_t *format, ...) _cold_attribute; extern void winnt_error(NTSTATUS status, const wchar_t *format, ...) _cold_attribute; +extern NTSTATUS +winnt_fsctl(HANDLE h, u32 code, const void *in, u32 in_size, + void *out, u32 out_size_avail, u32 *actual_out_size_ret); + #endif /* _WIMLIB_WIN32_COMMON_H */ diff --git a/src/win32_apply.c b/src/win32_apply.c index 395e6984..afa11ac7 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.c @@ -1092,6 +1092,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,10 +1102,6 @@ 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, @@ -1122,14 +1121,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)) - return 0; + status = winnt_fsctl(h, FSCTL_SET_COMPRESSION, + &compression_state, sizeof(USHORT), NULL, 0, NULL); + if (NT_SUCCESS(status)) + return status; - 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; } @@ -1347,7 +1344,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 @@ -1360,7 +1357,7 @@ do_create_file(PHANDLE FileHandle, struct win32_apply_ctx *ctx) { return (*func_NtCreateFile)(FileHandle, - DesiredAccess, + DesiredAccess | SYNCHRONIZE, &ctx->attr, &ctx->iosb, AllocationSize, @@ -1525,10 +1522,8 @@ 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); + status = winnt_fsctl(h, FSCTL_SET_REPARSE_POINT, + rpbuf, rpbuflen, NULL, 0, NULL); (*func_NtClose)(h); if (NT_SUCCESS(status)) @@ -2378,7 +2373,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; @@ -2399,9 +2393,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; diff --git a/src/win32_capture.c b/src/win32_capture.c index c9e8ba1b..951f61bf 100644 --- a/src/win32_capture.c +++ b/src/win32_capture.c @@ -243,8 +243,7 @@ get_windows_file_path(const struct windows_file *file) * Open the file named by the NT namespace path @path of length @path_nchars * characters. If @cur_dir is not NULL then the path is given relative to * @cur_dir; otherwise the path is absolute. @perms is the access mask of - * permissions to request on the handle. If permission to read the data is - * requested, then SYNCHRONIZE is automatically added. + * permissions to request on the handle. SYNCHRONIZE permision is always added. */ static NTSTATUS winnt_openat(HANDLE cur_dir, const wchar_t *path, size_t path_nchars, @@ -264,8 +263,8 @@ winnt_openat(HANDLE cur_dir, const wchar_t *path, size_t path_nchars, NTSTATUS status; ULONG options = FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT; + perms |= SYNCHRONIZE; if (perms & (FILE_READ_DATA | FILE_LIST_DIRECTORY)) { - perms |= SYNCHRONIZE; options |= FILE_SYNCHRONOUS_IO_NONALERT; options |= FILE_SEQUENTIAL_ONLY; } @@ -978,7 +977,8 @@ winnt_load_reparse_data(HANDLE h, struct wim_inode *inode, const wchar_t *full_path, struct capture_params *params) { struct reparse_buffer_disk rpbuf; - DWORD bytes_returned; + NTSTATUS status; + u32 len; u16 rpbuflen; int ret; @@ -989,16 +989,15 @@ winnt_load_reparse_data(HANDLE h, struct wim_inode *inode, return 0; } - if (!DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, - NULL, 0, &rpbuf, REPARSE_POINT_MAX_SIZE, - &bytes_returned, NULL)) - { - win32_error(GetLastError(), L"\"%ls\": Can't get reparse point", + status = winnt_fsctl(h, FSCTL_GET_REPARSE_POINT, + NULL, 0, &rpbuf, sizeof(rpbuf), &len); + if (!NT_SUCCESS(status)) { + winnt_error(status, L"\"%ls\": Can't get reparse point", printable_path(full_path)); return WIMLIB_ERR_READLINK; } - rpbuflen = bytes_returned; + rpbuflen = len; if (unlikely(rpbuflen < REPARSE_DATA_OFFSET)) { ERROR("\"%ls\": reparse point buffer is too short", @@ -1295,12 +1294,9 @@ get_sort_key(HANDLE h) { STARTING_VCN_INPUT_BUFFER in = { .StartingVcn.QuadPart = 0 }; RETRIEVAL_POINTERS_BUFFER out; - DWORD bytesReturned; - if (!DeviceIoControl(h, FSCTL_GET_RETRIEVAL_POINTERS, - &in, sizeof(in), - &out, sizeof(out), - &bytesReturned, NULL)) + if (!NT_SUCCESS(winnt_fsctl(h, FSCTL_GET_RETRIEVAL_POINTERS, + &in, sizeof(in), &out, sizeof(out), NULL))) return 0; return extract_starting_lcn(&out); @@ -1396,14 +1392,12 @@ try_to_use_wimboot_hash(HANDLE h, struct wim_inode *inode, struct wof_external_info wof_info; struct wim_provider_external_info wim_info; } out; - IO_STATUS_BLOCK iosb; NTSTATUS status; /* WOF may be attached. Try reading this file's external * backing info. */ - status = (*func_NtFsControlFile)(h, NULL, NULL, NULL, &iosb, - FSCTL_GET_EXTERNAL_BACKING, - NULL, 0, &out, sizeof(out)); + status = winnt_fsctl(h, FSCTL_GET_EXTERNAL_BACKING, + NULL, 0, &out, sizeof(out), NULL); /* Is WOF not attached? */ if (status == STATUS_INVALID_DEVICE_REQUEST || @@ -2349,9 +2343,7 @@ load_files_from_mft(const wchar_t *path, struct ntfs_inode_map *inode_map) }; const size_t outsize = 32768; QUERY_FILE_LAYOUT_OUTPUT *out = NULL; - DWORD bytes_returned; int ret; - DWORD err; NTSTATUS status; status = winnt_open(path, wcslen(path), @@ -2367,8 +2359,9 @@ load_files_from_mft(const wchar_t *path, struct ntfs_inode_map *inode_map) goto out; } - while (DeviceIoControl(h, FSCTL_QUERY_FILE_LAYOUT, &in, sizeof(in), - out, outsize, &bytes_returned, NULL)) + while (NT_SUCCESS(status = winnt_fsctl(h, FSCTL_QUERY_FILE_LAYOUT, + &in, sizeof(in), + out, outsize, NULL))) { const FILE_LAYOUT_ENTRY *file = (const void *)out + out->FirstFileOffset; @@ -2383,16 +2376,15 @@ load_files_from_mft(const wchar_t *path, struct ntfs_inode_map *inode_map) in.Flags &= ~QUERY_FILE_LAYOUT_RESTART; } - /* Normally, FSCTL_QUERY_FILE_LAYOUT fails with error code 38 after all - * files have been enumerated. */ - err = GetLastError(); - if (err != 38) { - if (err == ERROR_INVALID_FUNCTION || - err == ERROR_INVALID_PARAMETER) { + /* Normally, FSCTL_QUERY_FILE_LAYOUT fails with STATUS_END_OF_FILE after + * all files have been enumerated. */ + if (status != STATUS_END_OF_FILE) { + if (status == STATUS_INVALID_DEVICE_REQUEST /* old OS */ || + status == STATUS_INVALID_PARAMETER /* not root directory */ ) { /* Silently try standard recursive scan instead */ ret = -1; } else { - win32_error(err, + winnt_error(status, L"Error enumerating files on volume \"%ls\"", path); /* Try standard recursive scan instead */ diff --git a/src/win32_common.c b/src/win32_common.c index b8ac2197..628f1f0b 100644 --- a/src/win32_common.c +++ b/src/win32_common.c @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2013, 2014, 2015 Eric Biggers + * Copyright (C) 2013-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free @@ -27,6 +27,7 @@ #include "wimlib/win32_common.h" +#include "wimlib/assert.h" #include "wimlib/error.h" #include "wimlib/util.h" #include "wimlib/win32_vss.h" @@ -170,16 +171,20 @@ NTSTATUS (WINAPI *func_NtSetSecurityObject)(HANDLE Handle, SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR SecurityDescriptor); -NTSTATUS (WINAPI *func_NtFsControlFile) (HANDLE FileHandle, - HANDLE Event, - PIO_APC_ROUTINE ApcRoutine, - PVOID ApcContext, - PIO_STATUS_BLOCK IoStatusBlock, - ULONG FsControlCode, - PVOID InputBuffer, - ULONG InputBufferLength, - PVOID OutputBuffer, - ULONG OutputBufferLength); +static NTSTATUS (WINAPI *func_NtFsControlFile) (HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG FsControlCode, + PVOID InputBuffer, + ULONG InputBufferLength, + PVOID OutputBuffer, + ULONG OutputBufferLength); + +static NTSTATUS (WINAPI *func_NtWaitForSingleObject) (HANDLE Handle, + BOOLEAN Alertable, + PLARGE_INTEGER Timeout); NTSTATUS (WINAPI *func_NtClose) (HANDLE Handle); @@ -233,6 +238,7 @@ struct dll_spec ntdll_spec = { DLL_SYM(NtSetInformationFile, true), DLL_SYM(NtSetSecurityObject, true), DLL_SYM(NtFsControlFile, true), + DLL_SYM(NtWaitForSingleObject, true), DLL_SYM(NtClose, true), DLL_SYM(RtlNtStatusToDosError, true), DLL_SYM(RtlCreateSystemVolumeInformationFolder, false), @@ -531,4 +537,40 @@ winnt_error(NTSTATUS status, const wchar_t *format, ...) va_end(va); } +/* + * Synchronously execute a filesystem control method. This is a wrapper around + * NtFsControlFile() that handles STATUS_PENDING. Note that SYNCHRONIZE + * permission is, in general, required on the handle. + */ +NTSTATUS +winnt_fsctl(HANDLE h, u32 code, const void *in, u32 in_size, + void *out, u32 out_size_avail, u32 *actual_out_size_ret) +{ + IO_STATUS_BLOCK iosb; + NTSTATUS status; + + status = (*func_NtFsControlFile)(h, NULL, NULL, NULL, &iosb, code, + (void *)in, in_size, + out, out_size_avail); + if (status == STATUS_PENDING) { + /* Beware: this case is often encountered with remote + * filesystems, but rarely with local filesystems. */ + + status = (*func_NtWaitForSingleObject)(h, FALSE, NULL); + if (NT_SUCCESS(status)) { + status = iosb.Status; + } else { + /* We shouldn't be issuing ioctls on a handle to which + * we don't have SYNCHRONIZE access. Otherwise we have + * no way to wait for them to complete. */ + wimlib_assert(status != STATUS_ACCESS_DENIED); + } + } + + if (NT_SUCCESS(status) && actual_out_size_ret != NULL) + *actual_out_size_ret = (u32)iosb.Information; + + return status; +} + #endif /* __WIN32__ */