OSDN Git Service

* dtable.cc (dtable::dup_worker): Reset path_conv handle in duplicated
authorcorinna <corinna>
Tue, 15 Jun 2010 12:05:13 +0000 (12:05 +0000)
committercorinna <corinna>
Tue, 15 Jun 2010 12:05:13 +0000 (12:05 +0000)
fhandler.
* fhandler.cc (fhandler_base::fstatvfs): Keep handle in created
path_conv.
* fhandler.h (fhandler_base::get_stat_access): New method.
(fhandler_base::get_stat_handle): New method.
* fhandler_disk_file.cc (fhandler_base::fstat_by_handle): Use handle
returned by get_stat_handle.  Only request inode from system if it
isn't already set in the fhandler, and only for filesystems supporting
them.
(fhandler_base::fstat_fs): Use handle returned by get_stat_handle.
Change the way open_fs is called.  Explain why.
(fhandler_base::fstat_helper): Use handle returned by get_stat_handle.
Never use 0 inode number.  Simplify executable recognition by re-using
get_stat_handle if file could be opened with sufficient rights.
(fhandler_disk_file::fstatvfs): Use handle returned by get_stat_handle.
(fhandler_disk_file::facl): Use handle returned by get_stat_handle in
GETACL and GETACLCNT cases.
(fhandler_disk_file::link): Use handle returned by get_stat_handle
instead of opening file here again.  Add comment.
(readdir_get_ino): Keep handle in created path_conv and drop
opening file.
* ntdll.h (wait_pending): New helper function.
* path.cc (symlink_info::check): Drop unused 'opt' parameter from
declaration.  Add path_conv_handle argument.
(path_conv::check): Make sure conv_handle is closed.  Keep
PC_KEEP_HANDLE flag in pflags_or.  Accommodate call to sym.check to
new args.
(path_conv::~path_conv): Close conv_handle.
(symlink_info::check_shortcut): Don't re-open file here, just use
incoming handle.  Drop goto's and label out.
(symlink_info::check_sysfile): Don't re-open file here, just use
incoming handle.  Keep track of file position to accommodate the fact
that file has been opened asynchronously in calling function.
(symlink_info::check_nfs_symlink): Don't re-open file here, just use
incoming handle.
(symlink_info::check): Drop unused 'opt' parameter.  Add
path_conv_handle argument.  Always try to open file with GENERIC_READ
rights first to allow reading file content w/o having to re-open the
file.  Drop back to READ_CONTROL | FILE_READ_ATTRIBUTES otherwise.
Call symlink test functions (except for check_reparse_point) only if
file could be opened with GENERIC_READ.  Keep file handle open if
PC_KEEP_HANDLE is set in pflags.
* path.h (enum pathconv_arg): Add PC_KEEP_HANDLE flag.
(class path_conv_handle): New class.
(class path_conv): Add conv_handle member.
(path_conv::operator =): Duplicate conv_handle.
(path_conv::handle): New method.
(path_conv::access): New method.
(path_conv::reset_conv_handle): New method.
(path_conv::close_conv_handle): New method.

winsup/cygwin/ChangeLog
winsup/cygwin/dtable.cc
winsup/cygwin/fhandler.cc
winsup/cygwin/fhandler.h
winsup/cygwin/fhandler_disk_file.cc
winsup/cygwin/ntdll.h
winsup/cygwin/path.cc
winsup/cygwin/path.h
winsup/cygwin/sec_acl.cc
winsup/cygwin/syscalls.cc

index 89ff9f8..2bdbf73 100644 (file)
@@ -1,5 +1,59 @@
 2010-06-15  Corinna Vinschen  <corinna@vinschen.de>
 
+       * dtable.cc (dtable::dup_worker): Reset path_conv handle in duplicated
+       fhandler.
+       * fhandler.cc (fhandler_base::fstatvfs): Keep handle in created
+       path_conv.
+       * fhandler.h (fhandler_base::get_stat_access): New method.
+       (fhandler_base::get_stat_handle): New method.
+       * fhandler_disk_file.cc (fhandler_base::fstat_by_handle): Use handle
+       returned by get_stat_handle.  Only request inode from system if it
+       isn't already set in the fhandler, and only for filesystems supporting
+       them.
+       (fhandler_base::fstat_fs): Use handle returned by get_stat_handle.
+       Change the way open_fs is called.  Explain why.
+       (fhandler_base::fstat_helper): Use handle returned by get_stat_handle.
+       Never use 0 inode number.  Simplify executable recognition by re-using
+       get_stat_handle if file could be opened with sufficient rights.
+       (fhandler_disk_file::fstatvfs): Use handle returned by get_stat_handle.
+       (fhandler_disk_file::facl): Use handle returned by get_stat_handle in
+       GETACL and GETACLCNT cases.
+       (fhandler_disk_file::link): Use handle returned by get_stat_handle
+       instead of opening file here again.  Add comment.
+       (readdir_get_ino): Keep handle in created path_conv and drop
+       opening file.
+       * ntdll.h (wait_pending): New helper function.
+       * path.cc (symlink_info::check): Drop unused 'opt' parameter from
+       declaration.  Add path_conv_handle argument.
+       (path_conv::check): Make sure conv_handle is closed.  Keep
+       PC_KEEP_HANDLE flag in pflags_or.  Accommodate call to sym.check to
+       new args.
+       (path_conv::~path_conv): Close conv_handle.
+       (symlink_info::check_shortcut): Don't re-open file here, just use
+       incoming handle.  Drop goto's and label out.
+       (symlink_info::check_sysfile): Don't re-open file here, just use
+       incoming handle.  Keep track of file position to accommodate the fact
+       that file has been opened asynchronously in calling function.
+       (symlink_info::check_nfs_symlink): Don't re-open file here, just use
+       incoming handle.
+       (symlink_info::check): Drop unused 'opt' parameter.  Add
+       path_conv_handle argument.  Always try to open file with GENERIC_READ
+       rights first to allow reading file content w/o having to re-open the
+       file.  Drop back to READ_CONTROL | FILE_READ_ATTRIBUTES otherwise.
+       Call symlink test functions (except for check_reparse_point) only if
+       file could be opened with GENERIC_READ.  Keep file handle open if
+       PC_KEEP_HANDLE is set in pflags.
+       * path.h (enum pathconv_arg): Add PC_KEEP_HANDLE flag.
+       (class path_conv_handle): New class.
+       (class path_conv): Add conv_handle member.
+       (path_conv::operator =): Duplicate conv_handle.
+       (path_conv::handle): New method.
+       (path_conv::access): New method.
+       (path_conv::reset_conv_handle): New method.
+       (path_conv::close_conv_handle): New method.
+
+2010-06-15  Corinna Vinschen  <corinna@vinschen.de>
+
        * fhandler_disk_file.cc (fhandler_disk_file::fstatvfs): Fix indentation.
 
 2010-06-15  Corinna Vinschen  <corinna@vinschen.de>
