diff options
author | Ron Yorston <rmy@pobox.com> | 2019-03-05 09:57:42 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2019-03-05 09:57:42 +0000 |
commit | 4ed3d0d07004f3ae7e92b030973a608c5b800d8f (patch) | |
tree | e5d9dd9670435298bf99657cac63ace7a9ce516f | |
parent | 325fee1f922150460547d1317b3abd443a657385 (diff) | |
download | busybox-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.c | 92 |
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 | ||
484 | static 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 | */ |
486 | static int do_lstat(int follow, const char *file_name, struct mingw_stat *buf) | 500 | static 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, |