Skip to content

Commit 1c380ec

Browse files
committed
Merge pull request #2637 from billziss-gh/master
mingw: lstat: compute correct size for symlinks
2 parents 48049cf + 2d3b20e commit 1c380ec

File tree

1 file changed

+44
-21
lines changed

1 file changed

+44
-21
lines changed

compat/mingw.c

+44-21
Original file line numberDiff line numberDiff line change
@@ -958,10 +958,14 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
958958
return 1;
959959
}
960960

961+
static int readlink_1(const WCHAR *wpath, BOOL fail_on_unknown_tag,
962+
char *tmpbuf, int *plen, DWORD *ptag);
963+
961964
int mingw_lstat(const char *file_name, struct stat *buf)
962965
{
963966
WIN32_FILE_ATTRIBUTE_DATA fdata;
964-
WIN32_FIND_DATAW findbuf = { 0 };
967+
DWORD reparse_tag = 0;
968+
int link_len = 0;
965969
wchar_t wfilename[MAX_LONG_PATH];
966970
int wlen = xutftowcs_long_path(wfilename, file_name);
967971
if (wlen < 0)
@@ -976,28 +980,29 @@ int mingw_lstat(const char *file_name, struct stat *buf)
976980
}
977981

978982
if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
979-
/* for reparse points, use FindFirstFile to get the reparse tag */
983+
/* for reparse points, get the link tag and length */
980984
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
981-
HANDLE handle = FindFirstFileW(wfilename, &findbuf);
982-
if (handle == INVALID_HANDLE_VALUE)
983-
goto error;
984-
FindClose(handle);
985+
char tmpbuf[MAX_LONG_PATH];
986+
987+
if (readlink_1(wfilename, FALSE, tmpbuf, &link_len,
988+
&reparse_tag) < 0)
989+
return -1;
985990
}
986991
buf->st_ino = 0;
987992
buf->st_gid = 0;
988993
buf->st_uid = 0;
989994
buf->st_nlink = 1;
990995
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes,
991-
findbuf.dwReserved0, file_name);
992-
buf->st_size = S_ISLNK(buf->st_mode) ? MAX_LONG_PATH :
996+
reparse_tag, file_name);
997+
buf->st_size = S_ISLNK(buf->st_mode) ? link_len :
993998
fdata.nFileSizeLow | (((off_t) fdata.nFileSizeHigh) << 32);
994999
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
9951000
filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
9961001
filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
9971002
filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
9981003
return 0;
9991004
}
1000-
error:
1005+
10011006
switch (GetLastError()) {
10021007
case ERROR_ACCESS_DENIED:
10031008
case ERROR_SHARING_VIOLATION:
@@ -2923,17 +2928,13 @@ typedef struct _REPARSE_DATA_BUFFER {
29232928
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
29242929
#endif
29252930

2926-
int readlink(const char *path, char *buf, size_t bufsiz)
2931+
static int readlink_1(const WCHAR *wpath, BOOL fail_on_unknown_tag,
2932+
char *tmpbuf, int *plen, DWORD *ptag)
29272933
{
29282934
HANDLE handle;
2929-
WCHAR wpath[MAX_LONG_PATH], *wbuf;
2935+
WCHAR *wbuf;
29302936
REPARSE_DATA_BUFFER *b = alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
29312937
DWORD dummy;
2932-
char tmpbuf[MAX_LONG_PATH];
2933-
int len;
2934-
2935-
if (xutftowcs_long_path(wpath, path) < 0)
2936-
return -1;
29372938

29382939
/* read reparse point data */
29392940
handle = CreateFileW(wpath, 0,
@@ -2953,7 +2954,7 @@ int readlink(const char *path, char *buf, size_t bufsiz)
29532954
CloseHandle(handle);
29542955

29552956
/* get target path for symlinks or mount points (aka 'junctions') */
2956-
switch (b->ReparseTag) {
2957+
switch ((*ptag = b->ReparseTag)) {
29572958
case IO_REPARSE_TAG_SYMLINK:
29582959
wbuf = (WCHAR*) (((char*) b->SymbolicLinkReparseBuffer.PathBuffer)
29592960
+ b->SymbolicLinkReparseBuffer.SubstituteNameOffset);
@@ -2967,19 +2968,41 @@ int readlink(const char *path, char *buf, size_t bufsiz)
29672968
+ b->MountPointReparseBuffer.SubstituteNameLength) = 0;
29682969
break;
29692970
default:
2970-
errno = EINVAL;
2971-
return -1;
2971+
if (fail_on_unknown_tag) {
2972+
errno = EINVAL;
2973+
return -1;
2974+
} else {
2975+
*plen = MAX_LONG_PATH;
2976+
return 0;
2977+
}
29722978
}
29732979

2980+
if ((*plen =
2981+
xwcstoutf(tmpbuf, normalize_ntpath(wbuf), MAX_LONG_PATH)) < 0)
2982+
return -1;
2983+
return 0;
2984+
}
2985+
2986+
int readlink(const char *path, char *buf, size_t bufsiz)
2987+
{
2988+
WCHAR wpath[MAX_LONG_PATH];
2989+
char tmpbuf[MAX_LONG_PATH];
2990+
int len;
2991+
DWORD tag;
2992+
2993+
if (xutftowcs_long_path(wpath, path) < 0)
2994+
return -1;
2995+
2996+
if (readlink_1(wpath, TRUE, tmpbuf, &len, &tag) < 0)
2997+
return -1;
2998+
29742999
/*
29753000
* Adapt to strange readlink() API: Copy up to bufsiz *bytes*, potentially
29763001
* cutting off a UTF-8 sequence. Insufficient bufsize is *not* a failure
29773002
* condition. There is no conversion function that produces invalid UTF-8,
29783003
* so convert to a (hopefully large enough) temporary buffer, then memcpy
29793004
* the requested number of bytes (including '\0' for robustness).
29803005
*/
2981-
if ((len = xwcstoutf(tmpbuf, normalize_ntpath(wbuf), MAX_LONG_PATH)) < 0)
2982-
return -1;
29833006
memcpy(buf, tmpbuf, min(bufsiz, len + 1));
29843007
return min(bufsiz, len);
29853008
}

0 commit comments

Comments
 (0)