39#pragma comment(lib, "Shlwapi.lib")
43#pragma comment(lib, "advapi32.lib")
90#define NTFS_MAX_PATH 32768
93#ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE
94#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE 16384
97#ifndef IO_REPARSE_TAG_SYMLINK
98#define IO_REPARSE_TAG_SYMLINK 0xA000000C
101#ifndef FILE_NAME_NORMALIZED
102#define FILE_NAME_NORMALIZED 0
108typedef struct ino_t {
109 unsigned long long serial;
110 unsigned char fileid[16];
117 unsigned short d_reclen;
118 unsigned char d_namelen;
119 unsigned char d_type;
120 char d_name[NAME_MAX];
125 struct dirent* entries;
137int closedir(DIR* dirp) {
138 struct __dir* data = NULL;
143 data = (
struct __dir*)dirp;
144 CloseHandle((HANDLE)data->fd);
151static void __seterrno(
int value) {
160static int __islink(
const wchar_t* name,
char* buffer) {
162 DWORD bytes_returned = 0;
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;
167 io_result = (DWORD)DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, buffer,
168 MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytes_returned, NULL);
172 if (io_result == 0)
return 0;
174 return ((REPARSE_GUID_DATA_BUFFER*)buffer)->ReparseTag == IO_REPARSE_TAG_SYMLINK;
179typedef struct dirent_FILE_ID_128 {
183typedef struct _dirent_FILE_ID_INFO {
184 ULONGLONG VolumeSerialNumber;
185 dirent_FILE_ID_128 FileId;
186} dirent_FILE_ID_INFO;
190typedef enum dirent_FILE_INFO_BY_HANDLE_CLASS { dirent_FileIdInfo = 18 } dirent_FILE_INFO_BY_HANDLE_CLASS;
193static __ino_t __inode(
const wchar_t* name) {
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);
202 HANDLE hKernel32 = GetModuleHandleW(L
"kernel32.dll");
203 if (!hKernel32)
return value;
205 pfnGetFileInformationByHandleEx fnGetFileInformationByHandleEx =
206 (pfnGetFileInformationByHandleEx)(
void*)GetProcAddress(hKernel32,
"GetFileInformationByHandleEx");
208 if (!fnGetFileInformationByHandleEx)
return value;
210 HANDLE hFile = CreateFileW(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
211 if (hFile == INVALID_HANDLE_VALUE)
return value;
213 result = fnGetFileInformationByHandleEx(hFile, dirent_FileIdInfo, &fileid,
sizeof(fileid));
215 value.serial = fileid.VolumeSerialNumber;
216 memcpy(value.fileid, fileid.FileId.Identifier, 16);
218 result = GetFileInformationByHandle(hFile, &info);
220 value.serial = info.dwVolumeSerialNumber;
221 memcpy(value.fileid + 8, &info.nFileIndexHigh, 4);
222 memcpy(value.fileid + 12, &info.nFileIndexLow, 4);
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;
236 static int extra_suffix = 4;
237 WIN32_FIND_DATAW w32fd = {0};
238 HANDLE hFindFile = INVALID_HANDLE_VALUE;
239 static size_t grow_factor = 2;
242 BOOL relative = PathIsRelativeW(wname + extra_prefix);
244 memcpy(wname + size - 1, suffix,
sizeof(
wchar_t) * (
size_t)extra_suffix);
245 wname[size + extra_suffix - 1] = 0;
248 wname += extra_prefix;
249 size -= extra_prefix;
251 hFindFile = FindFirstFileW(wname, &w32fd);
252 if (INVALID_HANDLE_VALUE == hFindFile) {
257 data = (
struct __dir*)malloc(
sizeof(
struct __dir));
258 if (!data)
goto out_of_memory;
260 data->fd = (intptr_t)CreateFileW(wname, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
261 wname[size - 1] = L
'\\';
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;
269 WideCharToMultiByte(CP_UTF8, 0, w32fd.cFileName, -1, data->entries[data->index].d_name, NAME_MAX, &default_char,
272 memcpy(wname + size, w32fd.cFileName,
sizeof(
wchar_t) * NAME_MAX);
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;
282 data->entries[data->index].d_type = DT_REG;
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;
289 if (++data->index == data->count) {
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;
296 }
while (FindNextFileW(hFindFile, &w32fd) != 0);
299 FindClose(hFindFile);
301 data->count = data->index;
306 if (INVALID_HANDLE_VALUE != (HANDLE)data->fd) CloseHandle((HANDLE)data->fd);
311 if (INVALID_HANDLE_VALUE != hFindFile) FindClose(hFindFile);
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);
329DIR* opendir(
const char* name) {
331 wchar_t* wname = __get_buffer();
337 size = MultiByteToWideChar(CP_UTF8, 0, name, -1, wname + 4, NTFS_MAX_PATH);
342 dirp = __internal_opendir(wname, size + 4);
353DIR* _wopendir(
const wchar_t* name) {
355 wchar_t* wname = __get_buffer();
362 if (size > NTFS_MAX_PATH) {
366 memcpy(wname + 4, name,
sizeof(
wchar_t) * ((
size_t)size + 1));
367 dirp = __internal_opendir(wname, size + 5);
378DIR* fdopendir(intptr_t fd) {
380 wchar_t* wname = __get_buffer();
381 typedef DWORD(__stdcall * pfnGetFinalPathNameByHandleW)(HANDLE hFile, LPWSTR lpszFilePath, DWORD cchFilePath,
384 HANDLE hKernel32 = GetModuleHandleW(L
"kernel32.dll");
390 pfnGetFinalPathNameByHandleW fnGetFinalPathNameByHandleW =
391 (pfnGetFinalPathNameByHandleW)(
void*)GetProcAddress(hKernel32,
"GetFinalPathNameByHandleW");
392 if (!fnGetFinalPathNameByHandleW) {
402 size = fnGetFinalPathNameByHandleW((HANDLE)fd, wname + 4, NTFS_MAX_PATH, FILE_NAME_NORMALIZED);
408 dirp = __internal_opendir(wname, size + 5);
419struct dirent* readdir(DIR* dirp) {
420 struct __dir* data = (
struct __dir*)dirp;
425 if (data->index < data->count) {
426 return &data->entries[data->index++];
439int readdir_r(DIR* dirp,
struct dirent* entry,
struct dirent** result) {
440 struct __dir* data = (
struct __dir*)dirp;
441 if (!data || !entry || !result) {
444 if (data->index < data->count) {
445 memcpy(entry, &data->entries[data->index++],
sizeof(
struct dirent));
459void seekdir(DIR* dirp,
long int offset) {
461 struct __dir* data = (
struct __dir*)dirp;
462 if (offset >= 0 && offset <= data->count) {
463 data->index = offset;
473void rewinddir(DIR* dirp) { seekdir(dirp, 0); }
481long int telldir(DIR* dirp) {
486 return ((
struct __dir*)dirp)->index;
495intptr_t dirfd(DIR* dirp) {
500 return ((
struct __dir*)dirp)->fd;
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;
523 entries = (
struct dirent**)malloc(
sizeof(
struct dirent*) * count);
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]) {
535 for (i = 0; i < index; ++i) free(entries[i]);
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);
545 for (i = 0; i < index; ++i) free(entries[i]);
550 entries = tmp_entries;
555 qsort(entries, index,
sizeof(
struct dirent*), (
int (*)(
const void*,
const void*))compar);
556 entries[index] = NULL;
557 if (namelist) *namelist = entries;
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);
575static int __strverscmp(
const char* s1,
const char* s2) {
576 if (!s1 || !s2)
return 0;
579 if (isdigit((
unsigned char)*s1) && isdigit((
unsigned char)*s2)) {
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;
585 if (*s1 != *s2)
return ((
unsigned char)*s1 < (
unsigned char)*s2) ? -1 : 1;
590 return *s1 ? 1 : (*s2 ? -1 : 0);
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);