/* * Closedir replacement. */ static int fscache_closedir(DIR *base_dir) { fscache_DIR *dir = (fscache_DIR*) base_dir; fsentry_release(dir->pfsentry); free(dir); return 0; }
/* * Clears the cache. */ static void fscache_clear(void) { struct hashmap_iter iter; struct fsentry *fse; while ((fse = hashmap_iter_first(&map, &iter))) { fscache_remove(fse); fsentry_release(fse); } }
/* * Create an fsentry-based directory listing (similar to opendir / readdir). * Dir should not contain trailing '/'. Use an empty string for the current * directory (not "."!). */ static struct fsentry *fsentry_create_list(const struct fsentry *dir) { wchar_t pattern[MAX_LONG_PATH + 2]; /* + 2 for "\*" */ WIN32_FIND_DATAW fdata; HANDLE h; int wlen; struct fsentry *list, **phead; DWORD err; /* convert name to UTF-16 and check length */ if ((wlen = xutftowcs_path_ex(pattern, dir->name, MAX_LONG_PATH, dir->len, MAX_PATH - 2, core_long_paths)) < 0) return NULL; /* * append optional '\' and wildcard '*'. Note: we need to use '\' as * Windows doesn't translate '/' to '\' for "\\?\"-prefixed paths. */ if (wlen) pattern[wlen++] = '\\'; pattern[wlen++] = '*'; pattern[wlen] = 0; /* open find handle */ h = FindFirstFileW(pattern, &fdata); if (h == INVALID_HANDLE_VALUE) { err = GetLastError(); errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err); return NULL; } /* allocate object to hold directory listing */ list = fsentry_alloc(NULL, dir->name, dir->len); /* walk directory and build linked list of fsentry structures */ phead = &list->next; do { *phead = fseentry_create_entry(list, &fdata); phead = &(*phead)->next; } while (FindNextFileW(h, &fdata)); /* remember result of last FindNextFile, then close find handle */ err = GetLastError(); FindClose(h); /* return the list if we've got all the files */ if (err == ERROR_NO_MORE_FILES) return list; /* otherwise free the list and return error */ fsentry_release(list); errno = err_win_to_posix(err); return NULL; }
/* * Lstat replacement, uses the cache if enabled, otherwise redirects to * mingw_lstat. */ int fscache_lstat(const char *filename, struct stat *st) { int dirlen, base, len; struct fsentry key[2], *fse; struct fscache *cache = fscache_getcache(); if (!cache || !do_fscache_enabled(cache, filename)) return mingw_lstat(filename, st); cache->lstat_requests++; /* split filename into path + name */ len = strlen(filename); if (len && is_dir_sep(filename[len - 1])) len--; base = len; while (base && !is_dir_sep(filename[base - 1])) base--; dirlen = base ? base - 1 : 0; /* lookup entry for path + name in cache */ fsentry_init(key, NULL, filename, dirlen); fsentry_init(key + 1, key, filename + base, len - base); fse = fscache_get(cache, key + 1); if (!fse) return -1; /* copy stat data */ st->st_ino = 0; st->st_gid = 0; st->st_uid = 0; st->st_dev = 0; st->st_rdev = 0; st->st_nlink = 1; st->st_mode = fse->st_mode; st->st_size = fse->st_size; st->st_atim = fse->st_atim; st->st_mtim = fse->st_mtim; st->st_ctim = fse->st_ctim; /* don't forget to release fsentry */ fsentry_release(fse); return 0; }
/* * Create an fsentry-based directory listing (similar to opendir / readdir). * Dir should not contain trailing '/'. Use an empty string for the current * directory (not "."!). */ static struct fsentry *fsentry_create_list(struct fscache *cache, const struct fsentry *dir, int *dir_not_found) { wchar_t pattern[MAX_LONG_PATH]; NTSTATUS status; IO_STATUS_BLOCK iosb; PFILE_FULL_DIR_INFORMATION di; HANDLE h; int wlen; struct fsentry *list, **phead; DWORD err; *dir_not_found = 0; /* convert name to UTF-16 and check length */ if ((wlen = xutftowcs_path_ex(pattern, dir->name, MAX_LONG_PATH, dir->len, MAX_PATH - 2, core_long_paths)) < 0) return NULL; /* handle CWD */ if (!wlen) { wlen = GetCurrentDirectoryW(ARRAY_SIZE(pattern), pattern); if (!wlen || wlen >= ARRAY_SIZE(pattern)) { errno = wlen ? ENAMETOOLONG : err_win_to_posix(GetLastError()); return NULL; } } h = CreateFileW(pattern, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { err = GetLastError(); *dir_not_found = 1; /* or empty directory */ errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err); trace_printf_key(&trace_fscache, "fscache: error(%d) '%.*s'\n", errno, dir->len, dir->name); return NULL; } /* allocate object to hold directory listing */ list = fsentry_alloc(cache, NULL, dir->name, dir->len); list->st_mode = S_IFDIR; /* walk directory and build linked list of fsentry structures */ phead = &list->next; status = NtQueryDirectoryFile(h, NULL, 0, 0, &iosb, cache->buffer, sizeof(cache->buffer), FileFullDirectoryInformation, FALSE, NULL, FALSE); if (!NT_SUCCESS(status)) { /* * NtQueryDirectoryFile returns STATUS_INVALID_PARAMETER when * asked to enumerate an invalid directory (ie it is a file * instead of a directory). Verify that is the actual cause * of the error. */ if (status == STATUS_INVALID_PARAMETER) { DWORD attributes = GetFileAttributesW(pattern); if (!(attributes & FILE_ATTRIBUTE_DIRECTORY)) status = ERROR_DIRECTORY; } goto Error; } di = (PFILE_FULL_DIR_INFORMATION)(cache->buffer); for (;;) { *phead = fseentry_create_entry(cache, list, di); phead = &(*phead)->next; /* If there is no offset in the entry, the buffer has been exhausted. */ if (di->NextEntryOffset == 0) { status = NtQueryDirectoryFile(h, NULL, 0, 0, &iosb, cache->buffer, sizeof(cache->buffer), FileFullDirectoryInformation, FALSE, NULL, FALSE); if (!NT_SUCCESS(status)) { if (status == STATUS_NO_MORE_FILES) break; goto Error; } di = (PFILE_FULL_DIR_INFORMATION)(cache->buffer); continue; } /* Advance to the next entry. */ di = (PFILE_FULL_DIR_INFORMATION)(((PUCHAR)di) + di->NextEntryOffset); } CloseHandle(h); return list; Error: errno = (status == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(status); trace_printf_key(&trace_fscache, "fscache: error(%d) unable to query directory contents '%.*s'\n", errno, dir->len, dir->name); CloseHandle(h); fsentry_release(list); return NULL; }