index 7f52c97..332bc71 100644 (file)
@@ -582,6 +582,7 @@ dtable::dup_worker (fhandler_base *oldfh, int flags)
     {
       *newfh = *oldfh;
       newfh->set_io_handle (NULL);
+      newfh->pc.reset_conv_handle ();
       if (oldfh->dup (newfh))
        {
          delete newfh;
index 714c46a..bd4422a 100644 (file)
@@ -1124,7 +1124,7 @@ fhandler_base::fstatvfs (struct statvfs *sfs)
 {
   /* If we hit this base implementation, it's some device in /dev.
      Just call statvfs on /dev for simplicity. */
-  path_conv pc ("/dev");
+  path_conv pc ("/dev", PC_KEEP_HANDLE);
   fhandler_disk_file fh (pc);
   return fh.fstatvfs (sfs);
 }
index cce11aa..561ada1 100644 (file)
@@ -181,6 +181,7 @@ class fhandler_base
 
   int get_access () const { return access; }
   void set_access (int x) { access = x; }
+  int get_stat_access () const { return pc.handle () ? pc.access () : access; }
 
   int get_flags () { return openflags; }
   void set_flags (int x, int supplied_bin = 0);
@@ -355,6 +356,7 @@ class fhandler_base
   virtual HANDLE& get_handle () { return io_handle; }
   virtual HANDLE& get_io_handle () { return io_handle; }
   virtual HANDLE& get_output_handle () { return io_handle; }
+  virtual HANDLE get_stat_handle () { return pc.handle () ?: io_handle; }
   virtual bool hit_eof () {return false;}
   virtual select_record *select_read (select_stuff *);
   virtual select_record *select_write (select_stuff *);
index 55bc30c..e456b11 100644 (file)
@@ -333,6 +333,7 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf)
 {
   NTSTATUS status;
   IO_STATUS_BLOCK io;
+  HANDLE h = get_stat_handle ();
 
   if (pc.fs_is_nfs ())
     return fstat_by_nfs_ea (buf);
@@ -351,22 +352,22 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf)
 
   if (pc.has_buggy_basic_info ())
     {
-      status = NtQueryInformationFile (get_handle (), &io, &fi, sizeof fi,
+      status = NtQueryInformationFile (h, &io, &fi, sizeof fi,
                                       FileNetworkOpenInformation);
       /* The timestamps are in the same relative memory location, only
         the DOS attributes have to be moved. */
       fi.fbi.FileAttributes = fi.fnoi.FileAttributes;
     }
   else
-    status = NtQueryInformationFile (get_handle (), &io, &fi.fbi, sizeof fi.fbi,
-                                    FileBasicInformation);
+    status = NtQueryInformationFile (h, &io, &fi.fbi,
+                                    sizeof fi.fbi, FileBasicInformation);
   if (!NT_SUCCESS (status))
     {
       debug_printf ("%p = NtQueryInformationFile(%S, FileBasicInformation)",
                    status, pc.get_nt_native_path ());
       return -1;
     }
-  status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
+  status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
                                   FileStandardInformation);
   if (!NT_SUCCESS (status))
     {
@@ -374,13 +375,17 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf)
                    status, pc.get_nt_native_path ());
       return -1;
     }
