From 4c5adf092e408292a5467fa98e06418f6b8f513d Mon Sep 17 00:00:00 2001 From: Mattiwatti Date: Mon, 3 Feb 2020 21:23:06 +0100 Subject: [PATCH] [iso] use NtCreateFile() to create files with preallocated sizes * Implement CreatePreallocatedFile() which uses NtCreateFile() to create files with preallocated sizes. This is used during ISO extraction to improve performance. * Remove now-unused preallocate_filesize which was called after CreateFileU(). * Closes #1445 --- src/iso.c | 19 ++----- src/process.h | 3 -- src/rufus.h | 5 ++ src/rufus.rc | 10 ++-- src/stdio.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 148 insertions(+), 23 deletions(-) diff --git a/src/iso.c b/src/iso.c index 0f9b440ba7e..5d1dc0b5a39 100644 --- a/src/iso.c +++ b/src/iso.c @@ -422,15 +422,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) { @@ -505,8 +496,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 = CreatePreallocatedFile(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()); @@ -515,7 +506,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); @@ -657,8 +647,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 = CreatePreallocatedFile(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()); @@ -667,7 +657,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/process.h b/src/process.h index fd506faef6f..5ff0210d631 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 722adc72b85..98be9361628 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -577,6 +577,9 @@ extern BOOL EnablePrivileges(void); extern void FlashTaskbar(HANDLE handle); extern DWORD WaitForSingleObjectWithMessages(HANDLE hHandle, DWORD dwMilliseconds); extern HICON CreateMirroredIcon(HICON hiconOrg); +extern HANDLE CreatePreallocatedFile(const char* lpFileName, DWORD dwDesiredAccess, + DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, + DWORD dwFlagsAndAttributes, LONGLONG fileSize); #define GetTextWidth(hDlg, id) GetTextSize(GetDlgItem(hDlg, id), NULL).cx DWORD WINAPI FormatThread(void* param); @@ -649,6 +652,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) diff --git a/src/rufus.rc b/src/rufus.rc index b0b17d38264..40617e44a98 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 232, 326 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_ACCEPTFILES -CAPTION "Rufus 3.9.1609" +CAPTION "Rufus 3.9.1610" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -394,8 +394,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,9,1609,0 - PRODUCTVERSION 3,9,1609,0 + FILEVERSION 3,9,1610,0 + PRODUCTVERSION 3,9,1610,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -413,13 +413,13 @@ BEGIN VALUE "Comments", "https://rufus.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "3.9.1609" + VALUE "FileVersion", "3.9.1610" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2020 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" VALUE "OriginalFilename", "rufus-3.9.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "3.9.1609" + VALUE "ProductVersion", "3.9.1610" END END BLOCK "VarFileInfo" diff --git a/src/stdio.c b/src/stdio.c index 13418cfd6cb..fcac09e5627 100644 --- a/src/stdio.c +++ b/src/stdio.c @@ -1,6 +1,7 @@ /* * Rufus: The Reliable USB Formatting Utility * Standard User I/O Routines (logging, status, error, etc.) + * Copyright © 2020 Mattiwatti * Copyright © 2011-2019 Pete Batard * * This program is free software: you can redistribute it and/or modify @@ -26,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -869,3 +871,135 @@ DWORD WaitForSingleObjectWithMessages(HANDLE hHandle, DWORD dwMilliseconds) return res; } + +#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)); + +HANDLE CreatePreallocatedFile(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; +}