77#include "../../trace.h"
88#include "config.h"
99#include "../../mem-pool.h"
10+ #include "ntifs.h"
1011
1112static volatile long initialized ;
1213static DWORD dwTlsIndex ;
@@ -26,6 +27,13 @@ struct fscache {
2627 unsigned int opendir_requests ;
2728 unsigned int fscache_requests ;
2829 unsigned int fscache_misses ;
30+ /*
31+ * 32k wide characters translates to 64kB, which is the maximum that
32+ * Windows 8.1 and earlier can handle. On network drives, not only
33+ * the client's Windows version matters, but also the server's,
34+ * therefore we need to keep this to 64kB.
35+ */
36+ WCHAR buffer [32 * 1024 ];
2937};
3038static struct trace_key trace_fscache = TRACE_KEY_INIT (FSCACHE );
3139
@@ -161,27 +169,44 @@ static void fsentry_release(struct fsentry *fse)
161169 InterlockedDecrement (& (fse -> u .refcnt ));
162170}
163171
172+ static int xwcstoutfn (char * utf , int utflen , const wchar_t * wcs , int wcslen )
173+ {
174+ if (!wcs || !utf || utflen < 1 ) {
175+ errno = EINVAL ;
176+ return -1 ;
177+ }
178+ utflen = WideCharToMultiByte (CP_UTF8 , 0 , wcs , wcslen , utf , utflen , NULL , NULL );
179+ if (utflen )
180+ return utflen ;
181+ errno = ERANGE ;
182+ return -1 ;
183+ }
184+
164185/*
165- * Allocate and initialize an fsentry from a WIN32_FIND_DATA structure.
186+ * Allocate and initialize an fsentry from a FILE_FULL_DIR_INFORMATION structure.
166187 */
167188static struct fsentry * fseentry_create_entry (struct fscache * cache ,
168189 struct fsentry * list ,
169- const WIN32_FIND_DATAW * fdata )
190+ PFILE_FULL_DIR_INFORMATION fdata )
170191{
171192 char buf [MAX_PATH * 3 ];
172193 int len ;
173194 struct fsentry * fse ;
174- len = xwcstoutf (buf , fdata -> cFileName , ARRAY_SIZE (buf ));
195+
196+ len = xwcstoutfn (buf , ARRAY_SIZE (buf ), fdata -> FileName , fdata -> FileNameLength / sizeof (wchar_t ));
175197
176198 fse = fsentry_alloc (cache , list , buf , len );
177199
178- fse -> st_mode = file_attr_to_st_mode (fdata -> dwFileAttributes );
200+ fse -> st_mode = file_attr_to_st_mode (fdata -> FileAttributes );
179201 fse -> dirent .d_type = S_ISDIR (fse -> st_mode ) ? DT_DIR : DT_REG ;
180- fse -> u .s .st_size = (((off64_t ) (fdata -> nFileSizeHigh )) << 32 )
181- | fdata -> nFileSizeLow ;
182- filetime_to_timespec (& (fdata -> ftLastAccessTime ), & (fse -> u .s .st_atim ));
183- filetime_to_timespec (& (fdata -> ftLastWriteTime ), & (fse -> u .s .st_mtim ));
184- filetime_to_timespec (& (fdata -> ftCreationTime ), & (fse -> u .s .st_ctim ));
202+ fse -> u .s .st_size = fdata -> EndOfFile .LowPart |
203+ (((off_t )fdata -> EndOfFile .HighPart ) << 32 );
204+ filetime_to_timespec ((FILETIME * )& (fdata -> LastAccessTime ),
205+ & (fse -> u .s .st_atim ));
206+ filetime_to_timespec ((FILETIME * )& (fdata -> LastWriteTime ),
207+ & (fse -> u .s .st_mtim ));
208+ filetime_to_timespec ((FILETIME * )& (fdata -> CreationTime ),
209+ & (fse -> u .s .st_ctim ));
185210
186211 return fse ;
187212}
@@ -194,8 +219,10 @@ static struct fsentry *fseentry_create_entry(struct fscache *cache,
194219static struct fsentry * fsentry_create_list (struct fscache * cache , const struct fsentry * dir ,
195220 int * dir_not_found )
196221{
197- wchar_t pattern [MAX_PATH + 2 ]; /* + 2 for '/' '*' */
198- WIN32_FIND_DATAW fdata ;
222+ wchar_t pattern [MAX_PATH ];
223+ NTSTATUS status ;
224+ IO_STATUS_BLOCK iosb ;
225+ PFILE_FULL_DIR_INFORMATION di ;
199226 HANDLE h ;
200227 int wlen ;
201228 struct fsentry * list , * * phead ;
@@ -211,15 +238,18 @@ static struct fsentry *fsentry_create_list(struct fscache *cache, const struct f
211238 return NULL ;
212239 }
213240
214- /* append optional '/' and wildcard '*' */
215- if (wlen )
216- pattern [wlen ++ ] = '/' ;
217- pattern [wlen ++ ] = '*' ;
218- pattern [wlen ] = 0 ;
241+ /* handle CWD */
242+ if (!wlen ) {
243+ wlen = GetCurrentDirectoryW (ARRAY_SIZE (pattern ), pattern );
244+ if (!wlen || wlen >= (ssize_t )ARRAY_SIZE (pattern )) {
245+ errno = wlen ? ENAMETOOLONG : err_win_to_posix (GetLastError ());
246+ return NULL ;
247+ }
248+ }
219249
220- /* open find handle */
221- h = FindFirstFileExW ( pattern , FindExInfoBasic , & fdata , FindExSearchNameMatch ,
222- NULL , FIND_FIRST_EX_LARGE_FETCH );
250+ h = CreateFileW ( pattern , FILE_LIST_DIRECTORY ,
251+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE ,
252+ NULL , OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
223253 if (h == INVALID_HANDLE_VALUE ) {
224254 err = GetLastError ();
225255 * dir_not_found = 1 ; /* or empty directory */
@@ -236,22 +266,55 @@ static struct fsentry *fsentry_create_list(struct fscache *cache, const struct f
236266
237267 /* walk directory and build linked list of fsentry structures */
238268 phead = & list -> next ;
239- do {
240- * phead = fseentry_create_entry (cache , list , & fdata );
269+ status = NtQueryDirectoryFile (h , NULL , 0 , 0 , & iosb , cache -> buffer ,
270+ sizeof (cache -> buffer ), FileFullDirectoryInformation , FALSE, NULL , FALSE);
271+ if (!NT_SUCCESS (status )) {
272+ /*
273+ * NtQueryDirectoryFile returns STATUS_INVALID_PARAMETER when
274+ * asked to enumerate an invalid directory (ie it is a file
275+ * instead of a directory). Verify that is the actual cause
276+ * of the error.
277+ */
278+ if (status == (NTSTATUS )STATUS_INVALID_PARAMETER ) {
279+ DWORD attributes = GetFileAttributesW (pattern );
280+ if (!(attributes & FILE_ATTRIBUTE_DIRECTORY ))
281+ status = ERROR_DIRECTORY ;
282+ }
283+ goto Error ;
284+ }
285+ di = (PFILE_FULL_DIR_INFORMATION )(cache -> buffer );
286+ for (;;) {
287+
288+ * phead = fseentry_create_entry (cache , list , di );
241289 phead = & (* phead )-> next ;
242- } while (FindNextFileW (h , & fdata ));
243290
244- /* remember result of last FindNextFile, then close find handle */
245- err = GetLastError ();
246- FindClose (h );
291+ /* If there is no offset in the entry, the buffer has been exhausted. */
292+ if (di -> NextEntryOffset == 0 ) {
293+ status = NtQueryDirectoryFile (h , NULL , 0 , 0 , & iosb , cache -> buffer ,
294+ sizeof (cache -> buffer ), FileFullDirectoryInformation , FALSE, NULL , FALSE);
295+ if (!NT_SUCCESS (status )) {
296+ if (status == STATUS_NO_MORE_FILES )
297+ break ;
298+ goto Error ;
299+ }
300+
301+ di = (PFILE_FULL_DIR_INFORMATION )(cache -> buffer );
302+ continue ;
303+ }
304+
305+ /* Advance to the next entry. */
306+ di = (PFILE_FULL_DIR_INFORMATION )(((PUCHAR )di ) + di -> NextEntryOffset );
307+ }
247308
248- /* return the list if we've got all the files */
249- if (err == ERROR_NO_MORE_FILES )
250- return list ;
309+ CloseHandle (h );
310+ return list ;
251311
252- /* otherwise release the list and return error */
312+ Error :
313+ trace_printf_key (& trace_fscache ,
314+ "fscache: status(%ld) unable to query directory "
315+ "contents '%s'\n" , status , dir -> dirent .d_name );
316+ CloseHandle (h );
253317 fsentry_release (list );
254- errno = err_win_to_posix (err );
255318 return NULL ;
256319}
257320
0 commit comments