From 9740f34d11c458fd7a07a025810422a6db1ad374 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen <corinna@vinschen.de> Date: Tue, 31 Oct 2006 11:40:47 +0000 Subject: [PATCH] * fhandler_disk_file.cc (fhandler_base::fstat_by_handle): Drop directory attribute for reparse points to avoid mistreating. (fhandler_base::fstat_by_name): Ditto. * path.cc (symlink_info::check_reparse_point): New method testing reparse points for symbolic links. (symlink_info::check_shortcut): Move file attribute tesat to calling function. (symlink_info::check): Add handling for reparse points. * path.h (enum path_types): Add PATH_REP to denote reparse point based symlinks. (path_conv::is_rep_symlink): New method. * syscalls.cc (unlink): Handle reparse points. --- winsup/cygwin/ChangeLog | 15 +++++ winsup/cygwin/fhandler_disk_file.cc | 10 +++- winsup/cygwin/path.cc | 87 +++++++++++++++++++++++++---- winsup/cygwin/path.h | 2 + winsup/cygwin/syscalls.cc | 12 +++- 5 files changed, 112 insertions(+), 14 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 1487544c6..fddfd6bf7 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,18 @@ +2006-10-31 Corinna Vinschen <corinna@vinschen.de> + + * fhandler_disk_file.cc (fhandler_base::fstat_by_handle): Drop + directory attribute for reparse points to avoid mistreating. + (fhandler_base::fstat_by_name): Ditto. + * path.cc (symlink_info::check_reparse_point): New method testing + reparse points for symbolic links. + (symlink_info::check_shortcut): Move file attribute tesat to calling + function. + (symlink_info::check): Add handling for reparse points. + * path.h (enum path_types): Add PATH_REP to denote reparse point based + symlinks. + (path_conv::is_rep_symlink): New method. + * syscalls.cc (unlink): Handle reparse points. + 2006-10-27 Corinna Vinschen <corinna@vinschen.de> * shared.cc (open_shared): Drop useless attempt from 2006-08-11. diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc index da358bd1d..f3b5ec03e 100644 --- a/winsup/cygwin/fhandler_disk_file.cc +++ b/winsup/cygwin/fhandler_disk_file.cc @@ -240,6 +240,8 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf) /* If the change time is 0, it's a file system which doesn't support a change timestamp. In that case use the LastWriteTime entry, as in other calls to fstat_helper. */ + if (pc.is_rep_symlink ()) + pfai->BasicInformation.FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY; pc.file_attributes (pfai->BasicInformation.FileAttributes); return fstat_helper (buf, pfai->BasicInformation.ChangeTime.QuadPart ? @@ -275,7 +277,11 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf) local.dwFileAttributes = DWORD (pc); } else - pc.file_attributes (local.dwFileAttributes); + { + if (pc.is_rep_symlink ()) + local.dwFileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY; + pc.file_attributes (local.dwFileAttributes); + } return fstat_helper (buf, local.ftLastWriteTime, /* see fstat_helper comment */ local.ftLastAccessTime, @@ -306,6 +312,8 @@ fhandler_base::fstat_by_name (struct __stat64 *buf) else if ((handle = FindFirstFile (pc, &local)) != INVALID_HANDLE_VALUE) { FindClose (handle); + if (pc.is_rep_symlink ()) + local.dwFileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY; pc.file_attributes (local.dwFileAttributes); res = fstat_helper (buf, local.ftLastWriteTime, /* see fstat_helper comment */ diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index cfe0735c5..f32cfb4c8 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -104,6 +104,7 @@ struct symlink_info bool case_check (char *path); int check_sysfile (const char *path, HANDLE h); int check_shortcut (const char *path, HANDLE h); + int check_reparse_point (const char *path, HANDLE h); bool set_error (int); }; @@ -2996,10 +2997,6 @@ symlink_info::check_shortcut (const char *path, HANDLE h) int res = 0; DWORD size, got = 0; - /* Valid Cygwin & U/WIN shortcuts are R/O. */ - if (!(fileattr & FILE_ATTRIBUTE_READONLY)) - goto file_not_symlink; - if ((size = GetFileSize (h, NULL)) > 8192) /* Not a Cygwin symlink. */ goto file_not_symlink; buf = (char *) alloca (size); @@ -3033,7 +3030,6 @@ close_it: return res; } - int symlink_info::check_sysfile (const char *path, HANDLE h) { @@ -3093,6 +3089,61 @@ symlink_info::check_sysfile (const char *path, HANDLE h) return res; } +int +symlink_info::check_reparse_point (const char *path, HANDLE h) +{ + int res = 0; + PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) + alloca (MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + DWORD size; + + if (!DeviceIoControl (h, FSCTL_GET_REPARSE_POINT, NULL, 0, (LPVOID) rp, + MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &size, NULL)) + { + debug_printf ("DeviceIoControl(FSCTL_GET_REPARSE_POINT) failed, %E"); + set_error (EIO); + goto close_it; + } + if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK) + { + if (rp->SymbolicLinkReparseBuffer.PrintNameLength > 2 * CYG_MAX_PATH) + { + debug_printf ("Symlink name too long"); + set_error (EIO); + goto close_it; + } + res = sys_wcstombs (contents, CYG_MAX_PATH, + (WCHAR *)((char *)rp->SymbolicLinkReparseBuffer.PathBuffer + + rp->SymbolicLinkReparseBuffer.PrintNameOffset), + rp->SymbolicLinkReparseBuffer.PrintNameLength / 2); + pflags = PATH_SYMLINK | PATH_REP; + fileattr &= ~FILE_ATTRIBUTE_DIRECTORY; + } + else if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) + { + if (rp->SymbolicLinkReparseBuffer.PrintNameLength == 0) + { + /* Likely a volume mount point. Not treated as symlink. */ + goto close_it; + } + if (rp->MountPointReparseBuffer.PrintNameLength > 2 * CYG_MAX_PATH) + { + debug_printf ("Symlink name too long"); + set_error (EIO); + goto close_it; + } + res = sys_wcstombs (contents, CYG_MAX_PATH, + (WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer + + rp->MountPointReparseBuffer.PrintNameOffset), + rp->MountPointReparseBuffer.PrintNameLength / 2); + pflags = PATH_SYMLINK | PATH_REP; + fileattr &= ~FILE_ATTRIBUTE_DIRECTORY; + } +close_it: + CloseHandle (h); + return res; +} + enum { SCAN_BEG, @@ -3337,26 +3388,35 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt) sym_check = 0; - if (fileattr & FILE_ATTRIBUTE_DIRECTORY) + if ((fileattr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) + == FILE_ATTRIBUTE_DIRECTORY) goto file_not_symlink; - /* Windows shortcuts are treated as symlinks. */ - if (suffix.lnk_match ()) - sym_check = 1; + /* Reparse points are potentially symlinks. */ + if (fileattr & FILE_ATTRIBUTE_REPARSE_POINT) + sym_check = 3; /* This is the old Cygwin method creating symlinks: */ /* A symlink will have the `system' file attribute. */ /* Only files can be symlinks (which can be symlinks to directories). */ - if (fileattr & FILE_ATTRIBUTE_SYSTEM) + else if (fileattr & FILE_ATTRIBUTE_SYSTEM) sym_check = 2; + /* Windows shortcuts are potentially treated as symlinks. */ + /* Valid Cygwin & U/WIN shortcuts are R/O. */ + else if ((fileattr & FILE_ATTRIBUTE_READONLY) && suffix.lnk_match ()) + sym_check = 1; + if (!sym_check) goto file_not_symlink; /* Open the file. */ h = CreateFile (suffix.path, GENERIC_READ, FILE_SHARE_READ, - &sec_none_nih, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + &sec_none_nih, OPEN_EXISTING, + sym_check == 3 ? FILE_FLAG_OPEN_REPARSE_POINT + | FILE_FLAG_BACKUP_SEMANTICS + : FILE_ATTRIBUTE_NORMAL, 0); res = -1; if (h == INVALID_HANDLE_VALUE) goto file_not_symlink; @@ -3383,6 +3443,11 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt) if (!res) goto file_not_symlink; break; + case 3: + res = check_reparse_point (suffix.path, h); + if (!res) + goto file_not_symlink; + break; } break; diff --git a/winsup/cygwin/path.h b/winsup/cygwin/path.h index b00adc76d..0b2decc0d 100644 --- a/winsup/cygwin/path.h +++ b/winsup/cygwin/path.h @@ -76,6 +76,7 @@ enum path_types PATH_NO_ACCESS_CHECK = PC_NO_ACCESS_CHECK, PATH_LNK = 0x01000000, PATH_TEXT = 0x02000000, + PATH_REP = 0x04000000, PATH_HAS_SYMLINKS = 0x10000000, PATH_SOCKET = 0x40000000 }; @@ -163,6 +164,7 @@ class path_conv } int issymlink () const {return path_flags & PATH_SYMLINK;} int is_lnk_symlink () const {return path_flags & PATH_LNK;} + int is_rep_symlink () const {return path_flags & PATH_REP;} int isdevice () const {return dev.devn && dev.devn != FH_FS && dev.devn != FH_FIFO;} int isfifo () const {return dev == FH_FIFO;} int isspecial () const {return dev.devn && dev.devn != FH_FS;} diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 66839be95..eb07890a1 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -205,8 +205,11 @@ unlink (const char *ourname) if (!win32_name.isremote () && wincap.has_delete_on_close ()) { HANDLE h; + DWORD flags = FILE_FLAG_DELETE_ON_CLOSE; + if (win32_name.is_rep_symlink ()) + flags |= FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS; h = CreateFile (win32_name, 0, FILE_SHARE_READ, &sec_none_nih, - OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0); + OPEN_EXISTING, flags, 0); if (h != INVALID_HANDLE_VALUE) { if (wincap.has_hard_links () && setattrs) @@ -229,7 +232,12 @@ unlink (const char *ourname) } /* Try a delete with attributes reset */ - if (DeleteFile (win32_name)) + if (win32_name.is_rep_symlink () && RemoveDirectory (win32_name)) + { + syscall_printf ("RemoveDirectory after CreateFile/CloseHandle succeeded"); + goto ok; + } + else if (DeleteFile (win32_name)) { syscall_printf ("DeleteFile after CreateFile/CloseHandle succeeded"); goto ok;