diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index b7d80c0de..837178f89 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,13 @@ +2008-04-02 Corinna Vinschen + + * path.cc (symlink_info::check_reparse_point): Use NtFsControlFile + to read reparse points. + (enum shortcut_t): New type. + (symlink_info::check): Convert sym_check to shortcut_t. Use shortcut_t + enum values throughout. Check reparse point before checking for + sysfile shortcut. Open reparse points with READ_CONTROL only to + accommodate special Vista reparse points. Add comments. + 2008-04-02 Corinna Vinschen * flock.cc (get_lock_parent_dir): Drop call to NtOpenDirectoryObject diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index f92bf8fe7..b9191e252 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -3487,15 +3487,19 @@ symlink_info::check_sysfile (HANDLE h) int symlink_info::check_reparse_point (HANDLE h) { + NTSTATUS status; + IO_STATUS_BLOCK io; PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) alloca (MAXIMUM_REPARSE_DATA_BUFFER_SIZE); - DWORD size; char srcbuf[SYMLINK_MAX + 7]; - if (!DeviceIoControl (h, FSCTL_GET_REPARSE_POINT, NULL, 0, (LPVOID) rp, - MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &size, NULL)) + status = NtFsControlFile (h, NULL, NULL, NULL, &io, FSCTL_GET_REPARSE_POINT, + NULL, 0, (LPVOID) rp, + MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + if (!NT_SUCCESS (status)) { - debug_printf ("DeviceIoControl(FSCTL_GET_REPARSE_POINT) failed, %E"); + debug_printf ("NtFsControlFile(FSCTL_GET_REPARSE_POINT) failed, %p", + status); set_error (EIO); return 0; } @@ -3774,6 +3778,13 @@ symlink_info::parse_device (const char *contents) Return -1 on error, 0 if PATH is not a symlink, or the length stored into BUF if PATH is a symlink. */ +enum shortcut_t { + is_no_symlink, + is_shortcut_symlink, + is_reparse_symlink, + is_sysfile_symlink +}; + int symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt) { @@ -3896,9 +3907,9 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt) || (opt & PC_SYM_IGNORE)) goto file_not_symlink; - int sym_check; + shortcut_t sym_check; - sym_check = 0; + sym_check = is_no_symlink; if ((fileattr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) == FILE_ATTRIBUTE_DIRECTORY) @@ -3907,35 +3918,45 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt) /* Windows shortcuts are potentially treated as symlinks. */ /* Valid Cygwin & U/WIN shortcuts are R/O. */ if ((fileattr & FILE_ATTRIBUTE_READONLY) && suffix.lnk_match ()) - sym_check = 1; + sym_check = is_shortcut_symlink; + + /* Reparse points are potentially symlinks. This check must be + performed before checking the SYSTEM attribute for sysfile + symlinks, since reparse points can have this flag set, too. + For instance, Vista starts to create a couple of reparse points + with SYSTEM and HIDDEN flags set. */ + else if (fileattr & FILE_ATTRIBUTE_REPARSE_POINT) + sym_check = is_reparse_symlink; /* 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). */ else if (fileattr & FILE_ATTRIBUTE_SYSTEM) - sym_check = 2; + sym_check = is_sysfile_symlink; - /* Reparse points are potentially symlinks. */ - else if (fileattr & FILE_ATTRIBUTE_REPARSE_POINT) - sym_check = 3; - - if (!sym_check) + if (sym_check == is_no_symlink) goto file_not_symlink; res = -1; - /* Open the file. */ - status = NtOpenFile (&h, FILE_GENERIC_READ, &attr, &io, - FILE_SHARE_VALID_FLAGS, - FILE_SYNCHRONOUS_IO_NONALERT - | FILE_OPEN_FOR_BACKUP_INTENT - | (sym_check == 3 ? FILE_OPEN_REPARSE_POINT : 0)); + /* Open the file. Opening reparse points must not use GENERIC_READ. + The reason is that Vista starts to create a couple of reparse + points for backward compatibility, hidden system files, explicitely + denying everyone FILE_READ_DATA access. */ + status = NtOpenFile (&h, + sym_check == is_reparse_symlink + ? READ_CONTROL : FILE_GENERIC_READ, + &attr, &io, FILE_SHARE_VALID_FLAGS, + FILE_OPEN_FOR_BACKUP_INTENT + | (sym_check == is_reparse_symlink + ? FILE_OPEN_REPARSE_POINT + : FILE_SYNCHRONOUS_IO_NONALERT)); if (!NT_SUCCESS (status)) goto file_not_symlink; switch (sym_check) { - case 1: + case is_shortcut_symlink: res = check_shortcut (h); NtClose (h); if (!res) @@ -3951,18 +3972,20 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt) fileattr = INVALID_FILE_ATTRIBUTES; continue; /* in case we're going to tack *another* .lnk on this filename. */ - case 2: - res = check_sysfile (h); - NtClose (h); - if (!res) - goto file_not_symlink; - break; - case 3: + case is_reparse_symlink: res = check_reparse_point (h); NtClose (h); if (!res) goto file_not_symlink; break; + case is_sysfile_symlink: + res = check_sysfile (h); + NtClose (h); + if (!res) + goto file_not_symlink; + break; + default: /* Make gcc happy. Won't happen. */ + goto file_not_symlink; } break;