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
@@ -145,16 +153,30 @@ static void fsentry_release(struct fsentry *fse)
145
153
InterlockedDecrement (& (fse -> refcnt ));
146
154
}
147
155
156
+ static int xwcstoutfn (char * utf , int utflen , const wchar_t * wcs , int wcslen )
157
+ {
158
+ if (!wcs || !utf || utflen < 1 ) {
159
+ errno = EINVAL ;
160
+ return -1 ;
161
+ }
162
+ utflen = WideCharToMultiByte (CP_UTF8 , 0 , wcs , wcslen , utf , utflen , NULL , NULL );
163
+ if (utflen )
164
+ return utflen ;
165
+ errno = ERANGE ;
166
+ return -1 ;
167
+ }
168
+
148
169
/*
149
- * Allocate and initialize an fsentry from a WIN32_FIND_DATA structure.
170
+ * Allocate and initialize an fsentry from a FILE_FULL_DIR_INFORMATION structure.
150
171
*/
151
172
static struct fsentry * fseentry_create_entry (struct fscache * cache , struct fsentry * list ,
152
- const WIN32_FIND_DATAW * fdata )
173
+ PFILE_FULL_DIR_INFORMATION fdata )
153
174
{
154
175
char buf [MAX_PATH * 3 ];
155
176
int len ;
156
177
struct fsentry * fse ;
157
- len = xwcstoutf (buf , fdata -> cFileName , ARRAY_SIZE (buf ));
178
+
179
+ len = xwcstoutfn (buf , ARRAY_SIZE (buf ), fdata -> FileName , fdata -> FileNameLength / sizeof (wchar_t ));
158
180
159
181
fse = fsentry_alloc (cache , list , buf , len );
160
182
@@ -167,7 +189,8 @@ static struct fsentry *fseentry_create_entry(struct fscache *cache, struct fsent
167
189
* Let's work around this by detecting that situation and
168
190
* telling Git that these are *not* symbolic links.
169
191
*/
170
- if (fdata -> dwReserved0 == IO_REPARSE_TAG_SYMLINK &&
192
+ if (fdata -> FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
193
+ fdata -> EaSize == IO_REPARSE_TAG_SYMLINK &&
171
194
sizeof (buf ) > (list ? list -> len + 1 : 0 ) + fse -> len + 1 &&
172
195
is_inside_windows_container ()) {
173
196
size_t off = 0 ;
@@ -180,13 +203,13 @@ static struct fsentry *fseentry_create_entry(struct fscache *cache, struct fsent
180
203
buf [off + fse -> len ] = '\0' ;
181
204
}
182
205
183
- fse -> st_mode = file_attr_to_st_mode (fdata -> dwFileAttributes ,
184
- fdata -> dwReserved0 , buf );
206
+ fse -> st_mode = file_attr_to_st_mode (fdata -> FileAttributes ,
207
+ fdata -> EaSize , buf );
185
208
fse -> st_size = S_ISLNK (fse -> st_mode ) ? MAX_LONG_PATH :
186
- fdata -> nFileSizeLow | (((off_t ) fdata -> nFileSizeHigh ) << 32 );
187
- filetime_to_timespec (& (fdata -> ftLastAccessTime ), & (fse -> st_atim ));
188
- filetime_to_timespec (& (fdata -> ftLastWriteTime ), & (fse -> st_mtim ));
189
- filetime_to_timespec (& (fdata -> ftCreationTime ), & (fse -> st_ctim ));
209
+ fdata -> EndOfFile . LowPart | (((off_t )fdata -> EndOfFile . HighPart ) << 32 );
210
+ filetime_to_timespec (( FILETIME * ) & (fdata -> LastAccessTime ), & (fse -> st_atim ));
211
+ filetime_to_timespec (( FILETIME * ) & (fdata -> LastWriteTime ), & (fse -> st_mtim ));
212
+ filetime_to_timespec (( FILETIME * ) & (fdata -> CreationTime ), & (fse -> st_ctim ));
190
213
191
214
return fse ;
192
215
}
@@ -199,8 +222,10 @@ static struct fsentry *fseentry_create_entry(struct fscache *cache, struct fsent
199
222
static struct fsentry * fsentry_create_list (struct fscache * cache , const struct fsentry * dir ,
200
223
int * dir_not_found )
201
224
{
202
- wchar_t pattern [MAX_LONG_PATH + 2 ]; /* + 2 for "\*" */
203
- WIN32_FIND_DATAW fdata ;
225
+ wchar_t pattern [MAX_LONG_PATH ];
226
+ NTSTATUS status ;
227
+ IO_STATUS_BLOCK iosb ;
228
+ PFILE_FULL_DIR_INFORMATION di ;
204
229
HANDLE h ;
205
230
int wlen ;
206
231
struct fsentry * list , * * phead ;
@@ -213,18 +238,18 @@ static struct fsentry *fsentry_create_list(struct fscache *cache, const struct f
213
238
dir -> len , MAX_PATH - 2 , core_long_paths )) < 0 )
214
239
return NULL ;
215
240
216
- /*
217
- * append optional '\' and wildcard '*'. Note: we need to use '\' as
218
- * Windows doesn't translate '/' to '\' for "\\?\"-prefixed paths.
219
- */
220
- if ( wlen )
221
- pattern [ wlen ++ ] = '\\' ;
222
- pattern [ wlen ++ ] = '*' ;
223
- pattern [ wlen ] = 0 ;
224
-
225
- /* open find handle */
226
- h = FindFirstFileExW ( pattern , FindExInfoBasic , & fdata , FindExSearchNameMatch ,
227
- NULL , FIND_FIRST_EX_LARGE_FETCH );
241
+ /* handle CWD */
242
+ if (! wlen ) {
243
+ wlen = GetCurrentDirectoryW ( ARRAY_SIZE ( pattern ), pattern );
244
+ if (! wlen || wlen >= ARRAY_SIZE ( pattern )) {
245
+ errno = wlen ? ENAMETOOLONG : err_win_to_posix ( GetLastError ());
246
+ return NULL ;
247
+ }
248
+ }
249
+
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 );
228
253
if (h == INVALID_HANDLE_VALUE ) {
229
254
err = GetLastError ();
230
255
* dir_not_found = 1 ; /* or empty directory */
@@ -240,22 +265,55 @@ static struct fsentry *fsentry_create_list(struct fscache *cache, const struct f
240
265
241
266
/* walk directory and build linked list of fsentry structures */
242
267
phead = & list -> next ;
243
- do {
244
- * phead = fseentry_create_entry (cache , list , & fdata );
268
+ status = NtQueryDirectoryFile (h , NULL , 0 , 0 , & iosb , cache -> buffer ,
269
+ sizeof (cache -> buffer ), FileFullDirectoryInformation , FALSE, NULL , FALSE);
270
+ if (!NT_SUCCESS (status )) {
271
+ /*
272
+ * NtQueryDirectoryFile returns STATUS_INVALID_PARAMETER when
273
+ * asked to enumerate an invalid directory (ie it is a file
274
+ * instead of a directory). Verify that is the actual cause
275
+ * of the error.
276
+ */
277
+ if (status == STATUS_INVALID_PARAMETER ) {
278
+ DWORD attributes = GetFileAttributesW (pattern );
279
+ if (!(attributes & FILE_ATTRIBUTE_DIRECTORY ))
280
+ status = ERROR_DIRECTORY ;
281
+ }
282
+ goto Error ;
283
+ }
284
+ di = (PFILE_FULL_DIR_INFORMATION )(cache -> buffer );
285
+ for (;;) {
286
+
287
+ * phead = fseentry_create_entry (cache , list , di );
245
288
phead = & (* phead )-> next ;
246
- } while (FindNextFileW (h , & fdata ));
247
289
248
- /* remember result of last FindNextFile, then close find handle */
249
- err = GetLastError ();
250
- FindClose (h );
290
+ /* If there is no offset in the entry, the buffer has been exhausted. */
291
+ if (di -> NextEntryOffset == 0 ) {
292
+ status = NtQueryDirectoryFile (h , NULL , 0 , 0 , & iosb , cache -> buffer ,
293
+ sizeof (cache -> buffer ), FileFullDirectoryInformation , FALSE, NULL , FALSE);
294
+ if (!NT_SUCCESS (status )) {
295
+ if (status == STATUS_NO_MORE_FILES )
296
+ break ;
297
+ goto Error ;
298
+ }
299
+
300
+ di = (PFILE_FULL_DIR_INFORMATION )(cache -> buffer );
301
+ continue ;
302
+ }
303
+
304
+ /* Advance to the next entry. */
305
+ di = (PFILE_FULL_DIR_INFORMATION )(((PUCHAR )di ) + di -> NextEntryOffset );
306
+ }
251
307
252
- /* return the list if we've got all the files */
253
- if (err == ERROR_NO_MORE_FILES )
254
- return list ;
308
+ CloseHandle (h );
309
+ return list ;
255
310
256
- /* otherwise release the list and return error */
311
+ Error :
312
+ errno = (status == ERROR_DIRECTORY ) ? ENOTDIR : err_win_to_posix (status );
313
+ trace_printf_key (& trace_fscache , "fscache: error(%d) unable to query directory contents '%.*s'\n" ,
314
+ errno , dir -> len , dir -> name );
315
+ CloseHandle (h );
257
316
fsentry_release (list );
258
- errno = err_win_to_posix (err );
259
317
return NULL ;
260
318
}
261
319
0 commit comments