-  status = NtQueryInformationFile (get_handle (), &io, &fii, sizeof fii,
-                                  FileInternalInformation);
-  if (!NT_SUCCESS (status))
+  if (!ino && pc.hasgood_inode ())
     {
-      debug_printf ("%p = NtQueryInformationFile(%S, FileInternalInformation)",
-                   status, pc.get_nt_native_path ());
-      return -1;
+      status = NtQueryInformationFile (h, &io, &fii, sizeof fii,
+                                      FileInternalInformation);
+      if (!NT_SUCCESS (status))
+       {
+         debug_printf ("%p = NtQueryInformationFile(%S, FileInternalInformation)",
+                       status, pc.get_nt_native_path ());
+         return -1;
+       }
+      ino = fii.FileId.QuadPart;
     }
   /* If the change time is 0, it's a file system which doesn't
      support a change timestamp.  In that case use the LastWriteTime
@@ -397,7 +402,7 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf)
                       get_dev (),
                       fsi.EndOfFile.QuadPart,
                       fsi.AllocationSize.QuadPart,
-                      fii.FileId.QuadPart,
+                      ino,
                       fsi.NumberOfLinks,
                       fi.fbi.FileAttributes);
 }
@@ -492,7 +497,7 @@ fhandler_base::fstat_fs (struct __stat64 *buf)
   int oret;
   int open_flags = O_RDONLY | O_BINARY;
 
-  if (get_handle ())
+  if (get_stat_handle ())
     {
       if (!nohandle () && !is_fs_special ())
        res = fstat_by_handle (buf);
@@ -500,8 +505,16 @@ fhandler_base::fstat_fs (struct __stat64 *buf)
        res = fstat_by_name (buf);
       return res;
     }
-  query_open (query_read_attributes);
+  /* First try to open with generic read access.  This allows to read the file
+     in fstat_helper (when checking for executability) without having to
+     re-open it.  Opening a file can take a lot of time on network drives
+     so we try to avoid that. */
   oret = open_fs (open_flags, 0);
+  if (!oret)
+    {
+      query_open (query_read_attributes);
+      oret = open_fs (open_flags, 0);
+    }
   if (oret)
     {
       /* We now have a valid handle, regardless of the "nohandle" state.
@@ -546,6 +559,7 @@ fhandler_base::fstat_helper (struct __stat64 *buf,
 {
   IO_STATUS_BLOCK st;
   FILE_COMPRESSION_INFORMATION fci;
+  HANDLE h = get_stat_handle ();
 
   to_timestruc_t ((PFILETIME) LastAccessTime, &buf->st_atim);
   to_timestruc_t ((PFILETIME) LastWriteTime, &buf->st_mtim);
@@ -563,7 +577,7 @@ fhandler_base::fstat_helper (struct __stat64 *buf,
 #endif
 
   /* Enforce namehash as inode number on untrusted file systems. */
-  if (pc.isgood_inode (nFileIndex))
+  if (nFileIndex && pc.isgood_inode (nFileIndex))
     buf->st_ino = (__ino64_t) nFileIndex;
   else
     buf->st_ino = get_ino ();
@@ -576,9 +590,9 @@ fhandler_base::fstat_helper (struct __stat64 *buf,
     buf->st_blocks = (nAllocSize + S_BLKSIZE - 1) / S_BLKSIZE;
   else if (::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_COMPRESSED
                                              | FILE_ATTRIBUTE_SPARSE_FILE)
-          && get_handle () && !is_fs_special ()
-          && !NtQueryInformationFile (get_handle (), &st, (PVOID) &fci,
-                                     sizeof fci, FileCompressionInformation))
+          && h && !is_fs_special ()
+          && !NtQueryInformationFile (h, &st, (PVOID) &fci, sizeof fci,
+                                      FileCompressionInformation))
     /* Otherwise we request the actual amount of bytes allocated for
        compressed and sparsed files. */
     buf->st_blocks = (fci.CompressedFileSize.QuadPart + S_BLKSIZE - 1)
