44#include "fscache.h"
55#include "config.h"
66#include "../../mem-pool.h"
7+ #include "ntifs.h"
78
89static volatile long initialized ;
910static DWORD dwTlsIndex ;
@@ -23,6 +24,7 @@ struct fscache {
2324 unsigned int opendir_requests ;
2425 unsigned int fscache_requests ;
2526 unsigned int fscache_misses ;
27+ WCHAR buffer [64 * 1024 ];
2628};
2729static struct trace_key trace_fscache = TRACE_KEY_INIT (FSCACHE );
2830
@@ -145,16 +147,30 @@ static void fsentry_release(struct fsentry *fse)
145147 InterlockedDecrement (& (fse -> refcnt ));
146148}
147149
150+ static int xwcstoutfn (char * utf , int utflen , const wchar_t * wcs , int wcslen )
151+ {
152+ if (!wcs || !utf || utflen < 1 ) {
153+ errno = EINVAL ;
154+ return -1 ;
155+ }
156+ utflen = WideCharToMultiByte (CP_UTF8 , 0 , wcs , wcslen , utf , utflen , NULL , NULL );
157+ if (utflen )
158+ return utflen ;
159+ errno = ERANGE ;
160+ return -1 ;
161+ }
162+
148163/*
149- * Allocate and initialize an fsentry from a WIN32_FIND_DATA structure.
164+ * Allocate and initialize an fsentry from a FILE_FULL_DIR_INFORMATION structure.
150165 */
151166static struct fsentry * fseentry_create_entry (struct fscache * cache , struct fsentry * list ,
152- const WIN32_FIND_DATAW * fdata )
167+ PFILE_FULL_DIR_INFORMATION fdata )
153168{
154169 char buf [MAX_PATH * 3 ];
155170 int len ;
156171 struct fsentry * fse ;
157- len = xwcstoutf (buf , fdata -> cFileName , ARRAY_SIZE (buf ));
172+
173+ len = xwcstoutfn (buf , ARRAY_SIZE (buf ), fdata -> FileName , fdata -> FileNameLength / sizeof (wchar_t ));
158174
159175 fse = fsentry_alloc (cache , list , buf , len );
160176
@@ -167,7 +183,8 @@ static struct fsentry *fseentry_create_entry(struct fscache *cache, struct fsent
167183 * Let's work around this by detecting that situation and
168184 * telling Git that these are *not* symbolic links.
169185 */
170- if (fdata -> dwReserved0 == IO_REPARSE_TAG_SYMLINK &&
186+ if (fdata -> FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
187+ fdata -> EaSize == IO_REPARSE_TAG_SYMLINK &&
171188 sizeof (buf ) > (list ? list -> len + 1 : 0 ) + fse -> len + 1 &&
172189 is_inside_windows_container ()) {
173190 size_t off = 0 ;
@@ -180,13 +197,13 @@ static struct fsentry *fseentry_create_entry(struct fscache *cache, struct fsent
180197 buf [off + fse -> len ] = '\0' ;
181198 }
182199
183- fse -> st_mode = file_attr_to_st_mode (fdata -> dwFileAttributes ,
184- fdata -> dwReserved0 , buf );
200+ fse -> st_mode = file_attr_to_st_mode (fdata -> FileAttributes ,
201+ fdata -> EaSize , buf );
185202 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 ));
203+ fdata -> EndOfFile . LowPart | (((off_t )fdata -> EndOfFile . HighPart ) << 32 );
204+ filetime_to_timespec (( FILETIME * ) & (fdata -> LastAccessTime ), & (fse -> st_atim ));
205+ filetime_to_timespec (( FILETIME * ) & (fdata -> LastWriteTime ), & (fse -> st_mtim ));
206+ filetime_to_timespec (( FILETIME * ) & (fdata -> CreationTime ), & (fse -> st_ctim ));
190207
191208 return fse ;
192209}
@@ -199,8 +216,10 @@ static struct fsentry *fseentry_create_entry(struct fscache *cache, struct fsent
199216static struct fsentry * fsentry_create_list (struct fscache * cache , const struct fsentry * dir ,
200217 int * dir_not_found )
201218{
202- wchar_t pattern [MAX_LONG_PATH + 2 ]; /* + 2 for "\*" */
203- WIN32_FIND_DATAW fdata ;
219+ wchar_t pattern [MAX_LONG_PATH ];
220+ NTSTATUS status ;
221+ IO_STATUS_BLOCK iosb ;
222+ PFILE_FULL_DIR_INFORMATION di ;
204223 HANDLE h ;
205224 int wlen ;
206225 struct fsentry * list , * * phead ;
@@ -213,18 +232,18 @@ static struct fsentry *fsentry_create_list(struct fscache *cache, const struct f
213232 dir -> len , MAX_PATH - 2 , core_long_paths )) < 0 )
214233 return NULL ;
215234
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 );
235+ /* handle CWD */
236+ if (! wlen ) {
237+ wlen = GetCurrentDirectoryW ( ARRAY_SIZE ( pattern ), pattern );
238+ if (! wlen || wlen >= ARRAY_SIZE ( pattern )) {
239+ errno = wlen ? ENAMETOOLONG : err_win_to_posix ( GetLastError ());
240+ return NULL ;
241+ }
242+ }
243+
244+ h = CreateFileW ( pattern , FILE_LIST_DIRECTORY ,
245+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE ,
246+ NULL , OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
228247 if (h == INVALID_HANDLE_VALUE ) {
229248 err = GetLastError ();
230249 * dir_not_found = 1 ; /* or empty directory */
@@ -240,22 +259,55 @@ static struct fsentry *fsentry_create_list(struct fscache *cache, const struct f
240259
241260 /* walk directory and build linked list of fsentry structures */
242261 phead = & list -> next ;
243- do {
244- * phead = fseentry_create_entry (cache , list , & fdata );
262+ status = NtQueryDirectoryFile (h , NULL , 0 , 0 , & iosb , cache -> buffer ,
263+ sizeof (cache -> buffer ), FileFullDirectoryInformation , FALSE, NULL , FALSE);
264+ if (!NT_SUCCESS (status )) {
265+ /*
266+ * NtQueryDirectoryFile returns STATUS_INVALID_PARAMETER when
267+ * asked to enumerate an invalid directory (ie it is a file
268+ * instead of a directory). Verify that is the actual cause
269+ * of the error.
270+ */
271+ if (status == STATUS_INVALID_PARAMETER ) {
272+ DWORD attributes = GetFileAttributesW (pattern );
273+ if (!(attributes & FILE_ATTRIBUTE_DIRECTORY ))
274+ status = ERROR_DIRECTORY ;
275+ }
276+ goto Error ;
277+ }
278+ di = (PFILE_FULL_DIR_INFORMATION )(cache -> buffer );
279+ for (;;) {
280+
281+ * phead = fseentry_create_entry (cache , list , di );
245282 phead = & (* phead )-> next ;
246- } while (FindNextFileW (h , & fdata ));
247283
248- /* remember result of last FindNextFile, then close find handle */
249- err = GetLastError ();
250- FindClose (h );
284+ /* If there is no offset in the entry, the buffer has been exhausted. */
285+ if (di -> NextEntryOffset == 0 ) {
286+ status = NtQueryDirectoryFile (h , NULL , 0 , 0 , & iosb , cache -> buffer ,
287+ sizeof (cache -> buffer ), FileFullDirectoryInformation , FALSE, NULL , FALSE);
288+ if (!NT_SUCCESS (status )) {
289+ if (status == STATUS_NO_MORE_FILES )
290+ break ;
291+ goto Error ;
292+ }
293+
294+ di = (PFILE_FULL_DIR_INFORMATION )(cache -> buffer );
295+ continue ;
296+ }
297+
298+ /* Advance to the next entry. */
299+ di = (PFILE_FULL_DIR_INFORMATION )(((PUCHAR )di ) + di -> NextEntryOffset );
300+ }
251301
252- /* return the list if we've got all the files */
253- if (err == ERROR_NO_MORE_FILES )
254- return list ;
302+ CloseHandle (h );
303+ return list ;
255304
256- /* otherwise release the list and return error */
305+ Error :
306+ errno = (status == ERROR_DIRECTORY ) ? ENOTDIR : err_win_to_posix (status );
307+ trace_printf_key (& trace_fscache , "fscache: error(%d) unable to query directory contents '%.*s'\n" ,
308+ errno , dir -> len , dir -> name );
309+ CloseHandle (h );
257310 fsentry_release (list );
258- errno = err_win_to_posix (err );
259311 return NULL ;
260312}
261313
0 commit comments