Skip to content

Commit 643694d

Browse files
committed
Win32: don't call GetFileAttributes twice in mingw_lstat()
GetFileAttributes cannot handle paths with trailing dir separator. The current [l]stat implementation calls GetFileAttributes twice if the path has trailing slashes (first with the original path passed to [l]stat, and and a second time with a path copy with trailing '/' removed). With Unicode conversion, we get the length of the path for free and also have a (wide char) buffer that can be modified. Remove trailing directory separators before calling the Win32 API. Signed-off-by: Karsten Blees <[email protected]>
1 parent 6b1da48 commit 643694d

File tree

1 file changed

+12
-36
lines changed

1 file changed

+12
-36
lines changed

compat/mingw.c

Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -498,9 +498,18 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
498498
{
499499
WIN32_FILE_ATTRIBUTE_DATA fdata;
500500
wchar_t wfilename[MAX_LONG_PATH];
501-
if (xutftowcs_long_path(wfilename, file_name) < 0)
501+
int wlen = xutftowcs_long_path(wfilename, file_name);
502+
if (wlen < 0)
502503
return -1;
503504

505+
/* strip trailing '/', or GetFileAttributes will fail */
506+
while (wlen && is_dir_sep(wfilename[wlen - 1]))
507+
wfilename[--wlen] = 0;
508+
if (!wlen) {
509+
errno = ENOENT;
510+
return -1;
511+
}
512+
504513
if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
505514
buf->st_ino = 0;
506515
buf->st_gid = 0;
@@ -554,48 +563,15 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
554563
return -1;
555564
}
556565

557-
/* We provide our own lstat/fstat functions, since the provided
558-
* lstat/fstat functions are so slow. These stat functions are
559-
* tailored for Git's usage (read: fast), and are not meant to be
560-
* complete. Note that Git stat()s are redirected to mingw_lstat()
561-
* too, since Windows doesn't really handle symlinks that well.
562-
*/
563-
static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
564-
{
565-
int namelen;
566-
char alt_name[MAX_LONG_PATH];
567-
568-
if (!do_lstat(follow, file_name, buf))
569-
return 0;
570-
571-
/* if file_name ended in a '/', Windows returned ENOENT;
572-
* try again without trailing slashes
573-
*/
574-
if (errno != ENOENT)
575-
return -1;
576-
577-
namelen = strlen(file_name);
578-
if (namelen && file_name[namelen-1] != '/')
579-
return -1;
580-
while (namelen && file_name[namelen-1] == '/')
581-
--namelen;
582-
if (!namelen || namelen >= MAX_LONG_PATH)
583-
return -1;
584-
585-
memcpy(alt_name, file_name, namelen);
586-
alt_name[namelen] = 0;
587-
return do_lstat(follow, alt_name, buf);
588-
}
589-
590566
int (*lstat)(const char *file_name, struct stat *buf) = mingw_lstat;
591567

592568
int mingw_lstat(const char *file_name, struct stat *buf)
593569
{
594-
return do_stat_internal(0, file_name, buf);
570+
return do_lstat(0, file_name, buf);
595571
}
596572
int mingw_stat(const char *file_name, struct stat *buf)
597573
{
598-
return do_stat_internal(1, file_name, buf);
574+
return do_lstat(1, file_name, buf);
599575
}
600576

601577
int mingw_fstat(int fd, struct stat *buf)

0 commit comments

Comments
 (0)