7
7
#include "../../trace.h"
8
8
#include "config.h"
9
9
#include "../../mem-pool.h"
10
+ #include "ntifs.h"
10
11
11
12
static volatile long initialized ;
12
13
static DWORD dwTlsIndex ;
@@ -26,6 +27,13 @@ struct fscache {
26
27
unsigned int opendir_requests ;
27
28
unsigned int fscache_requests ;
28
29
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 ];
29
37
};
30
38
static struct trace_key trace_fscache = TRACE_KEY_INIT (FSCACHE );
31
39
@@ -161,27 +169,44 @@ static void fsentry_release(struct fsentry *fse)
161
169
InterlockedDecrement (& (fse -> u .refcnt ));
162
170
}
163
171
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
+
164
185
/*
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.
166
187
*/
167
188
static struct fsentry * fseentry_create_entry (struct fscache * cache ,
168
189
struct fsentry * list ,
169
- const WIN32_FIND_DATAW * fdata )
190
+ PFILE_FULL_DIR_INFORMATION fdata )
170
191
{
171
192
char buf [MAX_PATH * 3 ];
172
193
int len ;
173
194
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 ));
175
197
176
198
fse = fsentry_alloc (cache , list , buf , len );
177
199
178
- fse -> st_mode = file_attr_to_st_mode (fdata -> dwFileAttributes );
200
+ fse -> st_mode = file_attr_to_st_mode (fdata -> FileAttributes );
179
201
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 ));
185
210
186
211
return fse ;
187
212
}
@@ -194,8 +219,10 @@ static struct fsentry *fseentry_create_entry(struct fscache *cache,
194
219
static struct fsentry * fsentry_create_list (struct fscache * cache , const struct fsentry * dir ,
195
220
int * dir_not_found )
196
221
{
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 ;
199
226
HANDLE h ;
200
227
int wlen ;
201
228
struct fsentry * list , * * phead ;
@@ -211,15 +238,18 @@ static struct fsentry *fsentry_create_list(struct fscache *cache, const struct f
211
238
return NULL ;
212
239
}
213
240
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
+ }
219
249
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 );
223
253
if (h == INVALID_HANDLE_VALUE ) {
224
254
err = GetLastError ();
225
255
* dir_not_found = 1 ; /* or empty directory */
@@ -236,22 +266,55 @@ static struct fsentry *fsentry_create_list(struct fscache *cache, const struct f
236
266
237
267
/* walk directory and build linked list of fsentry structures */
238
268
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 );
241
289
phead = & (* phead )-> next ;
242
- } while (FindNextFileW (h , & fdata ));
243
290
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
+ }
247
308
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 ;
251
311
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 );
253
317
fsentry_release (list );
254
- errno = err_win_to_posix (err );
255
318
return NULL ;
256
319
}
257
320
0 commit comments