@@ -597,15 +611,14 @@ fhandler_base::fstat_helper (struct __stat64 *buf,
       buf->st_size = pc.get_symlink_length ();
       /* symlinks are everything for everyone! */
       buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
-      get_file_attribute (get_handle (), pc, NULL,
+      get_file_attribute (h, pc, NULL,
                          &buf->st_uid, &buf->st_gid);
       goto done;
     }
   else if (pc.issocket ())
     buf->st_mode = S_IFSOCK;
 
-  if (!get_file_attribute (is_fs_special () && !pc.issocket ()
-                          ? NULL : get_handle (), pc,
+  if (!get_file_attribute (is_fs_special () && !pc.issocket () ? NULL : h, pc,
                           &buf->st_mode, &buf->st_uid, &buf->st_gid))
     {
       /* If read-only attribute is set, modify ntsec return value */
@@ -659,50 +672,26 @@ fhandler_base::fstat_helper (struct __stat64 *buf,
             shebang scripts. */
          if (pc.exec_state () == dont_know_if_executable)
            {
-             OBJECT_ATTRIBUTES attr;
+             LARGE_INTEGER off = { QuadPart:0LL };
+             char magic[3];
              NTSTATUS status;
-             HANDLE h;
              IO_STATUS_BLOCK io;
 
-             /* The NWFS implementation is frighteningly incomplete.  When
-                re-opening a file by handle, the subsequent NtReadFile
-                returns with the weird status STATUS_FILE_IS_A_DIRECTORY.
-                We're still using the re-open by handle method for all
-                other filesystems since it's 8-10% faster than opening
-                by name. */
-             if (pc.fs_is_nwfs ())
-               InitializeObjectAttributes (&attr, pc.get_nt_native_path (),
-                                           OBJ_CASE_INSENSITIVE, NULL, NULL)
-             else
-               InitializeObjectAttributes (&attr, &ro_u_empty, 0,
-                                           get_handle (), NULL);
-             status = NtOpenFile (&h, SYNCHRONIZE | FILE_READ_DATA,
-                                  &attr, &io, FILE_SHARE_VALID_FLAGS,
-                                  FILE_SYNCHRONOUS_IO_NONALERT);
-             if (NT_SUCCESS (status))
+             if (get_stat_access () & (GENERIC_READ | FILE_READ_DATA))
                {
-                 LARGE_INTEGER off = { QuadPart:0LL };
-                 char magic[3];
-
-                 status = NtReadFile (h, NULL, NULL, NULL, &io, magic,
-                                      3, &off, NULL);
-                 if (NT_SUCCESS (status))
-                   {
-                     if (has_exec_chars (magic, io.Information))
-                       {
-                         /* Heureka, it's an executable */
-                         pc.set_exec ();
-                         buf->st_mode |= STD_XBITS;
-                       }
-                   }
-                 else
+                 status = NtReadFile (h, NULL, NULL, NULL,
+                                      &io, magic, 3, &off, NULL);
+                 status = wait_pending (status, h, io);
+                 if (!NT_SUCCESS (status))
                    debug_printf ("%p = NtReadFile(%S)", status,
                                  pc.get_nt_native_path ());
-                 NtClose (h);
+                 else if (has_exec_chars (magic, io.Information))
+                   {
+                     /* Heureka, it's an executable */
+                     pc.set_exec ();
+                     buf->st_mode |= STD_XBITS;
+                   }
                }
-             else
-               debug_printf ("%p = NtOpenFile(%S)", status,
-                             pc.get_nt_native_path ());
            }
        }
       if (pc.exec_state () == is_executable)
@@ -740,7 +729,7 @@ fhandler_disk_file::fstatvfs (struct statvfs *sfs)
   IO_STATUS_BLOCK io;
   FILE_FS_FULL_SIZE_INFORMATION full_fsi;
   FILE_FS_SIZE_INFORMATION fsi;
-  HANDLE fh = get_handle ();
+  HANDLE fh = get_stat_handle ();
 
   if (!fh)
     {
@@ -1050,7 +1039,8 @@ cant_access_acl:
     }
   else
     {
-      if (!get_handle ())
+      if ((cmd == SETACL && !get_handle ())
+         || (cmd != SETACL && !get_stat_handle ()))
        {
          query_open (cmd == SETACL ? query_write_control : query_read_control);
          if (!(oret = open (O_BINARY, 0)))
@@ -1087,10 +1077,10 @@ cant_access_acl:
            if (!aclbufp)
              set_errno(EFAULT);
            else
-             res = getacl (get_handle (), pc, nentries, aclbufp);
+             res = getacl (get_stat_handle (), pc, nentries, aclbufp);
            break;
          case GETACLCNT:
-           res = getacl (get_handle (), pc, 0, NULL);
+           res = getacl (get_stat_handle (), pc, 0, NULL);
            break;
          default:
            set_errno (EINVAL);
@@ -1277,17 +1267,13 @@ fhandler_disk_file::link (const char *newpath)
        }
     }
 
-  HANDLE fh;
-  NTSTATUS status;
-  OBJECT_ATTRIBUTES attr;
-  IO_STATUS_BLOCK io;
-  status = NtOpenFile (&fh, READ_CONTROL,
-                      pc.get_object_attr (attr, sec_none_nih), &io,
-                      FILE_SHARE_VALID_FLAGS,
-                      FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
-  if (!NT_SUCCESS (status))
+  /* We only need READ_CONTROL access so the handle returned in pc is
+     sufficient.  And if the file couldn't be opened with READ_CONTROL
+     access in path_conv, we won't be able to do it here anyway. */
+  HANDLE fh = get_stat_handle ();
+  if (!fh)
     {
-      __seterrno_from_nt_status (status);
+      set_errno (EACCES);
       return -1;
     }
   PUNICODE_STRING tgt = newpc.get_nt_native_path ();
@@ -1296,8 +1282,10 @@ fhandler_disk_file::link (const char *newpath)
   pfli->ReplaceIfExists = FALSE;
   pfli->RootDirectory = NULL;
   memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length);
+
+  NTSTATUS status;
+  IO_STATUS_BLOCK io;
   status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
-  NtClose (fh);
   if (!NT_SUCCESS (status))
     {
       if (status == STATUS_INVALID_DEVICE_REQUEST)
@@ -1764,8 +1752,6 @@ readdir_get_ino (const char *path, bool dot_dot)
   char *fname;
   struct __stat64 st;
   HANDLE hdl;
-  OBJECT_ATTRIBUTES attr;
-  IO_STATUS_BLOCK io;
   __ino64_t ino = 0;
 
   if (dot_dot)
@@ -1777,7 +1763,7 @@ readdir_get_ino (const char *path, bool dot_dot)
       strcpy (c, "..");
       path = fname;
     }
-  path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_NOWARN);
+  path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_NOWARN | PC_KEEP_HANDLE);
   if (pc.isspecial ())
     {
       if (!stat_worker (pc, &st))
@@ -1785,17 +1771,11 @@ readdir_get_ino (const char *path, bool dot_dot)
     }
   else if (!pc.hasgood_inode ())
     ino = hash_path_name (0, pc.get_nt_native_path ());
-  else if (NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
-                                  pc.get_object_attr (attr, sec_none_nih),
-                                  &io, FILE_SHARE_VALID_FLAGS,
-                                  FILE_OPEN_FOR_BACKUP_INTENT
-                                  | (pc.is_rep_symlink ()
-                                     ? FILE_OPEN_REPARSE_POINT : 0))))
+  else if ((hdl = pc.handle ()) != NULL)
     {
       ino = pc.get_ino_by_handle (hdl);
       if (!ino)
        ino = hash_path_name (0, pc.get_nt_native_path ());
-      NtClose (hdl);
     }
   return ino;
 }
