aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2019-03-05 09:57:42 +0000
committerRon Yorston <rmy@pobox.com>2019-03-05 09:57:42 +0000
commit4ed3d0d07004f3ae7e92b030973a608c5b800d8f (patch)
treee5d9dd9670435298bf99657cac63ace7a9ce516f
parent325fee1f922150460547d1317b3abd443a657385 (diff)
downloadbusybox-w32-4ed3d0d07004f3ae7e92b030973a608c5b800d8f.tar.gz
busybox-w32-4ed3d0d07004f3ae7e92b030973a608c5b800d8f.tar.bz2
busybox-w32-4ed3d0d07004f3ae7e92b030973a608c5b800d8f.zip
win32: improved results for symlinks from stat(2)
The file size and times reported when Windows follows a symlink are incorrect. To get the correct values canonicalize the path and try again. Also fetch the correct device id and inode for symlinks.
-rw-r--r--win32/mingw.c92
1 files changed, 56 insertions, 36 deletions
diff --git a/win32/mingw.c b/win32/mingw.c
index 9d9935f4b..11cef0abe 100644
--- a/win32/mingw.c
+++ b/win32/mingw.c
@@ -138,6 +138,7 @@ int err_win_to_posix(DWORD winerr)
138 case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break; 138 case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break;
139 case ERROR_WRITE_FAULT: error = EIO; break; 139 case ERROR_WRITE_FAULT: error = EIO; break;
140 case ERROR_WRITE_PROTECT: error = EROFS; break; 140 case ERROR_WRITE_PROTECT: error = EROFS; break;
141 case ERROR_CANT_RESOLVE_FILENAME: error = ELOOP; break;
141 } 142 }
142 return error; 143 return error;
143} 144}
@@ -480,60 +481,80 @@ static uid_t file_owner(HANDLE fh)
480} 481}
481#endif 482#endif
482 483
484static int is_symlink(DWORD attr, const char *pathname, WIN32_FIND_DATAA *fbuf)
485{
486 if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
487 HANDLE handle = FindFirstFileA(pathname, fbuf);
488 if (handle != INVALID_HANDLE_VALUE) {
489 FindClose(handle);
490 return ((fbuf->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
491 fbuf->dwReserved0 == IO_REPARSE_TAG_SYMLINK);
492 }
493 }
494 return 0;
495}
496
483/* If follow is true then act like stat() and report on the link 497/* If follow is true then act like stat() and report on the link
484 * target. Otherwise report on the link itself. 498 * target. Otherwise report on the link itself.
485 */ 499 */
486static int do_lstat(int follow, const char *file_name, struct mingw_stat *buf) 500static int do_lstat(int follow, const char *file_name, struct mingw_stat *buf)
487{ 501{
488 int err; 502 int err = EINVAL;
489 WIN32_FILE_ATTRIBUTE_DATA fdata; 503 WIN32_FILE_ATTRIBUTE_DATA fdata;
504 WIN32_FIND_DATAA findbuf;
490#if ENABLE_FEATURE_EXTRA_FILE_DATA 505#if ENABLE_FEATURE_EXTRA_FILE_DATA
506 DWORD flags;
491 BY_HANDLE_FILE_INFORMATION hdata; 507 BY_HANDLE_FILE_INFORMATION hdata;
492 HANDLE fh; 508 HANDLE fh;
493#endif 509#endif
494 510
495 if (!(err = get_file_attr(file_name, &fdata))) { 511 while (file_name && !(err=get_file_attr(file_name, &fdata))) {
496 buf->st_ino = 0; 512 buf->st_ino = 0;
497 buf->st_uid = DEFAULT_UID; 513 buf->st_uid = DEFAULT_UID;
498 buf->st_gid = DEFAULT_GID; 514 buf->st_gid = DEFAULT_GID;
499 buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes); 515 buf->st_dev = buf->st_rdev = 0;
500 buf->st_nlink = S_ISDIR(buf->st_mode) ? 2 : 1; 516
501 if (S_ISREG(buf->st_mode) && 517 if (is_symlink(fdata.dwFileAttributes, file_name, &findbuf)) {
502 (has_exe_suffix(file_name) || has_exec_format(file_name))) 518 char *name = auto_string(xmalloc_realpath(file_name));
503 buf->st_mode |= S_IXUSR|S_IXGRP|S_IXOTH; 519
504 buf->st_size = fdata.nFileSizeLow | 520 if (follow) {
505 (((off64_t)fdata.nFileSizeHigh)<<32); 521 /* The file size and times are wrong when Windows follows
506 buf->st_dev = buf->st_rdev = 0; /* not used by Git */ 522 * a symlink. Use the canonicalized path to try again. */
507 buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); 523 err = errno;
508 buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); 524 file_name = name;
509 buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); 525 continue;
510 if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
511 WIN32_FIND_DATAA findbuf;
512 HANDLE handle = FindFirstFileA(file_name, &findbuf);
513 if (handle != INVALID_HANDLE_VALUE) {
514 if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
515 (findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
516 if (follow) {
517 char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
518 buf->st_size = readlink(file_name, buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
519 } else {
520 buf->st_mode = S_IFLNK;
521 }
522 buf->st_mode |= S_IRUSR|S_IRGRP|S_IROTH;
523 if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
524 buf->st_mode |= S_IWUSR|S_IWGRP;
525 }
526 FindClose(handle);
527 } 526 }
527
528 /* Get the contents of a symlink, not its target. */
529 buf->st_mode = S_IFLNK|S_IRWXU|S_IRWXG|S_IRWXO;
530 buf->st_size = name ? strlen(name) : 0; /* should use readlink */
531 buf->st_atime = filetime_to_time_t(&(findbuf.ftLastAccessTime));
532 buf->st_mtime = filetime_to_time_t(&(findbuf.ftLastWriteTime));
533 buf->st_ctime = filetime_to_time_t(&(findbuf.ftCreationTime));
528 } 534 }
535 else {
536 /* The file is not a symlink. */
537 buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
538 if (S_ISREG(buf->st_mode) &&
539 (has_exe_suffix(file_name) || has_exec_format(file_name)))
540 buf->st_mode |= S_IXUSR|S_IXGRP|S_IXOTH;
541 buf->st_size = fdata.nFileSizeLow |
542 (((off64_t)fdata.nFileSizeHigh)<<32);
543 buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
544 buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
545 buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
546 }
547 buf->st_nlink = S_ISDIR(buf->st_mode) ? 2 : 1;
529 548
530#if ENABLE_FEATURE_EXTRA_FILE_DATA 549#if ENABLE_FEATURE_EXTRA_FILE_DATA
550 flags = FILE_FLAG_BACKUP_SEMANTICS;
551 if (S_ISLNK(buf->st_mode))
552 flags |= FILE_FLAG_OPEN_REPARSE_POINT;
531#if ENABLE_FEATURE_IDENTIFY_OWNER 553#if ENABLE_FEATURE_IDENTIFY_OWNER
532 fh = CreateFile(file_name, READ_CONTROL, 0, NULL, 554 fh = CreateFile(file_name, READ_CONTROL, 0, NULL,
533 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 555 OPEN_EXISTING, flags, NULL);
534#else 556#else
535 fh = CreateFile(file_name, 0, 0, NULL, OPEN_EXISTING, 557 fh = CreateFile(file_name, 0, 0, NULL, OPEN_EXISTING, flags, NULL);
536 FILE_FLAG_BACKUP_SEMANTICS, NULL);
537#endif 558#endif
538 if (fh != INVALID_HANDLE_VALUE) { 559 if (fh != INVALID_HANDLE_VALUE) {
539 if (GetFileInformationByHandle(fh, &hdata)) { 560 if (GetFileInformationByHandle(fh, &hdata)) {
@@ -987,9 +1008,8 @@ static char *resolve_symlinks(char *path)
987 return NULL; 1008 return NULL;
988 1009
989 /* need a file handle to resolve symlinks */ 1010 /* need a file handle to resolve symlinks */
990 h = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, 1011 h = CreateFileA(path, 0, 0, NULL, OPEN_EXISTING,
991 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS, 1012 FILE_FLAG_BACKUP_SEMANTICS, NULL);
992 NULL);
993 if (h != INVALID_HANDLE_VALUE) { 1013 if (h != INVALID_HANDLE_VALUE) {
994 /* normalize the path and return it on success */ 1014 /* normalize the path and return it on success */
995 DWORD status = GetFinalPathNameByHandleA(h, path, MAX_PATH, 1015 DWORD status = GetFinalPathNameByHandleA(h, path, MAX_PATH,