Skip to content

Commit b908441

Browse files
committed
Win32: implement stat() with symlink support
With respect to symlinks, the current stat() implementation is almost the same as lstat(): except for the file type (st_mode & S_IFMT), it returns information about the link rather than the target. Implement stat by opening the file with as little permissions as possible and calling GetFileInformationByHandle on it. This way, all link resoltion is handled by the Windows file system layer. If symlinks are disabled, use lstat() as before, but fail with ELOOP if a symlink would have to be resolved. Signed-off-by: Karsten Blees <[email protected]>
1 parent 643694d commit b908441

File tree

1 file changed

+48
-16
lines changed

1 file changed

+48
-16
lines changed

compat/mingw.c

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -569,16 +569,60 @@ int mingw_lstat(const char *file_name, struct stat *buf)
569569
{
570570
return do_lstat(0, file_name, buf);
571571
}
572+
573+
static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
574+
{
575+
BY_HANDLE_FILE_INFORMATION fdata;
576+
if (!GetFileInformationByHandle(hnd, &fdata)) {
577+
errno = err_win_to_posix(GetLastError());
578+
return -1;
579+
}
580+
buf->st_ino = 0;
581+
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
582+
buf->st_gid = buf->st_uid = 0;
583+
buf->st_nlink = 1;
584+
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
585+
buf->st_size = fdata.nFileSizeLow | (((off_t) fdata.nFileSizeHigh) << 32);
586+
buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
587+
buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
588+
buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
589+
return 0;
590+
}
591+
572592
int mingw_stat(const char *file_name, struct stat *buf)
573593
{
574-
return do_lstat(1, file_name, buf);
594+
wchar_t wfile_name[MAX_LONG_PATH];
595+
HANDLE hnd;
596+
int result;
597+
598+
/* if symlinks are disabled, use lstat() (without following links) */
599+
if (!has_symlinks) {
600+
result = lstat(file_name, buf);
601+
if (!result && S_ISLNK(buf->st_mode)) {
602+
errno = ELOOP;
603+
return -1;
604+
}
605+
return result;
606+
}
607+
608+
/* otherwise just open the file and let Windows resolve the links */
609+
if (xutftowcs_long_path(wfile_name, file_name) < 0)
610+
return -1;
611+
hnd = CreateFileW(wfile_name, 0,
612+
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
613+
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
614+
if (hnd == INVALID_HANDLE_VALUE) {
615+
errno = err_win_to_posix(GetLastError());
616+
return -1;
617+
}
618+
result = get_file_info_by_handle(hnd, buf);
619+
CloseHandle(hnd);
620+
return result;
575621
}
576622

577623
int mingw_fstat(int fd, struct stat *buf)
578624
{
579625
HANDLE fh = (HANDLE)_get_osfhandle(fd);
580-
BY_HANDLE_FILE_INFORMATION fdata;
581-
582626
if (fh == INVALID_HANDLE_VALUE) {
583627
errno = EBADF;
584628
return -1;
@@ -587,20 +631,8 @@ int mingw_fstat(int fd, struct stat *buf)
587631
if (GetFileType(fh) != FILE_TYPE_DISK)
588632
return _fstati64(fd, buf);
589633

590-
if (GetFileInformationByHandle(fh, &fdata)) {
591-
buf->st_ino = 0;
592-
buf->st_gid = 0;
593-
buf->st_uid = 0;
594-
buf->st_nlink = 1;
595-
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
596-
buf->st_size = fdata.nFileSizeLow |
597-
(((off_t)fdata.nFileSizeHigh)<<32);
598-
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
599-
buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
600-
buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
601-
buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
634+
if (!get_file_info_by_handle(fh, buf))
602635
return 0;
603-
}
604636
errno = EBADF;
605637
return -1;
606638
}

0 commit comments

Comments
 (0)