index 17ffb76..0b2229d 100644 (file)
@@ -868,6 +868,16 @@ typedef enum _EVENT_INFORMATION_CLASS
 #define NtCurrentProcess() ((HANDLE) 0xffffffff)
 #define NtCurrentThread()  ((HANDLE) 0xfffffffe)
 
+/* Helper macro for sync I/O with async handle. */
+inline NTSTATUS
+wait_pending (NTSTATUS status, HANDLE h, IO_STATUS_BLOCK &io)
+{
+  if (status != STATUS_PENDING)
+    return status;
+  WaitForSingleObject (h, INFINITE);
+  return io.Status;
+}
+
 extern "C"
 {
   NTSTATUS NTAPI NtAdjustPrivilegesToken (HANDLE, BOOLEAN, PTOKEN_PRIVILEGES,
index 8ea7e97..dadbeca 100644 (file)
@@ -96,8 +96,8 @@ struct symlink_info
   _major_t major;
   _minor_t minor;
   _mode_t mode;
-  int check (char *path, const suffix_info *suffixes, unsigned opt,
-            fs_info &fs);
+  int check (char *path, const suffix_info *suffixes, fs_info &fs,
+            path_conv_handle &conv_hdl);
   int set (char *path);
   bool parse_device (const char *);
   int check_sysfile (HANDLE h);
@@ -639,6 +639,7 @@ path_conv::check (const char *src, unsigned opt,
       cfree (modifiable_path ());
       path = NULL;
     }
+  close_conv_handle ();
   memset (&dev, 0, sizeof (dev));
   fs.clear ();
   if (normalized_path)
@@ -695,7 +696,9 @@ path_conv::check (const char *src, unsigned opt,
 
       int symlen = 0;
 
-      for (unsigned pflags_or = opt & PC_NO_ACCESS_CHECK; ; pflags_or = 0)
+      for (unsigned pflags_or = opt & (PC_NO_ACCESS_CHECK | PC_KEEP_HANDLE);
+          ;
+          pflags_or = 0)
        {
          const suffix_info *suff;
          char *full_path;
@@ -823,7 +826,7 @@ path_conv::check (const char *src, unsigned opt,
          if (is_msdos)
            sym.pflags |= PATH_NOPOSIX | PATH_NOACL;
 
-         symlen = sym.check (full_path, suff, opt, fs);
+         symlen = sym.check (full_path, suff, fs, conv_handle);
 
 is_virtual_symlink:
 
@@ -1124,6 +1127,7 @@ path_conv::~path_conv ()
       cfree (wide_path);
       wide_path = NULL;
     }
+  close_conv_handle ();
 }
 
 bool
@@ -1683,55 +1687,50 @@ cmp_shortcut_header (win_shortcut_hdr *file_header)
 }
 
 int
-symlink_info::check_shortcut (HANDLE in_h)
+symlink_info::check_shortcut (HANDLE h)
 {
   tmp_pathbuf tp;
   win_shortcut_hdr *file_header;
   char *buf, *cp;
   unsigned short len;
   int res = 0;
-  OBJECT_ATTRIBUTES attr;
   NTSTATUS status;
-  HANDLE h;
   IO_STATUS_BLOCK io;
   FILE_STANDARD_INFORMATION fsi;
+  LARGE_INTEGER off = { QuadPart:0LL };
 
-  InitializeObjectAttributes (&attr, &ro_u_empty, 0, in_h, NULL);
-  status = NtOpenFile (&h, FILE_READ_DATA | SYNCHRONIZE,
-                      &attr, &io, FILE_SHARE_VALID_FLAGS,
-                      FILE_OPEN_FOR_BACKUP_INTENT
-                      | FILE_SYNCHRONOUS_IO_NONALERT);
-  if (!NT_SUCCESS (status))
-    return 0;
   status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
                                   FileStandardInformation);
   if (!NT_SUCCESS (status))
     {
       set_error (EIO);
-      goto out;
+      return 0;
     }
   if (fsi.EndOfFile.QuadPart <= sizeof (win_shortcut_hdr)
       || fsi.EndOfFile.QuadPart > 4 * 65536)
-    goto out;
+    return 0;
   if (fsi.EndOfFile.LowPart < NT_MAX_PATH * sizeof (WCHAR))
     buf = (char *) tp.w_get ();
   else
     buf = (char *) alloca (fsi.EndOfFile.LowPart + 1);
-  if (!NT_SUCCESS (NtReadFile (h, NULL, NULL, NULL,
-                              &io, buf, fsi.EndOfFile.LowPart, NULL, NULL)))
+  status = NtReadFile (h, NULL, NULL, NULL, &io, buf, fsi.EndOfFile.LowPart,
+                      &off, NULL);
+  status = wait_pending (status, h, io);
+  if (!NT_SUCCESS (status))
     {
-      set_error (EIO);
-      goto out;
+      if (status != STATUS_END_OF_FILE)
+       set_error (EIO);
+      return 0;
     }
   file_header = (win_shortcut_hdr *) buf;
   if (io.Information != fsi.EndOfFile.LowPart
       || !cmp_shortcut_header (file_header))
-    goto out;
+    return 0;
   cp = buf + sizeof (win_shortcut_hdr);
   if (file_header->flags & WSH_FLAG_IDLIST) /* Skip ITEMIDLIST */
     cp += *(unsigned short *) cp + 2;
   if (!(len = *(unsigned short *) cp))
-    goto out;
+    return 0;
   cp += 2;
   /* Check if this is a device file - these start with the sequence :\\ */
   if (strncmp (cp, ":\\", 2) == 0)
@@ -1751,11 +1750,11 @@ symlink_info::check_shortcut (HANDLE in_h)
          char *tmpbuf = tp.c_get ();
          if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) (cp + 2))
              > SYMLINK_MAX + 1)
