solidc
Robust collection of general-purpose cross-platform C libraries and data structures designed for rapid and safe development in C
Loading...
Searching...
No Matches
win32_dirent.c
1/*
2MIT License
3Copyright (c) 2019 win32ports
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"), to deal
6in the Software without restriction, including without limitation the rights
7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8copies of the Software, and to permit persons to whom the Software is
9furnished to do so, subject to the following conditions:
10The above copyright notice and this permission notice shall be included in all
11copies or substantial portions of the Software.
12THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18SOFTWARE.
19*/
20
21// Source: https://github.com/win32ports/dirent_h
22
23#ifdef _WIN32
24#ifdef __cplusplus
25extern "C" {
26#endif /* __cplusplus */
27
28#include <ctype.h> // for isdigit
29#include <errno.h>
30#include <shlwapi.h> // for PathIsRelativeW
31#include <stdint.h>
32#include <stdlib.h> // for malloc, free
33#include <string.h> // for memcpy, memset
34#include <sys/types.h>
35#include <windows.h> // for Windows API
36#include <winioctl.h> // for FSCTL_GET_REPARSE_POINT
37
38#ifdef _MSC_VER
39#pragma comment(lib, "Shlwapi.lib")
40#endif
41
42#include <wincrypt.h> // For HCRYPTPROV, CryptAcquireContextW, CryptGenRandom, etc.
43#pragma comment(lib, "advapi32.lib") // Link against the Crypto API library
44
45/*
46 * Constants and Macros
47 */
48
49#ifndef NAME_MAX
50#define NAME_MAX 260
51#endif /* NAME_MAX */
52
53#ifndef DT_UNKNOWN
54#define DT_UNKNOWN 0
55#endif /* DT_UNKNOWN */
56
57#ifndef DT_FIFO
58#define DT_FIFO 1
59#endif /* DT_FIFO */
60
61#ifndef DT_CHR
62#define DT_CHR 2
63#endif /* DT_CHR */
64
65#ifndef DT_DIR
66#define DT_DIR 4
67#endif /* DT_DIR */
68
69#ifndef DT_BLK
70#define DT_BLK 6
71#endif /* DT_BLK */
72
73#ifndef DT_REG
74#define DT_REG 8
75#endif /* DT_REG */
76
77#ifndef DT_LNK
78#define DT_LNK 10
79#endif /* DT_LNK */
80
81#ifndef DT_SOCK
82#define DT_SOCK 12
83#endif /* DT_SOCK */
84
85#ifndef DT_WHT
86#define DT_WHT 14
87#endif /* DT_WHT */
88
89#ifndef NTFS_MAX_PATH
90#define NTFS_MAX_PATH 32768
91#endif /* NTFS_MAX_PATH */
92
93#ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE
94#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE 16384
95#endif /* MAXIMUM_REPARSE_DATA_BUFFER_SIZE */
96
97#ifndef IO_REPARSE_TAG_SYMLINK
98#define IO_REPARSE_TAG_SYMLINK 0xA000000C
99#endif /* IO_REPARSE_TAG_SYMLINK */
100
101#ifndef FILE_NAME_NORMALIZED
102#define FILE_NAME_NORMALIZED 0
103#endif /* FILE_NAME_NORMALIZED */
104
105typedef void* DIR;
106
108typedef struct ino_t {
109 unsigned long long serial;
110 unsigned char fileid[16];
111} __ino_t;
112
114struct dirent {
115 __ino_t d_ino; /* File serial number */
116 off_t d_off; /* Offset to the next dirent */
117 unsigned short d_reclen; /* Length of this record */
118 unsigned char d_namelen; /* Length of the name */
119 unsigned char d_type; /* Type of file (DT_*) */
120 char d_name[NAME_MAX]; /* Null-terminated filename */
121};
122
124struct __dir {
125 struct dirent* entries;
126 intptr_t fd;
127 long int count;
128 long int index;
129};
130
137int closedir(DIR* dirp) {
138 struct __dir* data = NULL;
139 if (!dirp) {
140 errno = EBADF;
141 return -1;
142 }
143 data = (struct __dir*)dirp;
144 CloseHandle((HANDLE)data->fd);
145 free(data->entries);
146 free(data);
147 return 0;
148}
149
151static void __seterrno(int value) {
152#ifdef _MSC_VER
153 _set_errno(value);
154#else /* _MSC_VER */
155 errno = value;
156#endif /* _MSC_VER */
157}
158
160static int __islink(const wchar_t* name, char* buffer) {
161 DWORD io_result = 0;
162 DWORD bytes_returned = 0;
163 HANDLE hFile =
164 CreateFileW(name, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0);
165 if (hFile == INVALID_HANDLE_VALUE) return 0;
166
167 io_result = (DWORD)DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, buffer,
168 MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytes_returned, NULL);
169
170 CloseHandle(hFile);
171
172 if (io_result == 0) return 0;
173
174 return ((REPARSE_GUID_DATA_BUFFER*)buffer)->ReparseTag == IO_REPARSE_TAG_SYMLINK;
175}
176
177#pragma pack(push, 1)
178
179typedef struct dirent_FILE_ID_128 {
180 BYTE Identifier[16];
181} dirent_FILE_ID_128;
182
183typedef struct _dirent_FILE_ID_INFO {
184 ULONGLONG VolumeSerialNumber;
185 dirent_FILE_ID_128 FileId;
186} dirent_FILE_ID_INFO;
187
188#pragma pack(pop)
189
190typedef enum dirent_FILE_INFO_BY_HANDLE_CLASS { dirent_FileIdInfo = 18 } dirent_FILE_INFO_BY_HANDLE_CLASS;
191
193static __ino_t __inode(const wchar_t* name) {
194 __ino_t value = {0};
195 BOOL result;
196 dirent_FILE_ID_INFO fileid;
197 BY_HANDLE_FILE_INFORMATION info;
198 typedef BOOL(__stdcall * pfnGetFileInformationByHandleEx)(HANDLE hFile,
199 dirent_FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
200 LPVOID lpFileInformation, DWORD dwBufferSize);
201
202 HANDLE hKernel32 = GetModuleHandleW(L"kernel32.dll");
203 if (!hKernel32) return value;
204
205 pfnGetFileInformationByHandleEx fnGetFileInformationByHandleEx =
206 (pfnGetFileInformationByHandleEx)(void*)GetProcAddress(hKernel32, "GetFileInformationByHandleEx");
207
208 if (!fnGetFileInformationByHandleEx) return value;
209
210 HANDLE hFile = CreateFileW(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
211 if (hFile == INVALID_HANDLE_VALUE) return value;
212
213 result = fnGetFileInformationByHandleEx(hFile, dirent_FileIdInfo, &fileid, sizeof(fileid));
214 if (result) {
215 value.serial = fileid.VolumeSerialNumber;
216 memcpy(value.fileid, fileid.FileId.Identifier, 16);
217 } else {
218 result = GetFileInformationByHandle(hFile, &info);
219 if (result) {
220 value.serial = info.dwVolumeSerialNumber;
221 memcpy(value.fileid + 8, &info.nFileIndexHigh, 4);
222 memcpy(value.fileid + 12, &info.nFileIndexLow, 4);
223 }
224 }
225 CloseHandle(hFile);
226 return value;
227}
228
230static DIR* __internal_opendir(wchar_t* wname, int size) {
231 struct __dir* data = NULL;
232 struct dirent* tmp_entries = NULL;
233 static char default_char = '?';
234 static wchar_t* suffix = L"\\*.*";
235 static int extra_prefix = 4; /* use prefix "\\?\" to handle long file names */
236 static int extra_suffix = 4; /* use suffix "\*.*" to find everything */
237 WIN32_FIND_DATAW w32fd = {0};
238 HANDLE hFindFile = INVALID_HANDLE_VALUE;
239 static size_t grow_factor = 2;
240 char* buffer = NULL;
241
242 BOOL relative = PathIsRelativeW(wname + extra_prefix);
243
244 memcpy(wname + size - 1, suffix, sizeof(wchar_t) * (size_t)extra_suffix);
245 wname[size + extra_suffix - 1] = 0;
246
247 if (relative) {
248 wname += extra_prefix;
249 size -= extra_prefix;
250 }
251 hFindFile = FindFirstFileW(wname, &w32fd);
252 if (INVALID_HANDLE_VALUE == hFindFile) {
253 __seterrno(ENOENT);
254 return NULL;
255 }
256
257 data = (struct __dir*)malloc(sizeof(struct __dir));
258 if (!data) goto out_of_memory;
259 wname[size - 1] = 0;
260 data->fd = (intptr_t)CreateFileW(wname, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
261 wname[size - 1] = L'\\';
262 data->count = 16;
263 data->index = 0;
264 data->entries = (struct dirent*)malloc(sizeof(struct dirent) * (size_t)data->count);
265 if (!data->entries) goto out_of_memory;
266 buffer = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
267 if (!buffer) goto out_of_memory;
268 do {
269 WideCharToMultiByte(CP_UTF8, 0, w32fd.cFileName, -1, data->entries[data->index].d_name, NAME_MAX, &default_char,
270 NULL);
271
272 memcpy(wname + size, w32fd.cFileName, sizeof(wchar_t) * NAME_MAX);
273
274 if (((w32fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT) &&
275 __islink(wname, buffer))
276 data->entries[data->index].d_type = DT_LNK;
277 else if ((w32fd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) == FILE_ATTRIBUTE_DEVICE)
278 data->entries[data->index].d_type = DT_CHR;
279 else if ((w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
280 data->entries[data->index].d_type = DT_DIR;
281 else
282 data->entries[data->index].d_type = DT_REG;
283
284 data->entries[data->index].d_ino = __inode(wname);
285 data->entries[data->index].d_reclen = sizeof(struct dirent);
286 data->entries[data->index].d_namelen = (unsigned char)wcslen(w32fd.cFileName);
287 data->entries[data->index].d_off = data->index + 1; // POSIX fix: offset to next entry
288
289 if (++data->index == data->count) {
290 tmp_entries =
291 (struct dirent*)realloc(data->entries, sizeof(struct dirent) * (size_t)data->count * grow_factor);
292 if (!tmp_entries) goto out_of_memory;
293 data->entries = tmp_entries;
294 data->count *= grow_factor;
295 }
296 } while (FindNextFileW(hFindFile, &w32fd) != 0);
297
298 free(buffer);
299 FindClose(hFindFile);
300
301 data->count = data->index;
302 data->index = 0;
303 return (DIR*)data;
304out_of_memory:
305 if (data) {
306 if (INVALID_HANDLE_VALUE != (HANDLE)data->fd) CloseHandle((HANDLE)data->fd);
307 free(data->entries);
308 }
309 free(buffer);
310 free(data);
311 if (INVALID_HANDLE_VALUE != hFindFile) FindClose(hFindFile);
312 __seterrno(ENOMEM);
313 return NULL;
314}
315
317static wchar_t* __get_buffer(void) {
318 wchar_t* name = malloc(sizeof(wchar_t) * (NTFS_MAX_PATH + NAME_MAX + 8));
319 if (name) memcpy(name, L"\\\\?\\", sizeof(wchar_t) * 4);
320 return name;
321}
322
329DIR* opendir(const char* name) {
330 DIR* dirp = NULL;
331 wchar_t* wname = __get_buffer();
332 int size = 0;
333 if (!wname) {
334 errno = ENOMEM;
335 return NULL;
336 }
337 size = MultiByteToWideChar(CP_UTF8, 0, name, -1, wname + 4, NTFS_MAX_PATH);
338 if (0 == size) {
339 free(wname);
340 return NULL;
341 }
342 dirp = __internal_opendir(wname, size + 4);
343 free(wname);
344 return dirp;
345}
346
353DIR* _wopendir(const wchar_t* name) {
354 DIR* dirp = NULL;
355 wchar_t* wname = __get_buffer();
356 int size = 0;
357 if (!wname) {
358 errno = ENOMEM;
359 return NULL;
360 }
361 size = wcslen(name);
362 if (size > NTFS_MAX_PATH) {
363 free(wname);
364 return NULL;
365 }
366 memcpy(wname + 4, name, sizeof(wchar_t) * ((size_t)size + 1));
367 dirp = __internal_opendir(wname, size + 5);
368 free(wname);
369 return dirp;
370}
371
378DIR* fdopendir(intptr_t fd) {
379 DIR* dirp = NULL;
380 wchar_t* wname = __get_buffer();
381 typedef DWORD(__stdcall * pfnGetFinalPathNameByHandleW)(HANDLE hFile, LPWSTR lpszFilePath, DWORD cchFilePath,
382 DWORD dwFlags);
383
384 HANDLE hKernel32 = GetModuleHandleW(L"kernel32.dll");
385 if (!hKernel32) {
386 errno = EINVAL;
387 return NULL;
388 }
389
390 pfnGetFinalPathNameByHandleW fnGetFinalPathNameByHandleW =
391 (pfnGetFinalPathNameByHandleW)(void*)GetProcAddress(hKernel32, "GetFinalPathNameByHandleW");
392 if (!fnGetFinalPathNameByHandleW) {
393 errno = EINVAL;
394 return NULL;
395 }
396
397 size_t size = 0;
398 if (!wname) {
399 errno = ENOMEM;
400 return NULL;
401 }
402 size = fnGetFinalPathNameByHandleW((HANDLE)fd, wname + 4, NTFS_MAX_PATH, FILE_NAME_NORMALIZED);
403 if (0 == size) {
404 free(wname);
405 errno = ENOTDIR;
406 return NULL;
407 }
408 dirp = __internal_opendir(wname, size + 5);
409 free(wname);
410 return dirp;
411}
412
419struct dirent* readdir(DIR* dirp) {
420 struct __dir* data = (struct __dir*)dirp;
421 if (!data) {
422 errno = EBADF;
423 return NULL;
424 }
425 if (data->index < data->count) {
426 return &data->entries[data->index++];
427 }
428 return NULL;
429}
430
439int readdir_r(DIR* dirp, struct dirent* entry, struct dirent** result) {
440 struct __dir* data = (struct __dir*)dirp;
441 if (!data || !entry || !result) {
442 return EINVAL;
443 }
444 if (data->index < data->count) {
445 memcpy(entry, &data->entries[data->index++], sizeof(struct dirent));
446 *result = entry;
447 } else {
448 *result = NULL;
449 }
450 return 0;
451}
452
459void seekdir(DIR* dirp, long int offset) {
460 if (dirp) {
461 struct __dir* data = (struct __dir*)dirp;
462 if (offset >= 0 && offset <= data->count) {
463 data->index = offset;
464 }
465 }
466}
467
473void rewinddir(DIR* dirp) { seekdir(dirp, 0); }
474
481long int telldir(DIR* dirp) {
482 if (!dirp) {
483 errno = EBADF;
484 return -1;
485 }
486 return ((struct __dir*)dirp)->index;
487}
488
495intptr_t dirfd(DIR* dirp) {
496 if (!dirp) {
497 errno = EINVAL;
498 return -1;
499 }
500 return ((struct __dir*)dirp)->fd;
501}
502
512int scandir(const char* dirp, struct dirent*** namelist, int (*filter)(const struct dirent*),
513 int (*compar)(const struct dirent**, const struct dirent**)) {
514 struct dirent **entries = NULL, **tmp_entries = NULL;
515 unsigned long int i = 0, index = 0, count = 16;
516 DIR* d = opendir(dirp);
517 struct __dir* data = (struct __dir*)d;
518 if (!data) {
519 closedir(d);
520 __seterrno(ENOENT);
521 return -1;
522 }
523 entries = (struct dirent**)malloc(sizeof(struct dirent*) * count);
524 if (!entries) {
525 closedir(d);
526 __seterrno(ENOMEM);
527 return -1;
528 }
529
530 for (i = 0; i < (size_t)data->count; ++i) {
531 if (!filter || filter(&data->entries[i])) {
532 entries[index] = (struct dirent*)malloc(sizeof(struct dirent));
533 if (!entries[index]) {
534 closedir(d);
535 for (i = 0; i < index; ++i) free(entries[i]);
536 free(entries);
537 __seterrno(ENOMEM);
538 return -1;
539 }
540 memcpy(entries[index], &data->entries[i], sizeof(struct dirent));
541 if (++index == count) {
542 tmp_entries = (struct dirent**)realloc(entries, sizeof(struct dirent*) * count * 2);
543 if (!tmp_entries) {
544 closedir(d);
545 for (i = 0; i < index; ++i) free(entries[i]);
546 free(entries);
547 __seterrno(ENOMEM);
548 return -1;
549 }
550 entries = tmp_entries;
551 count *= 2;
552 }
553 }
554 }
555 qsort(entries, index, sizeof(struct dirent*), (int (*)(const void*, const void*))compar);
556 entries[index] = NULL;
557 if (namelist) *namelist = entries;
558 closedir(d);
559 return (int)index;
560}
561
566int alphasort(const struct dirent** a, const struct dirent** b) {
567 if (!a || !b || !*a || !*b) return 0;
568 return strcoll((*a)->d_name, (*b)->d_name);
569}
570
575static int __strverscmp(const char* s1, const char* s2) {
576 if (!s1 || !s2) return 0;
577
578 while (*s1 && *s2) {
579 if (isdigit((unsigned char)*s1) && isdigit((unsigned char)*s2)) {
580 // Compare numeric parts
581 unsigned long n1 = strtoul(s1, (char**)&s1, 10);
582 unsigned long n2 = strtoul(s2, (char**)&s2, 10);
583 if (n1 != n2) return (n1 < n2) ? -1 : 1;
584 } else {
585 if (*s1 != *s2) return ((unsigned char)*s1 < (unsigned char)*s2) ? -1 : 1;
586 s1++;
587 s2++;
588 }
589 }
590 return *s1 ? 1 : (*s2 ? -1 : 0);
591}
592
597int versionsort(const struct dirent** a, const struct dirent** b) {
598 if (!a || !b || !*a || !*b) return 0;
599 return __strverscmp((*a)->d_name, (*b)->d_name);
600}
601
602#ifdef __cplusplus
603}
604#endif /* __cplusplus */
605
606#endif /* _WIN32 */