This is needed to handle STATUS_PENDING.
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);
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 */
{
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;
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,
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;
}
* 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
struct win32_apply_ctx *ctx)
{
return (*func_NtCreateFile)(FileHandle,
- DesiredAccess,
+ DesiredAccess | SYNCHRONIZE,
&ctx->attr,
&ctx->iosb,
AllocationSize,
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))
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;
* 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;
* 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,
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;
}
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;
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",
{
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);
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 ||
};
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),
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;
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 */
*/
/*
- * 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
#include "wimlib/win32_common.h"
+#include "wimlib/assert.h"
#include "wimlib/error.h"
#include "wimlib/util.h"
#include "wimlib/win32_vss.h"
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);
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),
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__ */