-           goto out;
+           return 0;
          res = posixify (tmpbuf);
        }
       else if (len > SYMLINK_MAX)
-       goto out;
+       return 0;
       else
        {
          cp[len] = '\0';
@@ -1764,41 +1763,33 @@ symlink_info::check_shortcut (HANDLE in_h)
     }
   if (res) /* It's a symlink.  */
     pflags |= PATH_SYMLINK | PATH_LNK;
-
-out:
-  NtClose (h);
   return res;
 }
 
 int
-symlink_info::check_sysfile (HANDLE in_h)
+symlink_info::check_sysfile (HANDLE h)
 {
   tmp_pathbuf tp;
   char cookie_buf[sizeof (SYMLINK_COOKIE) - 1];
   char *srcbuf = tp.c_get ();
   int res = 0;
-  OBJECT_ATTRIBUTES attr;
   NTSTATUS status;
-  HANDLE h;
   IO_STATUS_BLOCK io;
   bool interix_symlink = false;
+  LARGE_INTEGER off = { QuadPart:0LL };
 
-  InitializeObjectAttributes (&attr, &ro_u_empty, 0, in_h, NULL);
-  status = NtOpenFile (&h, FILE_READ_DATA | SYNCHRONIZE,
-                      &attr, &io, FILE_SHARE_VALID_FLAGS,
-                      FILE_OPEN_FOR_BACKUP_INTENT
-                      | FILE_SYNCHRONOUS_IO_NONALERT);
+  status = NtReadFile (h, NULL, NULL, NULL, &io, cookie_buf,
+                      sizeof (cookie_buf), &off, NULL);
+  status = wait_pending (status, h, io);
   if (!NT_SUCCESS (status))
-    return 0;
-  else if (!NT_SUCCESS (status = NtReadFile (h, NULL, NULL, NULL, &io,
-                                            cookie_buf, sizeof (cookie_buf),
-                                            NULL, NULL)))
     {
       debug_printf ("ReadFile1 failed %p", status);
       if (status != STATUS_END_OF_FILE)
        set_error (EIO);
+      return 0;
     }
-  else if (io.Information == sizeof (cookie_buf)
+  off.QuadPart = io.Information;
+  if (io.Information == sizeof (cookie_buf)
           && memcmp (cookie_buf, SYMLINK_COOKIE, sizeof (cookie_buf)) == 0)
     {
       /* It's a symlink.  */
@@ -1817,14 +1808,13 @@ symlink_info::check_sysfile (HANDLE in_h)
       /* Interix symlink cookies are shorter than Cygwin symlink cookies, so
          in case of an Interix symlink cooky we have read too far into the
         file.  Set file pointer back to the position right after the cookie. */
-      FILE_POSITION_INFORMATION fpi;
-      fpi.CurrentByteOffset.QuadPart = sizeof (INTERIX_SYMLINK_COOKIE) - 1;
-      NtSetInformationFile (h, &io, &fpi, sizeof fpi, FilePositionInformation);
+      off.QuadPart = sizeof (INTERIX_SYMLINK_COOKIE) - 1;
     }
   if (pflags & PATH_SYMLINK)
     {
       status = NtReadFile (h, NULL, NULL, NULL, &io, srcbuf,
-                          NT_MAX_PATH, NULL, NULL);
+                          NT_MAX_PATH, &off, NULL);
+      status = wait_pending (status, h, io);
       if (!NT_SUCCESS (status))
        {
          debug_printf ("ReadFile2 failed");
@@ -1852,7 +1842,6 @@ symlink_info::check_sysfile (HANDLE in_h)
       else
        res = posixify (srcbuf);
     }
-  NtClose (h);
   return res;
 }
 
@@ -1916,7 +1905,6 @@ symlink_info::check_nfs_symlink (HANDLE h)
 {
   tmp_pathbuf tp;
   NTSTATUS status;
-  OBJECT_ATTRIBUTES attr;
   IO_STATUS_BLOCK io;
   struct {
     FILE_GET_EA_INFORMATION fgei;
@@ -1925,11 +1913,6 @@ symlink_info::check_nfs_symlink (HANDLE h)
   PFILE_FULL_EA_INFORMATION pffei;
   int res = 0;
 
-  InitializeObjectAttributes (&attr, &ro_u_empty, 0, h, NULL);
-  status = NtOpenFile (&h, FILE_READ_EA, &attr, &io, FILE_SHARE_VALID_FLAGS,
-                      FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT);
-  if (!NT_SUCCESS (status))
-    return 0;
   /* To find out if the file is a symlink and to get the symlink target,
      try to fetch the NfsSymlinkTargetName EA. */
   fgei_buf.fgei.NextEntryOffset = 0;
@@ -1938,13 +1921,12 @@ symlink_info::check_nfs_symlink (HANDLE h)
   pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
   status = NtQueryEaFile (h, &io, pffei, NT_MAX_PATH * sizeof (WCHAR), TRUE,
                          &fgei_buf.fgei, sizeof fgei_buf, NULL, TRUE);
-  NtClose (h);
   if (NT_SUCCESS (status) && pffei->EaValueLength > 0)
     {
       PWCHAR spath = (PWCHAR)
                     (pffei->EaName + pffei->EaNameLength + 1);
       res = sys_wcstombs (contents, SYMLINK_MAX + 1,
-                     spath, pffei->EaValueLength);
+                         spath, pffei->EaValueLength) - 1;
       pflags |= PATH_SYMLINK;
     }
   return res;
@@ -2197,8 +2179,8 @@ symlink_info::parse_device (const char *contents)
    stored into BUF if PATH is a symlink.  */
 
 int
-symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt,
-                    fs_info &fs)
+symlink_info::check (char *path, const suffix_info *suffixes, fs_info &fs,
+                    path_conv_handle &conv_hdl)
 {
   int res;
   HANDLE h;
@@ -2238,6 +2220,10 @@ restart:
   PVOID eabuf = &nfs_aol_ffei;
   ULONG easize = sizeof nfs_aol_ffei;
 
+# define MIN_STAT_ACCESS       (READ_CONTROL | FILE_READ_ATTRIBUTES)
+# define FULL_STAT_ACCESS      (SYNCHRONIZE | GENERIC_READ)
+  ACCESS_MASK access = 0;
+
   bool had_ext = !!*ext_here;
   while (suffix.next ())
     {
@@ -2255,14 +2241,22 @@ restart:
         symlink (which would spoil the task of this method quite a bit).
         Fortunately it's ignored on most other file systems so we don't have
         to special case NFS too much. */
-      status = NtCreateFile (&h,
-                            READ_CONTROL | FILE_READ_ATTRIBUTES,
-                            &attr, &io, NULL, 0, FILE_SHARE_VALID_FLAGS,
-                            FILE_OPEN,
+      status = NtCreateFile (&h, access = FULL_STAT_ACCESS, &attr, &io, NULL,
+                            0, FILE_SHARE_VALID_FLAGS, FILE_OPEN,
                             FILE_OPEN_REPARSE_POINT
                             | FILE_OPEN_FOR_BACKUP_INTENT,
                             eabuf, easize);
-      debug_printf ("%p = NtCreateFile (%S)", status, &upath);
+      if (status == STATUS_ACCESS_DENIED)
+       {
+         status = NtCreateFile (&h, access = MIN_STAT_ACCESS, &attr, &io,
+                                NULL, 0, FILE_SHARE_VALID_FLAGS, FILE_OPEN,
+                                FILE_OPEN_REPARSE_POINT
+                                | FILE_OPEN_FOR_BACKUP_INTENT,
+                                eabuf, easize);
+         debug_printf ("%p = NtCreateFile (2:%S)", status, &upath);
+       }
+      else
+       debug_printf ("%p = NtCreateFile (1:%S)", status, &upath);
       /* No right to access EAs or EAs not supported? */
       if (!NT_SUCCESS (status)
          && (status == STATUS_ACCESS_DENIED
@@ -2282,11 +2276,20 @@ restart:
              eabuf = NULL;
              easize = 0;
            }
-         status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
-                              &attr, &io, FILE_SHARE_VALID_FLAGS,
+         status = NtOpenFile (&h, access = FULL_STAT_ACCESS, &attr, &io,
+                              FILE_SHARE_VALID_FLAGS,
                               FILE_OPEN_REPARSE_POINT
                               | FILE_OPEN_FOR_BACKUP_INTENT);
-         debug_printf ("%p = NtOpenFile (no-EA, %S)", status, &upath);
+         if (status == STATUS_ACCESS_DENIED)
+           {
+             status = NtOpenFile (&h, access = MIN_STAT_ACCESS, &attr, &io,
+                                  FILE_SHARE_VALID_FLAGS,
+                                  FILE_OPEN_REPARSE_POINT
+                                  | FILE_OPEN_FOR_BACKUP_INTENT);
+             debug_printf ("%p = NtOpenFile (no-EAs 2:%S)", status, &upath);
+           }
+         else
+           debug_printf ("%p = NtOpenFile (no-EA 1:%S)", status, &upath);
        }
       if (status == STATUS_OBJECT_NAME_NOT_FOUND)
        {
@@ -2478,7 +2481,10 @@ restart:
       if ((fileattr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY))
          == FILE_ATTRIBUTE_READONLY && suffix.lnk_match ())
        {
-         res = check_shortcut (h);
+         if (!(access & GENERIC_READ))
+           res = 0;
+         else
+           res = check_shortcut (h);
          if (!res)
            {
              /* If searching for `foo' and then finding a `foo.lnk' which is
@@ -2538,17 +2544,23 @@ restart:
       else if ((fileattr & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY))
               == FILE_ATTRIBUTE_SYSTEM)
        {
-         res = check_sysfile (h);
+         if (!(access & GENERIC_READ))
+           res = 0;
+         else
+           res = check_sysfile (h);
          if (res)
            break;
        }
 
-      /* If the file could be opened with FILE_READ_EA, and if it's on a
-        NFS share, check if it's a symlink.  Only files can be symlinks
+      /* If the file is on an NFS share and could be opened with extended
+        attributes, check if it's a symlink.  Only files can be symlinks
         (which can be symlinks to directories). */
       else if (fs.is_nfs () && !no_ea && !(fileattr & FILE_ATTRIBUTE_DIRECTORY))
        {
-         res = check_nfs_symlink (h);
+         if (!(access & GENERIC_READ))
+           res = 0;
+         else
+           res = check_nfs_symlink (h);
          if (res)
            break;
        }
@@ -2562,7 +2574,12 @@ restart:
     }
 
   if (h)
-    NtClose (h);
+    {
+      if (pflags & PC_KEEP_HANDLE)
+       conv_hdl.set (h, access);
+      else
+       NtClose (h);
+    }
 
   syscall_printf ("%d = symlink.check (%s, %p) (%p)",
                  res, suffix.path, contents, pflags);
index 4180615..a2396f1 100644 (file)
@@ -57,6 +57,7 @@ enum pathconv_arg
   PC_CHECK_EA          = 0x0040,
   PC_POSIX             = 0x0080,
   PC_NOWARN            = 0x0100,
+  PC_KEEP_HANDLE       = 0x00400000,
   PC_NO_ACCESS_CHECK   = 0x00800000
 };
 
@@ -86,6 +87,33 @@ enum path_types
   PATH_SOCKET          = 0x40000000
 };
 
+class path_conv_handle
+{
+  HANDLE      hdl;
+  ACCESS_MASK acc;
+public:
+  path_conv_handle () : hdl (NULL), acc (0) {}
+  inline void set (HANDLE h, ACCESS_MASK a) { hdl = h; acc = a; }
+  inline void close ()
+  {
+    if (hdl)
+      CloseHandle (hdl);
+    set (NULL, 0);
+  }
+  inline void dup (path_conv_handle &pch)
+  {
+    if (!DuplicateHandle (GetCurrentProcess (), pch.handle (),
+                         GetCurrentProcess (), &hdl,
+                         0, TRUE, DUPLICATE_SAME_ACCESS))
+      {
+       hdl = NULL;
+       acc = 0;
+      }
+  }
+  inline HANDLE handle () const { return hdl; }
+  inline ACCESS_MASK access () const { return acc; }
+};
+
 class symlink_info;
 
 class path_conv
@@ -98,6 +126,7 @@ class path_conv
   void add_ext_from_sym (symlink_info&);
   DWORD symlink_length;
   const char *path;
+  path_conv_handle conv_handle;
  public:
   unsigned path_flags;
   const char *known_suffix;
@@ -220,6 +249,7 @@ class path_conv
   {
     memcpy (this, &pc, sizeof pc);
     path = cstrdup (pc.path);
+    conv_handle.dup (pc.conv_handle);
     normalized_path = cstrdup(pc.normalized_path);
     wide_path = NULL;
     return *this;
@@ -250,6 +280,11 @@ class path_conv
   }
   bool is_binary ();
 
+  HANDLE handle () const { return conv_handle.handle (); }
+  ACCESS_MASK access () const { return conv_handle.access (); }
+  void reset_conv_handle () { conv_handle.set (NULL, 0); }
+  void close_conv_handle () { conv_handle.close (); }
+
   __ino64_t get_ino_by_handle (HANDLE h);
 #if 0 /* obsolete, method still exists in fhandler_disk_file.cc */
   unsigned __stdcall ndisk_links (DWORD);
index 1563204..4d5e4ba 100644 (file)
@@ -419,7 +419,8 @@ acl_worker (const char *path, int cmd, int nentries, __aclent32_t *aclbufp,
            unsigned fmode)
 {
   int res = -1;
-  fhandler_base *fh = build_fh_name (path, fmode, stat_suffixes);
+  fhandler_base *fh = build_fh_name (path, fmode | PC_KEEP_HANDLE,
+                                    stat_suffixes);
   if (fh->error ())
     {
       debug_printf ("got %d error from build_fh_name", fh->error ());
index 8adf22e..8ba0af2 100644 (file)
@@ -1169,7 +1169,8 @@ link (const char *oldpath, const char *newpath)
   int res = -1;
   fhandler_base *fh;
 
-  if (!(fh = build_fh_name (oldpath, PC_SYM_NOFOLLOW, stat_suffixes)))
+  if (!(fh = build_fh_name (oldpath, PC_SYM_NOFOLLOW | PC_KEEP_HANDLE,
+                           stat_suffixes)))
     goto error;
 
   if (fh->error ())
@@ -1542,7 +1543,7 @@ extern "C" int
 stat64 (const char *name, struct __stat64 *buf)
 {
   syscall_printf ("entering");
-  path_conv pc (name, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes);
+  path_conv pc (name, PC_SYM_FOLLOW | PC_POSIX | PC_KEEP_HANDLE, stat_suffixes);
   return stat_worker (pc, buf);
 }
 
@@ -1581,7 +1582,8 @@ extern "C" int
 lstat64 (const char *name, struct __stat64 *buf)
 {
   syscall_printf ("entering");
-  path_conv pc (name, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
+  path_conv pc (name, PC_SYM_NOFOLLOW | PC_POSIX | PC_KEEP_HANDLE,
+               stat_suffixes);
   return stat_worker (pc, buf);
 }
 
@@ -2561,7 +2563,8 @@ statvfs (const char *name, struct statvfs *sfs)
   if (efault.faulted (EFAULT))
     goto error;
 
-  if (!(fh = build_fh_name (name, PC_SYM_FOLLOW, stat_suffixes)))
+  if (!(fh = build_fh_name (name, PC_SYM_FOLLOW | PC_KEEP_HANDLE,
+                           stat_suffixes)))
     goto error;
 
   if (fh->error ())