5
5
#include "../../dir.h"
6
6
#include "config.h"
7
7
#include "../../mem-pool.h"
8
+ #include "ntifs.h"
8
9
9
10
static volatile long initialized ;
10
11
static DWORD dwTlsIndex ;
@@ -24,6 +25,13 @@ struct fscache {
24
25
unsigned int opendir_requests ;
25
26
unsigned int fscache_requests ;
26
27
unsigned int fscache_misses ;
28
+ /*
29
+ * 32k wide characters translates to 64kB, which is the maximum that
30
+ * Windows 8.1 and earlier can handle. On network drives, not only
31
+ * the client's Windows version matters, but also the server's,
32
+ * therefore we need to keep this to 64kB.
33
+ */
34
+ WCHAR buffer [32 * 1024 ];
27
35
};
28
36
static struct trace_key trace_fscache = TRACE_KEY_INIT (FSCACHE );
29
37
@@ -159,27 +167,44 @@ static void fsentry_release(struct fsentry *fse)
159
167
InterlockedDecrement (& (fse -> u .refcnt ));
160
168
}
161
169
170
+ static int xwcstoutfn (char * utf , int utflen , const wchar_t * wcs , int wcslen )
171
+ {
172
+ if (!wcs || !utf || utflen < 1 ) {
173
+ errno = EINVAL ;
174
+ return -1 ;
175
+ }
176
+ utflen = WideCharToMultiByte (CP_UTF8 , 0 , wcs , wcslen , utf , utflen , NULL , NULL );
177
+ if (utflen )
178
+ return utflen ;
179
+ errno = ERANGE ;
180
+ return -1 ;
181
+ }
182
+
162
183
/*
163
- * Allocate and initialize an fsentry from a WIN32_FIND_DATA structure.
184
+ * Allocate and initialize an fsentry from a FILE_FULL_DIR_INFORMATION structure.
164
185
*/
165
186
static struct fsentry * fseentry_create_entry (struct fscache * cache ,
166
187
struct fsentry * list ,
167
- const WIN32_FIND_DATAW * fdata )
188
+ PFILE_FULL_DIR_INFORMATION fdata )
168
189
{
169
190
char buf [MAX_PATH * 3 ];
170
191
int len ;
171
192
struct fsentry * fse ;
172
- len = xwcstoutf (buf , fdata -> cFileName , ARRAY_SIZE (buf ));
193
+
194
+ len = xwcstoutfn (buf , ARRAY_SIZE (buf ), fdata -> FileName , fdata -> FileNameLength / sizeof (wchar_t ));
173
195
174
196
fse = fsentry_alloc (cache , list , buf , len );
175
197
176
- fse -> st_mode = file_attr_to_st_mode (fdata -> dwFileAttributes );
198
+ fse -> st_mode = file_attr_to_st_mode (fdata -> FileAttributes );
177
199
fse -> dirent .d_type = S_ISDIR (fse -> st_mode ) ? DT_DIR : DT_REG ;
178
- fse -> u .s .st_size = (((off64_t ) (fdata -> nFileSizeHigh )) << 32 )
179
- | fdata -> nFileSizeLow ;
180
- filetime_to_timespec (& (fdata -> ftLastAccessTime ), & (fse -> u .s .st_atim ));
181
- filetime_to_timespec (& (fdata -> ftLastWriteTime ), & (fse -> u .s .st_mtim ));
182
- filetime_to_timespec (& (fdata -> ftCreationTime ), & (fse -> u .s .st_ctim ));
200
+ fse -> u .s .st_size = fdata -> EndOfFile .LowPart |
201
+ (((off_t )fdata -> EndOfFile .HighPart ) << 32 );
202
+ filetime_to_timespec ((FILETIME * )& (fdata -> LastAccessTime ),
203
+ & (fse -> u .s .st_atim ));
204
+ filetime_to_timespec ((FILETIME * )& (fdata -> LastWriteTime ),
205
+ & (fse -> u .s .st_mtim ));
206
+ filetime_to_timespec ((FILETIME * )& (fdata -> CreationTime ),
207
+ & (fse -> u .s .st_ctim ));
183
208
184
209
return fse ;
185
210
}
@@ -192,8 +217,10 @@ static struct fsentry *fseentry_create_entry(struct fscache *cache,
192
217
static struct fsentry * fsentry_create_list (struct fscache * cache , const struct fsentry * dir ,
193
218
int * dir_not_found )
194
219
{
195
- wchar_t pattern [MAX_PATH + 2 ]; /* + 2 for '/' '*' */
196
- WIN32_FIND_DATAW fdata ;
220
+ wchar_t pattern [MAX_PATH ];
221
+ NTSTATUS status ;
222
+ IO_STATUS_BLOCK iosb ;
223
+ PFILE_FULL_DIR_INFORMATION di ;
197
224
HANDLE h ;
198
225
int wlen ;
199
226
struct fsentry * list , * * phead ;
@@ -209,15 +236,18 @@ static struct fsentry *fsentry_create_list(struct fscache *cache, const struct f
209
236
return NULL ;
210
237
}
211
238
212
- /* append optional '/' and wildcard '*' */
213
- if (wlen )
214
- pattern [wlen ++ ] = '/' ;
215
- pattern [wlen ++ ] = '*' ;
216
- pattern [wlen ] = 0 ;
239
+ /* handle CWD */
240
+ if (!wlen ) {
241
+ wlen = GetCurrentDirectoryW (ARRAY_SIZE (pattern ), pattern );
242
+ if (!wlen || wlen >= ARRAY_SIZE (pattern )) {
243
+ errno = wlen ? ENAMETOOLONG : err_win_to_posix (GetLastError ());
244
+ return NULL ;
245
+ }
246
+ }
217
247
218
- /* open find handle */
219
- h = FindFirstFileExW ( pattern , FindExInfoBasic , & fdata , FindExSearchNameMatch ,
220
- NULL , FIND_FIRST_EX_LARGE_FETCH );
248
+ h = CreateFileW ( pattern , FILE_LIST_DIRECTORY ,
249
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE ,
250
+ NULL , OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
221
251
if (h == INVALID_HANDLE_VALUE ) {
222
252
err = GetLastError ();
223
253
* dir_not_found = 1 ; /* or empty directory */
@@ -234,22 +264,55 @@ static struct fsentry *fsentry_create_list(struct fscache *cache, const struct f
234
264
235
265
/* walk directory and build linked list of fsentry structures */
236
266
phead = & list -> next ;
237
- do {
238
- * phead = fseentry_create_entry (cache , list , & fdata );
267
+ status = NtQueryDirectoryFile (h , NULL , 0 , 0 , & iosb , cache -> buffer ,
268
+ sizeof (cache -> buffer ), FileFullDirectoryInformation , FALSE, NULL , FALSE);
269
+ if (!NT_SUCCESS (status )) {
270
+ /*
271
+ * NtQueryDirectoryFile returns STATUS_INVALID_PARAMETER when
272
+ * asked to enumerate an invalid directory (ie it is a file
273
+ * instead of a directory). Verify that is the actual cause
274
+ * of the error.
275
+ */
276
+ if (status == STATUS_INVALID_PARAMETER ) {
277
+ DWORD attributes = GetFileAttributesW (pattern );
278
+ if (!(attributes & FILE_ATTRIBUTE_DIRECTORY ))
279
+ status = ERROR_DIRECTORY ;
280
+ }
281
+ goto Error ;
282
+ }
283
+ di = (PFILE_FULL_DIR_INFORMATION )(cache -> buffer );
284
+ for (;;) {
285
+
286
+ * phead = fseentry_create_entry (cache , list , di );
239
287
phead = & (* phead )-> next ;
240
- } while (FindNextFileW (h , & fdata ));
241
288
242
- /* remember result of last FindNextFile, then close find handle */
243
- err = GetLastError ();
244
- FindClose (h );
289
+ /* If there is no offset in the entry, the buffer has been exhausted. */
290
+ if (di -> NextEntryOffset == 0 ) {
291
+ status = NtQueryDirectoryFile (h , NULL , 0 , 0 , & iosb , cache -> buffer ,
292
+ sizeof (cache -> buffer ), FileFullDirectoryInformation , FALSE, NULL , FALSE);
293
+ if (!NT_SUCCESS (status )) {
294
+ if (status == STATUS_NO_MORE_FILES )
295
+ break ;
296
+ goto Error ;
297
+ }
298
+
299
+ di = (PFILE_FULL_DIR_INFORMATION )(cache -> buffer );
300
+ continue ;
301
+ }
302
+
303
+ /* Advance to the next entry. */
304
+ di = (PFILE_FULL_DIR_INFORMATION )(((PUCHAR )di ) + di -> NextEntryOffset );
305
+ }
245
306
246
- /* return the list if we've got all the files */
247
- if (err == ERROR_NO_MORE_FILES )
248
- return list ;
307
+ CloseHandle (h );
308
+ return list ;
249
309
250
- /* otherwise release the list and return error */
310
+ Error :
311
+ trace_printf_key (& trace_fscache ,
312
+ "fscache: status(%ld) unable to query directory "
313
+ "contents '%s'\n" , status , dir -> dirent .d_name );
314
+ CloseHandle (h );
251
315
fsentry_release (list );
252
- errno = err_win_to_posix (err );
253
316
return NULL ;
254
317
}
255
318
0 commit comments