|
1 | | -#include "common/io.h" |
2 | 1 | #include "fastfetch.h" |
| 2 | +#include "common/io.h" |
3 | 3 | #include "common/stringUtils.h" |
| 4 | +#include "common/windows/nt.h" |
4 | 5 |
|
5 | 6 | #include <windows.h> |
6 | | -#include "common/windows/nt.h" |
7 | | -#include <ntstatus.h> |
8 | 7 |
|
9 | | -static void createSubfolders(const char* fileName) |
| 8 | +static bool createSubfolders(wchar_t* fileName) |
10 | 9 | { |
11 | | - FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); |
12 | | - char *token = NULL; |
13 | | - while((token = strchr(fileName, '/')) != NULL) |
| 10 | + HANDLE hRoot = ffGetProcessParams()->CurrentDirectory.Handle; |
| 11 | + bool closeRoot = false; |
| 12 | + wchar_t* ptr = fileName; |
| 13 | + |
| 14 | + // Absolute drive path: C:\... |
| 15 | + if (ffCharIsEnglishAlphabet((char)ptr[0]) && ptr[1] == L':' && ptr[2] == L'\\') |
14 | 16 | { |
15 | | - ffStrbufAppendNS(&path, (uint32_t)(token - fileName + 1), fileName); |
16 | | - CreateDirectoryA(path.chars, NULL); |
17 | | - fileName = token + 1; |
| 17 | + wchar_t saved = ptr[3]; |
| 18 | + ptr[3] = L'\0'; |
| 19 | + |
| 20 | + hRoot = CreateFileW( |
| 21 | + fileName, |
| 22 | + FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE, |
| 23 | + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
| 24 | + NULL, |
| 25 | + OPEN_EXISTING, |
| 26 | + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_FLAG_BACKUP_SEMANTICS, |
| 27 | + NULL |
| 28 | + ); |
| 29 | + |
| 30 | + ptr[3] = saved; |
| 31 | + if (hRoot == INVALID_HANDLE_VALUE) |
| 32 | + return false; |
| 33 | + |
| 34 | + closeRoot = true; |
| 35 | + ptr += 3; // skip "C:\" |
18 | 36 | } |
| 37 | + // UNC path: \\server\share\... |
| 38 | + else if (ptr[0] == L'\\' && ptr[1] == L'\\') |
| 39 | + { |
| 40 | + wchar_t* serverEnd = wcschr(ptr + 2, L'\\'); |
| 41 | + if (serverEnd == NULL) |
| 42 | + return false; |
| 43 | + |
| 44 | + wchar_t* shareEnd = wcschr(serverEnd + 1, L'\\'); |
| 45 | + if (shareEnd == NULL) |
| 46 | + return true; // no parent subfolder exists before file name |
| 47 | + |
| 48 | + wchar_t saved = *shareEnd; |
| 49 | + *shareEnd = L'\0'; |
| 50 | + |
| 51 | + hRoot = CreateFileW( |
| 52 | + fileName, |
| 53 | + FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE, |
| 54 | + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
| 55 | + NULL, |
| 56 | + OPEN_EXISTING, |
| 57 | + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_FLAG_BACKUP_SEMANTICS, |
| 58 | + NULL |
| 59 | + ); |
| 60 | + |
| 61 | + *shareEnd = saved; |
| 62 | + if (hRoot == INVALID_HANDLE_VALUE) |
| 63 | + return false; |
| 64 | + |
| 65 | + closeRoot = true; |
| 66 | + ptr = shareEnd + 1; // first component under share |
| 67 | + } |
| 68 | + // Rooted path on current drive: \foo\bar |
| 69 | + else if (ptr[0] == L'\\') |
| 70 | + { |
| 71 | + UNICODE_STRING* dosPath = &ffGetProcessParams()->CurrentDirectory.DosPath; |
| 72 | + wchar_t driveRoot[] = { dosPath->Buffer[0], L':', L'\\', L'\0' }; |
| 73 | + hRoot = CreateFileW( |
| 74 | + driveRoot, |
| 75 | + FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE, |
| 76 | + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
| 77 | + NULL, |
| 78 | + OPEN_EXISTING, |
| 79 | + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_FLAG_BACKUP_SEMANTICS, |
| 80 | + NULL |
| 81 | + ); |
| 82 | + if (hRoot == INVALID_HANDLE_VALUE) |
| 83 | + return false; |
| 84 | + closeRoot = true; |
| 85 | + ptr++; // skip leading '\' |
| 86 | + } |
| 87 | + |
| 88 | + while (true) |
| 89 | + { |
| 90 | + wchar_t* token = wcschr(ptr, L'\\'); |
| 91 | + if (token == NULL) |
| 92 | + break; |
| 93 | + |
| 94 | + // Skip empty path segments caused by duplicated '\' |
| 95 | + if (token == ptr) |
| 96 | + { |
| 97 | + ptr = token + 1; |
| 98 | + continue; |
| 99 | + } |
| 100 | + |
| 101 | + HANDLE hNew = INVALID_HANDLE_VALUE; |
| 102 | + IO_STATUS_BLOCK iosb = {}; |
| 103 | + |
| 104 | + NTSTATUS status = NtCreateFile( |
| 105 | + &hNew, |
| 106 | + FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE, |
| 107 | + &(OBJECT_ATTRIBUTES) { |
| 108 | + .Length = sizeof(OBJECT_ATTRIBUTES), |
| 109 | + .RootDirectory = hRoot, |
| 110 | + .ObjectName = &(UNICODE_STRING) { |
| 111 | + .Buffer = ptr, |
| 112 | + .Length = (USHORT)((USHORT)(token - ptr) * sizeof(wchar_t)), |
| 113 | + .MaximumLength = (USHORT)((USHORT)(token - ptr) * sizeof(wchar_t)), |
| 114 | + }, |
| 115 | + .Attributes = OBJ_CASE_INSENSITIVE, |
| 116 | + }, |
| 117 | + &iosb, |
| 118 | + NULL, |
| 119 | + FILE_ATTRIBUTE_NORMAL, |
| 120 | + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
| 121 | + FILE_OPEN_IF, |
| 122 | + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, |
| 123 | + NULL, |
| 124 | + 0 |
| 125 | + ); |
| 126 | + |
| 127 | + if (!NT_SUCCESS(status)) |
| 128 | + { |
| 129 | + if (closeRoot && hRoot != INVALID_HANDLE_VALUE) |
| 130 | + CloseHandle(hRoot); |
| 131 | + return false; |
| 132 | + } |
| 133 | + |
| 134 | + if (closeRoot && hRoot != INVALID_HANDLE_VALUE) |
| 135 | + CloseHandle(hRoot); |
| 136 | + hRoot = hNew; |
| 137 | + closeRoot = true; |
| 138 | + |
| 139 | + ptr = token + 1; |
| 140 | + } |
| 141 | + |
| 142 | + if (closeRoot && hRoot != INVALID_HANDLE_VALUE) |
| 143 | + CloseHandle(hRoot); |
| 144 | + |
| 145 | + return true; |
19 | 146 | } |
20 | 147 |
|
21 | 148 | bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data) |
22 | 149 | { |
23 | | - HANDLE FF_AUTO_CLOSE_FD handle = CreateFileA(fileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
| 150 | + wchar_t fileNameW[MAX_PATH]; |
| 151 | + ULONG len = 0; |
| 152 | + if (!NT_SUCCESS(RtlUTF8ToUnicodeN(fileNameW, (ULONG) sizeof(fileNameW), &len, fileName, (ULONG)strlen(fileName) + 1))) |
| 153 | + return false; |
| 154 | + |
| 155 | + for (ULONG i = 0; i < len / sizeof(wchar_t); ++i) |
| 156 | + { |
| 157 | + if (fileNameW[i] == L'/') |
| 158 | + fileNameW[i] = L'\\'; |
| 159 | + } |
| 160 | + |
| 161 | + HANDLE FF_AUTO_CLOSE_FD handle = CreateFileW(fileNameW, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
24 | 162 | if (handle == INVALID_HANDLE_VALUE) |
25 | 163 | { |
26 | 164 | if (GetLastError() == ERROR_PATH_NOT_FOUND) |
27 | 165 | { |
28 | | - createSubfolders(fileName); |
29 | | - handle = CreateFileA(fileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
| 166 | + if (!createSubfolders(fileNameW)) |
| 167 | + return false; |
| 168 | + handle = CreateFileW(fileNameW, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
30 | 169 | if (handle == INVALID_HANDLE_VALUE) |
31 | 170 | return false; |
32 | 171 | } |
@@ -70,12 +209,13 @@ static inline void readUntilEOF(HANDLE handle, FFstrbuf* buffer) |
70 | 209 |
|
71 | 210 | bool ffAppendFDBuffer(HANDLE handle, FFstrbuf* buffer) |
72 | 211 | { |
73 | | - LARGE_INTEGER fileSize; |
74 | | - if(!GetFileSizeEx(handle, &fileSize)) |
75 | | - fileSize.QuadPart = 0; |
| 212 | + FILE_STANDARD_INFORMATION fileInfo; |
| 213 | + IO_STATUS_BLOCK iosb; |
| 214 | + if(!NT_SUCCESS(NtQueryInformationFile(handle, &iosb, &fileInfo, sizeof(fileInfo), FileStandardInformation))) |
| 215 | + fileInfo.EndOfFile.QuadPart = 0; |
76 | 216 |
|
77 | | - if (fileSize.QuadPart > 0) |
78 | | - readWithLength(handle, buffer, (uint32_t)fileSize.QuadPart); |
| 217 | + if (fileInfo.EndOfFile.QuadPart > 0) |
| 218 | + readWithLength(handle, buffer, (uint32_t)fileInfo.EndOfFile.QuadPart); |
79 | 219 | else |
80 | 220 | readUntilEOF(handle, buffer); |
81 | 221 |
|
|
0 commit comments