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