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