Skip to content

Commit 2dbd943

Browse files
authored
Merge pull request #2637 from billziss-gh/master
mingw: lstat: compute correct size for symlinks
2 parents 01c50ad + 2f55cc4 commit 2dbd943

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
@@ -969,10 +969,14 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
969969
return 1;
970970
}
971971

972+
static int readlink_1(const WCHAR *wpath, BOOL fail_on_unknown_tag,
973+
char *tmpbuf, int *plen, DWORD *ptag);
974+
972975
int mingw_lstat(const char *file_name, struct stat *buf)
973976
{
974977
WIN32_FILE_ATTRIBUTE_DATA fdata;
975-
WIN32_FIND_DATAW findbuf = { 0 };
978+
DWORD reparse_tag = 0;
979+
int link_len = 0;
976980
wchar_t wfilename[MAX_LONG_PATH];
977981
int wlen = xutftowcs_long_path(wfilename, file_name);
978982
if (wlen < 0)
@@ -987,28 +991,29 @@ int mingw_lstat(const char *file_name, struct stat *buf)
987991
}
988992

989993
if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
990-
/* for reparse points, use FindFirstFile to get the reparse tag */
994+
/* for reparse points, get the link tag and length */
991995
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
992-
HANDLE handle = FindFirstFileW(wfilename, &findbuf);
993-
if (handle == INVALID_HANDLE_VALUE)
994-
goto error;
995-
FindClose(handle);
996+
char tmpbuf[MAX_LONG_PATH];
997+
998+
if (readlink_1(wfilename, FALSE, tmpbuf, &link_len,
999+
&reparse_tag) < 0)
1000+
return -1;
9961001
}
9971002
buf->st_ino = 0;
9981003
buf->st_gid = 0;
9991004
buf->st_uid = 0;
10001005
buf->st_nlink = 1;
10011006
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes,
1002-
findbuf.dwReserved0, file_name);
1003-
buf->st_size = S_ISLNK(buf->st_mode) ? MAX_LONG_PATH :
1007+
reparse_tag, file_name);
1008+
buf->st_size = S_ISLNK(buf->st_mode) ? link_len :
10041009
fdata.nFileSizeLow | (((off_t) fdata.nFileSizeHigh) << 32);
10051010
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
10061011
filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
10071012
filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
10081013
filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
10091014
return 0;
10101015
}
1011-
error:
1016+
10121017
switch (GetLastError()) {
10131018
case ERROR_ACCESS_DENIED:
10141019
case ERROR_SHARING_VIOLATION:
@@ -2968,17 +2973,13 @@ typedef struct _REPARSE_DATA_BUFFER {
29682973
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
29692974
#endif
29702975

2971-
int readlink(const char *path, char *buf, size_t bufsiz)
2976+
static int readlink_1(const WCHAR *wpath, BOOL fail_on_unknown_tag,
2977+
char *tmpbuf, int *plen, DWORD *ptag)
29722978
{
29732979
HANDLE handle;
2974-
WCHAR wpath[MAX_LONG_PATH], *wbuf;
2980+
WCHAR *wbuf;
29752981
REPARSE_DATA_BUFFER *b = alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
29762982
DWORD dummy;
2977-
char tmpbuf[MAX_LONG_PATH];
2978-
int len;
2979-
2980-
if (xutftowcs_long_path(wpath, path) < 0)
2981-
return -1;
29822983

29832984
/* read reparse point data */
29842985
handle = CreateFileW(wpath, 0,
@@ -2998,7 +2999,7 @@ int readlink(const char *path, char *buf, size_t bufsiz)
29982999
CloseHandle(handle);
29993000

30003001
/* get target path for symlinks or mount points (aka 'junctions') */
3001-
switch (b->ReparseTag) {
3002+
switch ((*ptag = b->ReparseTag)) {
30023003
case IO_REPARSE_TAG_SYMLINK:
30033004
wbuf = (WCHAR*) (((char*) b->SymbolicLinkReparseBuffer.PathBuffer)
30043005
+ b->SymbolicLinkReparseBuffer.SubstituteNameOffset);
@@ -3012,19 +3013,41 @@ int readlink(const char *path, char *buf, size_t bufsiz)
30123013
+ b->MountPointReparseBuffer.SubstituteNameLength) = 0;
30133014
break;
30143015
default:
3015-
errno = EINVAL;
3016-
return -1;
3016+
if (fail_on_unknown_tag) {
3017+
errno = EINVAL;
3018+
return -1;
3019+
} else {
3020+
*plen = MAX_LONG_PATH;
3021+
return 0;
3022+
}
30173023
}
30183024

3025+
if ((*plen =
3026+
xwcstoutf(tmpbuf, normalize_ntpath(wbuf), MAX_LONG_PATH)) < 0)
3027+
return -1;
3028+
return 0;
3029+
}
3030+
3031+
int readlink(const char *path, char *buf, size_t bufsiz)
3032+
{
3033+
WCHAR wpath[MAX_LONG_PATH];
3034+
char tmpbuf[MAX_LONG_PATH];
3035+
int len;
3036+
DWORD tag;
3037+
3038+
if (xutftowcs_long_path(wpath, path) < 0)
3039+
return -1;
3040+
3041+
if (readlink_1(wpath, TRUE, tmpbuf, &len, &tag) < 0)
3042+
return -1;
3043+
30193044
/*
30203045
* Adapt to strange readlink() API: Copy up to bufsiz *bytes*, potentially
30213046
* cutting off a UTF-8 sequence. Insufficient bufsize is *not* a failure
30223047
* condition. There is no conversion function that produces invalid UTF-8,
30233048
* so convert to a (hopefully large enough) temporary buffer, then memcpy
30243049
* the requested number of bytes (including '\0' for robustness).
30253050
*/
3026-
if ((len = xwcstoutf(tmpbuf, normalize_ntpath(wbuf), MAX_LONG_PATH)) < 0)
3027-
return -1;
30283051
memcpy(buf, tmpbuf, min(bufsiz, len + 1));
30293052
return min(bufsiz, len);
30303053
}

0 commit comments

Comments
 (0)