mirror of
https://github.com/git-for-windows/git.git
synced 2026-05-31 02:17:14 -05:00
mingw: lstat: compute correct size for symlinks
This commit fixes mingw_lstat by computing the proper size for symlinks according to POSIX. POSIX specifies that upon successful return from lstat: "the value of the st_size member shall be set to the length of the pathname contained in the symbolic link not including any terminating null byte". Prior to this commit the mingw_lstat function returned a fixed size of 4096. This caused problems in git repositories that were accessed by git for Cygwin or git for WSL. For example, doing `git reset --hard` using git for Windows would update the size of symlinks in the index to be 4096; at a later time git for Cygwin or git for WSL would find that symlinks have changed size during `git status`. Vice versa doing `git reset --hard` in git for Cygwin or git for WSL would update the size of symlinks in the index with the correct value, only for git for Windows to find incorrectly at a later time that the size had changed. Signed-off-by: Bill Zissimopoulos <billziss@navimatics.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
committed by
Johannes Schindelin
parent
6fa7f5368c
commit
16c8396db8
@@ -962,10 +962,14 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int readlink_1(const WCHAR *wpath, BOOL fail_on_unknown_tag,
|
||||||
|
char *tmpbuf, int *plen, DWORD *ptag);
|
||||||
|
|
||||||
int mingw_lstat(const char *file_name, struct stat *buf)
|
int mingw_lstat(const char *file_name, struct stat *buf)
|
||||||
{
|
{
|
||||||
WIN32_FILE_ATTRIBUTE_DATA fdata;
|
WIN32_FILE_ATTRIBUTE_DATA fdata;
|
||||||
WIN32_FIND_DATAW findbuf = { 0 };
|
DWORD reparse_tag = 0;
|
||||||
|
int link_len = 0;
|
||||||
wchar_t wfilename[MAX_LONG_PATH];
|
wchar_t wfilename[MAX_LONG_PATH];
|
||||||
int wlen = xutftowcs_long_path(wfilename, file_name);
|
int wlen = xutftowcs_long_path(wfilename, file_name);
|
||||||
if (wlen < 0)
|
if (wlen < 0)
|
||||||
@@ -980,20 +984,21 @@ int mingw_lstat(const char *file_name, struct stat *buf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
|
if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
|
||||||
/* for reparse points, use FindFirstFile to get the reparse tag */
|
/* for reparse points, get the link tag and length */
|
||||||
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
||||||
HANDLE handle = FindFirstFileW(wfilename, &findbuf);
|
char tmpbuf[MAX_LONG_PATH];
|
||||||
if (handle == INVALID_HANDLE_VALUE)
|
|
||||||
goto error;
|
if (readlink_1(wfilename, FALSE, tmpbuf, &link_len,
|
||||||
FindClose(handle);
|
&reparse_tag) < 0)
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
buf->st_ino = 0;
|
buf->st_ino = 0;
|
||||||
buf->st_gid = 0;
|
buf->st_gid = 0;
|
||||||
buf->st_uid = 0;
|
buf->st_uid = 0;
|
||||||
buf->st_nlink = 1;
|
buf->st_nlink = 1;
|
||||||
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes,
|
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes,
|
||||||
findbuf.dwReserved0);
|
reparse_tag);
|
||||||
buf->st_size = S_ISLNK(buf->st_mode) ? MAX_LONG_PATH :
|
buf->st_size = S_ISLNK(buf->st_mode) ? link_len :
|
||||||
fdata.nFileSizeLow | (((off_t) fdata.nFileSizeHigh) << 32);
|
fdata.nFileSizeLow | (((off_t) fdata.nFileSizeHigh) << 32);
|
||||||
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
|
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
|
||||||
filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
|
filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
|
||||||
@@ -1001,7 +1006,7 @@ int mingw_lstat(const char *file_name, struct stat *buf)
|
|||||||
filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
|
filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
error:
|
|
||||||
switch (GetLastError()) {
|
switch (GetLastError()) {
|
||||||
case ERROR_ACCESS_DENIED:
|
case ERROR_ACCESS_DENIED:
|
||||||
case ERROR_SHARING_VIOLATION:
|
case ERROR_SHARING_VIOLATION:
|
||||||
@@ -3014,17 +3019,13 @@ typedef struct _REPARSE_DATA_BUFFER {
|
|||||||
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
|
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int readlink(const char *path, char *buf, size_t bufsiz)
|
static int readlink_1(const WCHAR *wpath, BOOL fail_on_unknown_tag,
|
||||||
|
char *tmpbuf, int *plen, DWORD *ptag)
|
||||||
{
|
{
|
||||||
HANDLE handle;
|
HANDLE handle;
|
||||||
WCHAR wpath[MAX_LONG_PATH], *wbuf;
|
WCHAR *wbuf;
|
||||||
REPARSE_DATA_BUFFER *b = alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
|
REPARSE_DATA_BUFFER *b = alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
|
||||||
DWORD dummy;
|
DWORD dummy;
|
||||||
char tmpbuf[MAX_LONG_PATH];
|
|
||||||
int len;
|
|
||||||
|
|
||||||
if (xutftowcs_long_path(wpath, path) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* read reparse point data */
|
/* read reparse point data */
|
||||||
handle = CreateFileW(wpath, 0,
|
handle = CreateFileW(wpath, 0,
|
||||||
@@ -3044,7 +3045,7 @@ int readlink(const char *path, char *buf, size_t bufsiz)
|
|||||||
CloseHandle(handle);
|
CloseHandle(handle);
|
||||||
|
|
||||||
/* get target path for symlinks or mount points (aka 'junctions') */
|
/* get target path for symlinks or mount points (aka 'junctions') */
|
||||||
switch (b->ReparseTag) {
|
switch ((*ptag = b->ReparseTag)) {
|
||||||
case IO_REPARSE_TAG_SYMLINK:
|
case IO_REPARSE_TAG_SYMLINK:
|
||||||
wbuf = (WCHAR*) (((char*) b->SymbolicLinkReparseBuffer.PathBuffer)
|
wbuf = (WCHAR*) (((char*) b->SymbolicLinkReparseBuffer.PathBuffer)
|
||||||
+ b->SymbolicLinkReparseBuffer.SubstituteNameOffset);
|
+ b->SymbolicLinkReparseBuffer.SubstituteNameOffset);
|
||||||
@@ -3058,10 +3059,34 @@ int readlink(const char *path, char *buf, size_t bufsiz)
|
|||||||
+ b->MountPointReparseBuffer.SubstituteNameLength) = 0;
|
+ b->MountPointReparseBuffer.SubstituteNameLength) = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
errno = EINVAL;
|
if (fail_on_unknown_tag) {
|
||||||
return -1;
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
*plen = MAX_LONG_PATH;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((*plen =
|
||||||
|
xwcstoutf(tmpbuf, normalize_ntpath(wbuf), MAX_LONG_PATH)) < 0)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int readlink(const char *path, char *buf, size_t bufsiz)
|
||||||
|
{
|
||||||
|
WCHAR wpath[MAX_LONG_PATH];
|
||||||
|
char tmpbuf[MAX_LONG_PATH];
|
||||||
|
int len;
|
||||||
|
DWORD tag;
|
||||||
|
|
||||||
|
if (xutftowcs_long_path(wpath, path) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (readlink_1(wpath, TRUE, tmpbuf, &len, &tag) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Adapt to strange readlink() API: Copy up to bufsiz *bytes*, potentially
|
* Adapt to strange readlink() API: Copy up to bufsiz *bytes*, potentially
|
||||||
* cutting off a UTF-8 sequence. Insufficient bufsize is *not* a failure
|
* cutting off a UTF-8 sequence. Insufficient bufsize is *not* a failure
|
||||||
@@ -3069,8 +3094,6 @@ int readlink(const char *path, char *buf, size_t bufsiz)
|
|||||||
* so convert to a (hopefully large enough) temporary buffer, then memcpy
|
* so convert to a (hopefully large enough) temporary buffer, then memcpy
|
||||||
* the requested number of bytes (including '\0' for robustness).
|
* the requested number of bytes (including '\0' for robustness).
|
||||||
*/
|
*/
|
||||||
if ((len = xwcstoutf(tmpbuf, normalize_ntpath(wbuf), MAX_LONG_PATH)) < 0)
|
|
||||||
return -1;
|
|
||||||
memcpy(buf, tmpbuf, min(bufsiz, len + 1));
|
memcpy(buf, tmpbuf, min(bufsiz, len + 1));
|
||||||
return min(bufsiz, len);
|
return min(bufsiz, len);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -584,6 +584,18 @@ int fscache_lstat(const char *filename, struct stat *st)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Special case symbolic links: FindFirstFile()/FindNextFile() did not
|
||||||
|
* provide us with the length of the target path.
|
||||||
|
*/
|
||||||
|
if (fse->u.s.st_size == MAX_LONG_PATH && S_ISLNK(fse->st_mode)) {
|
||||||
|
char buf[MAX_LONG_PATH];
|
||||||
|
int len = readlink(filename, buf, sizeof(buf) - 1);
|
||||||
|
|
||||||
|
if (len > 0)
|
||||||
|
fse->u.s.st_size = len;
|
||||||
|
}
|
||||||
|
|
||||||
/* copy stat data */
|
/* copy stat data */
|
||||||
st->st_ino = 0;
|
st->st_ino = 0;
|
||||||
st->st_gid = 0;
|
st->st_gid = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user