Skip to content

Commit

Permalink
Win32: implement stat() with symlink support
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
kblees committed May 15, 2015
1 parent 643694d commit b908441
Showing 1 changed file with 48 additions and 16 deletions.
64 changes: 48 additions & 16 deletions compat/mingw.c
Original file line number Diff line number Diff line change
Expand Up @@ -569,16 +569,60 @@ int mingw_lstat(const char *file_name, struct stat *buf)
{
return do_lstat(0, file_name, buf);
}

static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
{
BY_HANDLE_FILE_INFORMATION fdata;
if (!GetFileInformationByHandle(hnd, &fdata)) {
errno = err_win_to_posix(GetLastError());
return -1;
}
buf->st_ino = 0;
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
buf->st_gid = buf->st_uid = 0;
buf->st_nlink = 1;
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
buf->st_size = fdata.nFileSizeLow | (((off_t) fdata.nFileSizeHigh) << 32);
buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
return 0;
}

int mingw_stat(const char *file_name, struct stat *buf)
{
return do_lstat(1, file_name, buf);
wchar_t wfile_name[MAX_LONG_PATH];
HANDLE hnd;
int result;

/* if symlinks are disabled, use lstat() (without following links) */
if (!has_symlinks) {
result = lstat(file_name, buf);
if (!result && S_ISLNK(buf->st_mode)) {
errno = ELOOP;
return -1;
}
return result;
}

/* otherwise just open the file and let Windows resolve the links */
if (xutftowcs_long_path(wfile_name, file_name) < 0)
return -1;
hnd = CreateFileW(wfile_name, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (hnd == INVALID_HANDLE_VALUE) {
errno = err_win_to_posix(GetLastError());
return -1;
}
result = get_file_info_by_handle(hnd, buf);
CloseHandle(hnd);
return result;
}

int mingw_fstat(int fd, struct stat *buf)
{
HANDLE fh = (HANDLE)_get_osfhandle(fd);
BY_HANDLE_FILE_INFORMATION fdata;

if (fh == INVALID_HANDLE_VALUE) {
errno = EBADF;
return -1;
Expand All @@ -587,20 +631,8 @@ int mingw_fstat(int fd, struct stat *buf)
if (GetFileType(fh) != FILE_TYPE_DISK)
return _fstati64(fd, buf);

if (GetFileInformationByHandle(fh, &fdata)) {
buf->st_ino = 0;
buf->st_gid = 0;
buf->st_uid = 0;
buf->st_nlink = 1;
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
buf->st_size = fdata.nFileSizeLow |
(((off_t)fdata.nFileSizeHigh)<<32);
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
if (!get_file_info_by_handle(fh, buf))
return 0;
}
errno = EBADF;
return -1;
}
Expand Down

0 comments on commit b908441

Please sign in to comment.