From 820eedad9afceb737be2c07e1af49ca203d678f7 Mon Sep 17 00:00:00 2001 From: Mattiwatti Date: Mon, 3 Feb 2020 21:23:06 +0100 Subject: [PATCH] [iso] call CreatePreallocatedFileU to create files with known sizes * Implement CreatePreallocatedFileU which uses NtCreateFile to create files with preallocated sizes. This is used during ISO extraction to improve performance * Minor changes to process.c/.h to prevent macro/typedef conflicts * Remove now-unused preallocate_filesize which was called after CreateFileU --- src/iso.c | 19 ++---- src/msapi_utf8.h | 152 +++++++++++++++++++++++++++++++++++++++++++++++ src/process.c | 1 - src/process.h | 3 - src/rufus.h | 2 + 5 files changed, 158 insertions(+), 19 deletions(-) diff --git a/src/iso.c b/src/iso.c index 32e3af51..70afdf04 100644 --- a/src/iso.c +++ b/src/iso.c @@ -419,15 +419,6 @@ static void __inline set_directory_timestamp(char* path, LPFILETIME creation, LP safe_closehandle(dir_handle); } -// Preallocates the target size of a newly created file in order to prevent fragmentation from repeated writes -static void __inline preallocate_filesize(HANDLE hFile, int64_t file_length) -{ - SetFileInformationByHandle(hFile, FileEndOfFileInfo, &file_length, sizeof(file_length)); - - // FileAllocationInfo does not require the size to be a multiple of the cluster size; the FS driver takes care of this. - SetFileInformationByHandle(hFile, FileAllocationInfo, &file_length, sizeof(file_length)); -} - // Returns 0 on success, nonzero on error static int udf_extract_files(udf_t *p_udf, udf_dirent_t *p_udf_dirent, const char *psz_path) { @@ -500,8 +491,8 @@ static int udf_extract_files(udf_t *p_udf, udf_dirent_t *p_udf_dirent, const cha psz_sanpath = sanitize_filename(psz_fullpath, &is_identical); if (!is_identical) uprintf(" File name sanitized to '%s'", psz_sanpath); - file_handle = CreateFileU(psz_sanpath, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + file_handle = CreatePreallocatedFileU(psz_sanpath, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, file_length); if (file_handle == INVALID_HANDLE_VALUE) { err = GetLastError(); uprintf(" Unable to create file: %s", WindowsErrorString()); @@ -510,7 +501,6 @@ static int udf_extract_files(udf_t *p_udf, udf_dirent_t *p_udf_dirent, const cha else goto out; } else { - preallocate_filesize(file_handle, file_length); while (file_length > 0) { if (FormatStatus) goto out; memset(buf, 0, UDF_BLOCKSIZE); @@ -652,8 +642,8 @@ static int iso_extract_files(iso9660_t* p_iso, const char *psz_path) uprintf(" Ignoring Rock Ridge symbolic link to '%s'", p_statbuf->rr.psz_symlink); safe_free(p_statbuf->rr.psz_symlink); } - file_handle = CreateFileU(psz_sanpath, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + file_handle = CreatePreallocatedFileU(psz_sanpath, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, file_length); if (file_handle == INVALID_HANDLE_VALUE) { err = GetLastError(); uprintf(" Unable to create file: %s", WindowsErrorString()); @@ -662,7 +652,6 @@ static int iso_extract_files(iso9660_t* p_iso, const char *psz_path) else goto out; } else { - preallocate_filesize(file_handle, file_length); for (j=0; jextents; j++) { extent_length = p_statbuf->extsize[j]; for (i=0; extent_length>0; i++) { diff --git a/src/msapi_utf8.h b/src/msapi_utf8.h index 3ff15d95..1110aa6d 100644 --- a/src/msapi_utf8.h +++ b/src/msapi_utf8.h @@ -19,7 +19,9 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "rufus.h" #include +#include #include #include #include @@ -76,6 +78,17 @@ extern "C" { #define walloc(p, size) wchar_t* w ## p = (p == NULL)?NULL:(wchar_t*)calloc(size, sizeof(wchar_t)) #define wfree(p) sfree(w ## p) +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +#define STATUS_NOT_IMPLEMENTED ((NTSTATUS)0xC0000002L) +#define FILE_ATTRIBUTE_VALID_FLAGS 0x00007fb7 +#define NtCurrentPeb() (NtCurrentTeb()->ProcessEnvironmentBlock) +#define RtlGetProcessHeap() (NtCurrentPeb()->Reserved4[1]) // NtCurrentPeb()->ProcessHeap, mangled due to deficiencies in winternl.h + +PF_TYPE_DECL(NTAPI, NTSTATUS, NtCreateFile, (PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, PLARGE_INTEGER, ULONG, ULONG, ULONG, ULONG, PVOID, ULONG)); +PF_TYPE_DECL(NTAPI, BOOLEAN, RtlDosPathNameToNtPathName_U, (PCWSTR, PUNICODE_STRING, PWSTR*, PVOID)); +PF_TYPE_DECL(NTAPI, BOOLEAN, RtlFreeHeap, (PVOID, ULONG, PVOID)); +PF_TYPE_DECL(NTAPI, VOID, RtlSetLastWin32ErrorAndNtStatusFromNtStatus, (NTSTATUS)); + /* * Converts an UTF-16 string to UTF8 (allocate returned string) * Returns NULL on error @@ -477,6 +490,145 @@ static __inline HANDLE CreateFileU(const char* lpFileName, DWORD dwDesiredAccess return ret; } +static __inline HANDLE CreatePreallocatedFileU(const char* lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, + DWORD dwFlagsAndAttributes, LONGLONG fileSize) +{ + HANDLE fileHandle = INVALID_HANDLE_VALUE; + OBJECT_ATTRIBUTES objectAttributes; + IO_STATUS_BLOCK ioStatusBlock; + UNICODE_STRING ntPath; + ULONG fileAttributes, flags = 0; + LARGE_INTEGER allocationSize; + NTSTATUS status = STATUS_SUCCESS; + + PF_INIT_OR_SET_STATUS(NtCreateFile, Ntdll); + PF_INIT_OR_SET_STATUS(RtlDosPathNameToNtPathName_U, Ntdll); + PF_INIT_OR_SET_STATUS(RtlFreeHeap, Ntdll); + PF_INIT_OR_SET_STATUS(RtlSetLastWin32ErrorAndNtStatusFromNtStatus, Ntdll); + + if (!NT_SUCCESS(status)) + { + return CreateFileU(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, + dwCreationDisposition, dwFlagsAndAttributes, NULL); + } + + wconvert(lpFileName); + + // Determine creation disposition and flags + switch (dwCreationDisposition) + { + case CREATE_NEW: + dwCreationDisposition = FILE_CREATE; + break; + case CREATE_ALWAYS: + dwCreationDisposition = FILE_OVERWRITE_IF; + break; + case OPEN_EXISTING: + dwCreationDisposition = FILE_OPEN; + break; + case OPEN_ALWAYS: + dwCreationDisposition = FILE_OPEN_IF; + break; + case TRUNCATE_EXISTING: + dwCreationDisposition = FILE_OVERWRITE; + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; + } + + if ((dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED) == 0) + flags |= FILE_SYNCHRONOUS_IO_NONALERT; + + if ((dwFlagsAndAttributes & FILE_FLAG_WRITE_THROUGH) != 0) + flags |= FILE_WRITE_THROUGH; + + if ((dwFlagsAndAttributes & FILE_FLAG_NO_BUFFERING) != 0) + flags |= FILE_NO_INTERMEDIATE_BUFFERING; + + if ((dwFlagsAndAttributes & FILE_FLAG_RANDOM_ACCESS) != 0) + flags |= FILE_RANDOM_ACCESS; + + if ((dwFlagsAndAttributes & FILE_FLAG_SEQUENTIAL_SCAN) != 0) + flags |= FILE_SEQUENTIAL_ONLY; + + if ((dwFlagsAndAttributes & FILE_FLAG_DELETE_ON_CLOSE) != 0) + { + flags |= FILE_DELETE_ON_CLOSE; + dwDesiredAccess |= DELETE; + } + + if ((dwFlagsAndAttributes & FILE_FLAG_BACKUP_SEMANTICS) != 0) + { + if ((dwDesiredAccess & GENERIC_ALL) != 0) + flags |= (FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REMOTE_INSTANCE); + else + { + if ((dwDesiredAccess & GENERIC_READ) != 0) + flags |= FILE_OPEN_FOR_BACKUP_INTENT; + + if ((dwDesiredAccess & GENERIC_WRITE) != 0) + flags |= FILE_OPEN_REMOTE_INSTANCE; + } + } + else + { + flags |= FILE_NON_DIRECTORY_FILE; + } + + if ((dwFlagsAndAttributes & FILE_FLAG_OPEN_REPARSE_POINT) != 0) + flags |= FILE_OPEN_REPARSE_POINT; + + if ((dwFlagsAndAttributes & FILE_FLAG_OPEN_NO_RECALL) != 0) + flags |= FILE_OPEN_NO_RECALL; + + fileAttributes = dwFlagsAndAttributes & (FILE_ATTRIBUTE_VALID_FLAGS & ~FILE_ATTRIBUTE_DIRECTORY); + + dwDesiredAccess |= (SYNCHRONIZE | FILE_READ_ATTRIBUTES); + + // Convert DOS path to NT format + if (!pfRtlDosPathNameToNtPathName_U(wlpFileName, &ntPath, NULL, NULL)) + { + wfree(lpFileName); + SetLastError(ERROR_FILE_NOT_FOUND); + return INVALID_HANDLE_VALUE; + } + + InitializeObjectAttributes(&objectAttributes, &ntPath, 0, NULL, NULL); + + if (lpSecurityAttributes != NULL) + { + if (lpSecurityAttributes->bInheritHandle) + objectAttributes.Attributes |= OBJ_INHERIT; + objectAttributes.SecurityDescriptor = lpSecurityAttributes->lpSecurityDescriptor; + } + + if ((dwFlagsAndAttributes & FILE_FLAG_POSIX_SEMANTICS) == 0) + objectAttributes.Attributes |= OBJ_CASE_INSENSITIVE; + + allocationSize.QuadPart = fileSize; + + // Call NtCreateFile + status = pfNtCreateFile(&fileHandle, + dwDesiredAccess, + &objectAttributes, + &ioStatusBlock, + &allocationSize, + fileAttributes, + dwShareMode, + dwCreationDisposition, + flags, + NULL, + 0); + + pfRtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer); + wfree(lpFileName); + pfRtlSetLastWin32ErrorAndNtStatusFromNtStatus(status); + + return fileHandle; +} + static __inline BOOL CreateDirectoryU(const char* lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes) { BOOL ret = FALSE; diff --git a/src/process.c b/src/process.c index f5090965..b0efbb0a 100644 --- a/src/process.c +++ b/src/process.c @@ -38,7 +38,6 @@ PF_TYPE_DECL(NTAPI, PVOID, RtlCreateHeap, (ULONG, PVOID, SIZE_T, SIZE_T, PVOID, PRTL_HEAP_PARAMETERS)); PF_TYPE_DECL(NTAPI, PVOID, RtlDestroyHeap, (PVOID)); PF_TYPE_DECL(NTAPI, PVOID, RtlAllocateHeap, (PVOID, ULONG, SIZE_T)); -PF_TYPE_DECL(NTAPI, BOOLEAN, RtlFreeHeap, (PVOID, ULONG, PVOID)); PF_TYPE_DECL(NTAPI, NTSTATUS, NtQuerySystemInformation, (SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG)); PF_TYPE_DECL(NTAPI, NTSTATUS, NtQueryInformationFile, (HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS)); diff --git a/src/process.h b/src/process.h index fd506fae..5ff0210d 100644 --- a/src/process.h +++ b/src/process.h @@ -294,6 +294,3 @@ typedef struct _RTL_HEAP_PARAMETERS #define SE_TIME_ZONE_PRIVILEGE (34L) #define SE_CREATE_SYMBOLIC_LINK_PRIVILEGE (35L) #define SE_MAX_WELL_KNOWN_PRIVILEGE SE_CREATE_SYMBOLIC_LINK_PRIVILEGE - -#define PF_INIT_OR_SET_STATUS(proc, name) do {PF_INIT(proc, name); \ - if ((pf##proc == NULL) && (NT_SUCCESS(status))) status = STATUS_NOT_IMPLEMENTED; } while(0) diff --git a/src/rufus.h b/src/rufus.h index 0a5280c3..82c4fc1b 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -646,6 +646,8 @@ static __inline HMODULE GetLibraryHandle(char* szLibraryName) { #define PF_INIT_OR_OUT(proc, name) do {PF_INIT(proc, name); \ if (pf##proc == NULL) {uprintf("Unable to locate %s() in %s.dll: %s\n", \ #proc, #name, WindowsErrorString()); goto out;} } while(0) +#define PF_INIT_OR_SET_STATUS(proc, name) do {PF_INIT(proc, name); \ + if ((pf##proc == NULL) && (NT_SUCCESS(status))) status = STATUS_NOT_IMPLEMENTED; } while(0) /* Custom application errors */ #define FAC(f) (f<<16)