44#include "fscache.h"
55#include "config.h"
66#include "../../mem-pool.h"
7+ #include "ntifs.h"
78
89static volatile long initialized ;
910static DWORD dwTlsIndex ;
@@ -23,6 +24,13 @@ struct fscache {
2324 unsigned int opendir_requests ;
2425 unsigned int fscache_requests ;
2526 unsigned int fscache_misses ;
27+ /*
28+ * 32k wide characters translates to 64kB, which is the maximum that
29+ * Windows 8.1 and earlier can handle. On network drives, not only
30+ * the client's Windows version matters, but also the server's,
31+ * therefore we need to keep this to 64kB.
32+ */
33+ WCHAR buffer [32 * 1024 ];
2634};
2735static struct trace_key trace_fscache = TRACE_KEY_INIT (FSCACHE );
2836
@@ -153,27 +161,44 @@ static void fsentry_release(struct fsentry *fse)
153161 InterlockedDecrement (& (fse -> u .refcnt ));
154162}
155163
164+ static int xwcstoutfn (char * utf , int utflen , const wchar_t * wcs , int wcslen )
165+ {
166+ if (!wcs || !utf || utflen < 1 ) {
167+ errno = EINVAL ;
168+ return -1 ;
169+ }
170+ utflen = WideCharToMultiByte (CP_UTF8 , 0 , wcs , wcslen , utf , utflen , NULL , NULL );
171+ if (utflen )
172+ return utflen ;
173+ errno = ERANGE ;
174+ return -1 ;
175+ }
176+
156177/*
157- * Allocate and initialize an fsentry from a WIN32_FIND_DATA structure.
178+ * Allocate and initialize an fsentry from a FILE_FULL_DIR_INFORMATION structure.
158179 */
159180static struct fsentry * fseentry_create_entry (struct fscache * cache ,
160181 struct fsentry * list ,
161- const WIN32_FIND_DATAW * fdata )
182+ PFILE_FULL_DIR_INFORMATION fdata )
162183{
163184 char buf [MAX_PATH * 3 ];
164185 int len ;
165186 struct fsentry * fse ;
166- len = xwcstoutf (buf , fdata -> cFileName , ARRAY_SIZE (buf ));
187+
188+ len = xwcstoutfn (buf , ARRAY_SIZE (buf ), fdata -> FileName , fdata -> FileNameLength / sizeof (wchar_t ));
167189
168190 fse = fsentry_alloc (cache , list , buf , len );
169191
170- fse -> st_mode = file_attr_to_st_mode (fdata -> dwFileAttributes );
192+ fse -> st_mode = file_attr_to_st_mode (fdata -> FileAttributes );
171193 fse -> dirent .d_type = S_ISDIR (fse -> st_mode ) ? DT_DIR : DT_REG ;
172- fse -> u .s .st_size = (((off64_t ) (fdata -> nFileSizeHigh )) << 32 )
173- | fdata -> nFileSizeLow ;
174- filetime_to_timespec (& (fdata -> ftLastAccessTime ), & (fse -> u .s .st_atim ));
175- filetime_to_timespec (& (fdata -> ftLastWriteTime ), & (fse -> u .s .st_mtim ));
176- filetime_to_timespec (& (fdata -> ftCreationTime ), & (fse -> u .s .st_ctim ));
194+ fse -> u .s .st_size = fdata -> EndOfFile .LowPart |
195+ (((off_t )fdata -> EndOfFile .HighPart ) << 32 );
196+ filetime_to_timespec ((FILETIME * )& (fdata -> LastAccessTime ),
197+ & (fse -> u .s .st_atim ));
198+ filetime_to_timespec ((FILETIME * )& (fdata -> LastWriteTime ),
199+ & (fse -> u .s .st_mtim ));
200+ filetime_to_timespec ((FILETIME * )& (fdata -> CreationTime ),
201+ & (fse -> u .s .st_ctim ));
177202
178203 return fse ;
179204}
@@ -186,8 +211,10 @@ static struct fsentry *fseentry_create_entry(struct fscache *cache,
186211static struct fsentry * fsentry_create_list (struct fscache * cache , const struct fsentry * dir ,
187212 int * dir_not_found )
188213{
189- wchar_t pattern [MAX_PATH + 2 ]; /* + 2 for '/' '*' */
190- WIN32_FIND_DATAW fdata ;
214+ wchar_t pattern [MAX_PATH ];
215+ NTSTATUS status ;
216+ IO_STATUS_BLOCK iosb ;
217+ PFILE_FULL_DIR_INFORMATION di ;
191218 HANDLE h ;
192219 int wlen ;
193220 struct fsentry * list , * * phead ;
@@ -203,15 +230,18 @@ static struct fsentry *fsentry_create_list(struct fscache *cache, const struct f
203230 return NULL ;
204231 }
205232
206- /* append optional '/' and wildcard '*' */
207- if (wlen )
208- pattern [wlen ++ ] = '/' ;
209- pattern [wlen ++ ] = '*' ;
210- pattern [wlen ] = 0 ;
233+ /* handle CWD */
234+ if (!wlen ) {
235+ wlen = GetCurrentDirectoryW (ARRAY_SIZE (pattern ), pattern );
236+ if (!wlen || wlen >= ARRAY_SIZE (pattern )) {
237+ errno = wlen ? ENAMETOOLONG : err_win_to_posix (GetLastError ());
238+ return NULL ;
239+ }
240+ }
211241
212- /* open find handle */
213- h = FindFirstFileExW ( pattern , FindExInfoBasic , & fdata , FindExSearchNameMatch ,
214- NULL , FIND_FIRST_EX_LARGE_FETCH );
242+ h = CreateFileW ( pattern , FILE_LIST_DIRECTORY ,
243+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE ,
244+ NULL , OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
215245 if (h == INVALID_HANDLE_VALUE ) {
216246 err = GetLastError ();
217247 * dir_not_found = 1 ; /* or empty directory */
@@ -228,22 +258,55 @@ static struct fsentry *fsentry_create_list(struct fscache *cache, const struct f
228258
229259 /* walk directory and build linked list of fsentry structures */
230260 phead = & list -> next ;
231- do {
232- * phead = fseentry_create_entry (cache , list , & fdata );
261+ status = NtQueryDirectoryFile (h , NULL , 0 , 0 , & iosb , cache -> buffer ,
262+ sizeof (cache -> buffer ), FileFullDirectoryInformation , FALSE, NULL , FALSE);
263+ if (!NT_SUCCESS (status )) {
264+ /*
265+ * NtQueryDirectoryFile returns STATUS_INVALID_PARAMETER when
266+ * asked to enumerate an invalid directory (ie it is a file
267+ * instead of a directory). Verify that is the actual cause
268+ * of the error.
269+ */
270+ if (status == STATUS_INVALID_PARAMETER ) {
271+ DWORD attributes = GetFileAttributesW (pattern );
272+ if (!(attributes & FILE_ATTRIBUTE_DIRECTORY ))
273+ status = ERROR_DIRECTORY ;
274+ }
275+ goto Error ;
276+ }
277+ di = (PFILE_FULL_DIR_INFORMATION )(cache -> buffer );
278+ for (;;) {
279+
280+ * phead = fseentry_create_entry (cache , list , di );
233281 phead = & (* phead )-> next ;
234- } while (FindNextFileW (h , & fdata ));
235282
236- /* remember result of last FindNextFile, then close find handle */
237- err = GetLastError ();
238- FindClose (h );
283+ /* If there is no offset in the entry, the buffer has been exhausted. */
284+ if (di -> NextEntryOffset == 0 ) {
285+ status = NtQueryDirectoryFile (h , NULL , 0 , 0 , & iosb , cache -> buffer ,
286+ sizeof (cache -> buffer ), FileFullDirectoryInformation , FALSE, NULL , FALSE);
287+ if (!NT_SUCCESS (status )) {
288+ if (status == STATUS_NO_MORE_FILES )
289+ break ;
290+ goto Error ;
291+ }
292+
293+ di = (PFILE_FULL_DIR_INFORMATION )(cache -> buffer );
294+ continue ;
295+ }
296+
297+ /* Advance to the next entry. */
298+ di = (PFILE_FULL_DIR_INFORMATION )(((PUCHAR )di ) + di -> NextEntryOffset );
299+ }
239300
240- /* return the list if we've got all the files */
241- if (err == ERROR_NO_MORE_FILES )
242- return list ;
301+ CloseHandle (h );
302+ return list ;
243303
244- /* otherwise release the list and return error */
304+ Error :
305+ trace_printf_key (& trace_fscache ,
306+ "fscache: status(%ld) unable to query directory "
307+ "contents '%s'\n" , status , dir -> dirent .d_name );
308+ CloseHandle (h );
245309 fsentry_release (list );
246- errno = err_win_to_posix (err );
247310 return NULL ;
248311}
249312
0 commit comments