Skip to content

Commit b128260

Browse files
committed
IO (Windows): improves sub directory creation logic
1 parent ecfaf4d commit b128260

1 file changed

Lines changed: 158 additions & 18 deletions

File tree

src/common/impl/io_windows.c

Lines changed: 158 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,171 @@
1-
#include "common/io.h"
21
#include "fastfetch.h"
2+
#include "common/io.h"
33
#include "common/stringUtils.h"
4+
#include "common/windows/nt.h"
45

56
#include <windows.h>
6-
#include "common/windows/nt.h"
7-
#include <ntstatus.h>
87

9-
static void createSubfolders(const char* fileName)
8+
static bool createSubfolders(wchar_t* fileName)
109
{
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'\\')
1416
{
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:\"
1836
}
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;
19146
}
20147

21148
bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data)
22149
{
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);
24162
if (handle == INVALID_HANDLE_VALUE)
25163
{
26164
if (GetLastError() == ERROR_PATH_NOT_FOUND)
27165
{
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);
30169
if (handle == INVALID_HANDLE_VALUE)
31170
return false;
32171
}
@@ -70,12 +209,13 @@ static inline void readUntilEOF(HANDLE handle, FFstrbuf* buffer)
70209

71210
bool ffAppendFDBuffer(HANDLE handle, FFstrbuf* buffer)
72211
{
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;
76216

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);
79219
else
80220
readUntilEOF(handle, buffer);
81221

0 commit comments

Comments
 (0)