This is the mail archive of the cygwin-developers mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Cygwin Filesystem Performance degradation 1.7.5 vs 1.7.7, and methods for improving performance


Hi,

Attached is a patch on symlink_info::check.
I changed symlink_info::check so it will create a file handle with GENERIC_READ permissions only when it needs it. in all other cases it opens it with READ_CONTROL | FILE_READ_ATTRIBUTES.


This improved the performance of stat significally, without changing the behaviour.

FROM:
/bin$ time ls -l > /dev/null
real    0m3.875s
user    0m0.108s
sys     0m0.452s

TO:
/bin$ time ls -l > /dev/null
real    0m0.562s
user    0m0.171s
sys     0m0.296s

Yoni.

Index: cygwin/path.cc
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/path.cc,v
retrieving revision 1.615
diff -u -p -r1.615 path.cc
--- cygwin/path.cc	2 Oct 2010 19:03:44 -0000	1.615
+++ cygwin/path.cc	7 Oct 2010 06:31:06 -0000
@@ -2216,6 +2216,45 @@ symlink_info::parse_device (const char *
    Return -1 on error, 0 if PATH is not a symlink, or the length
    stored into BUF if PATH is a symlink.  */

+# define MIN_STAT_ACCESS (READ_CONTROL | FILE_READ_ATTRIBUTES)
+# define FULL_STAT_ACCESS (SYNCHRONIZE | GENERIC_READ)
+static int get_file_handle(HANDLE *h, OBJECT_ATTRIBUTES attr,
+ UNICODE_STRING upath, int read, ACCESS_MASK *access,
+ PVOID eabuf, ULONG easize)
+{
+ IO_STATUS_BLOCK io;
+ NTSTATUS status;
+ /* The EA given to NtCreateFile allows to get a handle to a symlink on
+ an NFS share, rather than getting a handle to the target of the
+ 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. */
+ if (read)
+ {
+ 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);
+ if (eabuf)
+ debug_printf ("%p = NtCreateFile (2:%S)", status, &upath);
+ else
+ debug_printf ("%p = NtOpenFile (no-EA 1:%S)", status, &upath);
+ if (status != STATUS_ACCESS_DENIED)
+ return status;
+ }
+ status = NtCreateFile (h, *access = MIN_STAT_ACCESS | FILE_READ_EA,
+ &attr, &io, NULL, 0, FILE_SHARE_VALID_FLAGS,
+ FILE_OPEN,
+ FILE_OPEN_REPARSE_POINT
+ | FILE_OPEN_FOR_BACKUP_INTENT, eabuf, easize);
+ if (eabuf)
+ debug_printf ("%p = NtCreateFile (1:%S)", status, &upath);
+ else
+ debug_printf ("%p = NtOpenFile (no-EAs 2:%S)", status, &upath);
+ return status;
+}
+
int
symlink_info::check (char *path, const suffix_info *suffixes, fs_info &fs,
path_conv_handle &conv_hdl)
@@ -2257,8 +2296,6 @@ 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;
@@ -2273,28 +2310,9 @@ restart:
NtClose (h);
h = NULL;
}
- /* The EA given to NtCreateFile allows to get a handle to a symlink on
- an NFS share, rather than getting a handle to the target of the
- 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, 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);
- if (status == STATUS_ACCESS_DENIED && eabuf)
- {
- status = NtCreateFile (&h, access = MIN_STAT_ACCESS | FILE_READ_EA,
- &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);
+
+ status = get_file_handle(&h, attr, upath, 0, &access, eabuf, easize);
+
/* No right to access EAs or EAs not supported? */
if (!NT_SUCCESS (status)
&& (status == STATUS_ACCESS_DENIED
@@ -2314,20 +2332,7 @@ restart:
eabuf = NULL;
easize = 0;
}
- status = NtOpenFile (&h, access = FULL_STAT_ACCESS, &attr, &io,
- FILE_SHARE_VALID_FLAGS,
- FILE_OPEN_REPARSE_POINT
- | FILE_OPEN_FOR_BACKUP_INTENT);
- 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);
+ status = get_file_handle(&h, attr, upath, 0, &access, NULL, 0);
}
if (status == STATUS_OBJECT_NAME_NOT_FOUND)
{
@@ -2565,6 +2570,12 @@ restart:
== FILE_ATTRIBUTE_READONLY && suffix.lnk_match ())
{
if (!(access & GENERIC_READ))
+ {
+ NtClose(h);
+ status = get_file_handle(&h, attr, upath, 1, &access, eabuf,
+ easize);
+ }
+ if (!NT_SUCCESS(status) || !(access & GENERIC_READ))
res = 0;
else
res = check_shortcut (h);
@@ -2637,6 +2648,12 @@ restart:
== FILE_ATTRIBUTE_SYSTEM)
{
if (!(access & GENERIC_READ))
+ {
+ NtClose(h);
+ status = get_file_handle(&h, attr, upath, 1, &access, eabuf,
+ easize);
+ }
+ if (!NT_SUCCESS(status) || !(access & GENERIC_READ))
res = 0;
else
res = check_sysfile (h);



On 6/10/2010 6:47 PM, Corinna Vinschen wrote:
On Oct 6 18:35, Corinna Vinschen wrote:
On Oct 6 14:37, Yoni Londner wrote:
The difference in huge. while opening the file with GENERIC_READ
took almost 1ms per file, opening with FILE_READ_ATTRIBUTES only
took 0.027ms per file:
testing /bin QIFR 3588 files stat() 3312ms, per file: 0.9229ms
testing /bin QIF 3588 files stat() 97.66ms, per file: 0.02722ms

I can't reproduce any noticable difference:


   0.2093ms<= QIF<= 0.3153ms
   0.2112ms<= QIFR<= 0.3306ms

I will start working on a patch doing this, and hope to post it soon.

Did anybody of you notice the license problem which has been mentioned at leats three times now? Please read http://cygwin.com/contrib.html.

If it's really as easy as dropping the GENERIC_READ, then I can do that
in symlink_info::check easily.  That's basically what is different
between 1.7.5 and 1.7.7.  "It seemed to be a good idea at the time".

Currently we have nothing to do with the driver, but maybe in the
future we will find better ways to improve performance, that will
require a kernel driver.

I'd prefer to stick to user space.

Attached is a preliminary version of the new symlink_info::xcheck function, which uses NtQueryDirectoryFile. First tests show that `ls -l' is about three times faster than when using symlink_info::check. To activate the function you can use the "CYGWIN=xcheck" setting. Of course you will not get correct ACLs and a wrong st_nlink but that has been discussed to death already.

The buzzword here is "preliminary".  I think I don't like the "xcheck"
name for the function and the flags, nor do I like the CYGWIN=xcheck
setting at all.  However, since I'm mostely offline the next couple of
weeks, there's at least something to play with.


Corinna




Index: environ.cc
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/environ.cc,v
retrieving revision 1.183
diff -u -p -r1.183 environ.cc
--- environ.cc	18 May 2010 14:30:50 -0000	1.183
+++ environ.cc	5 Oct 2010 16:41:14 -0000
@@ -31,6 +31,7 @@ details. */
  #include "child_info.h"
  #include "ntdll.h"

+extern bool use_xcheck;
  extern bool dos_file_warning;
  extern bool ignore_case_with_glob;
  extern bool allow_winsymlinks;
@@ -605,6 +606,7 @@ static struct parse_thing
    {"tty", {NULL}, set_process_state, NULL, {{0}, {PID_USETTY}}},
    {"upcaseenv", {&create_upcaseenv}, justset, NULL, {{false}, {true}}},
    {"winsymlinks", {&allow_winsymlinks}, justset, NULL, {{false}, {true}}},
+  {"xcheck", {&use_xcheck}, justset, NULL, {{false}, {true}}},
    {NULL, {0}, justset, 0, {{0}, {0}}}
  };

Index: fhandler_disk_file.cc
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/fhandler_disk_file.cc,v
retrieving revision 1.344
diff -u -p -r1.344 fhandler_disk_file.cc
--- fhandler_disk_file.cc	2 Oct 2010 19:03:44 -0000	1.344
+++ fhandler_disk_file.cc	5 Oct 2010 16:41:14 -0000
@@ -345,11 +345,11 @@ fhandler_base::fstat_by_handle (struct _
    IO_STATUS_BLOCK io;

    /* If the file has been opened for other purposes than stat, we can't rely
-     on the information stored in pc.fnoi.  So we overwrite them here. */
+     on the information stored in pc.finfo.  So we overwrite them here. */
    if (get_io_handle ())
      {
-      PFILE_NETWORK_OPEN_INFORMATION pfnoi = pc.fnoi ();
-      status = NtQueryInformationFile (h,&io, pfnoi, sizeof *pfnoi,
+      PFILE_CYGWIN_INFORMATION pfci = pc.finfo ();
+      status = NtQueryInformationFile (h,&io, pfci, sizeof *pfci,
                                        FileNetworkOpenInformation);
        if (!NT_SUCCESS (status))
         {
@@ -403,6 +403,8 @@ fhandler_base::fstat_by_name (struct __s
      WCHAR buf[NAME_MAX + 1];
    } fdi_buf;

+  if (!ino)
+    ino = pc.finfo ()->FileId.QuadPart;
    if (!ino&&  pc.hasgood_inode ()
        &&  wincap.has_fileid_dirinfo ()&&  !pc.has_buggy_fileid_dirinfo ())
      {
@@ -441,14 +443,20 @@ fhandler_base::fstat_fs (struct __stat64
    int oret;
    int open_flags = O_RDONLY | O_BINARY;

+  if (pc.fs_is_nfs ())
+    return fstat_by_nfs_ea (buf);
+
    if (get_stat_handle ())
      {
        if (!nohandle ()&&  !is_fs_special ())
-	res = pc.fs_is_nfs () ? fstat_by_nfs_ea (buf) : fstat_by_handle (buf);
+	res = fstat_by_handle (buf);
        if (res)
  	res = fstat_by_name (buf);
        return res;
      }
+  else if (pc.xchecked ())
+    return fstat_by_name (buf);
+
    /* 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
@@ -485,19 +493,19 @@ fhandler_base::fstat_helper (struct __st
    IO_STATUS_BLOCK st;
    FILE_COMPRESSION_INFORMATION fci;
    HANDLE h = get_stat_handle ();
-  PFILE_NETWORK_OPEN_INFORMATION pfnoi = pc.fnoi ();
+  PFILE_CYGWIN_INFORMATION pfci = pc.finfo ();
    ULONG attributes = pc.file_attributes ();

-  to_timestruc_t ((PFILETIME)&pfnoi->LastAccessTime,&buf->st_atim);
-  to_timestruc_t ((PFILETIME)&pfnoi->LastWriteTime,&buf->st_mtim);
+  to_timestruc_t ((PFILETIME)&pfci->LastAccessTime,&buf->st_atim);
+  to_timestruc_t ((PFILETIME)&pfci->LastWriteTime,&buf->st_mtim);
    /* If the ChangeTime is 0, the underlying FS doesn't support this timestamp
       (FAT for instance).  If so, it's faked using LastWriteTime. */
-  to_timestruc_t (pfnoi->ChangeTime.QuadPart ? (PFILETIME)&pfnoi->ChangeTime
-					    : (PFILETIME)&pfnoi->LastWriteTime,
+  to_timestruc_t (pfci->ChangeTime.QuadPart ? (PFILETIME)&pfci->ChangeTime
+					    : (PFILETIME)&pfci->LastWriteTime,
  		&buf->st_ctim);
-  to_timestruc_t ((PFILETIME)&pfnoi->CreationTime,&buf->st_birthtim);
+  to_timestruc_t ((PFILETIME)&pfci->CreationTime,&buf->st_birthtim);
    buf->st_rdev = buf->st_dev = get_dev ();
-  buf->st_size = (_off64_t) pfnoi->EndOfFile.QuadPart;
+  buf->st_size = (_off64_t) pfci->EndOfFile.QuadPart;
    /* The number of links to a directory includes the number of subdirectories
       in the directory, since all those subdirectories point to it.  However,
       this is painfully slow, so we do without it. */
@@ -515,10 +523,10 @@ fhandler_base::fstat_helper (struct __st

buf->st_blksize = PREFERRED_IO_BLKSIZE;

-  if (pfnoi->AllocationSize.QuadPart>= 0LL)
+  if (pfci->AllocationSize.QuadPart>= 0LL)
      /* A successful NtQueryInformationFile returns the allocation size
         correctly for compressed and sparse files as well. */
-    buf->st_blocks = (pfnoi->AllocationSize.QuadPart + S_BLKSIZE - 1)
+    buf->st_blocks = (pfci->AllocationSize.QuadPart + S_BLKSIZE - 1)
  		     / S_BLKSIZE;
    else if (::has_attribute (attributes, FILE_ATTRIBUTE_COMPRESSED
  					| FILE_ATTRIBUTE_SPARSE_FILE)
@@ -591,7 +599,7 @@ fhandler_base::fstat_helper (struct __st
  	{
  	  buf->st_mode |= S_IFREG;
  	  /* Check suffix for executable file. */
-	  if (pc.exec_state () == dont_know_if_executable)
+	  if (pc.exec_state () != is_executable)
  	    {
  	      PUNICODE_STRING path = pc.get_nt_native_path ();

Index: ntdll.h
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/ntdll.h,v
retrieving revision 1.104
diff -u -p -r1.104 ntdll.h
--- ntdll.h	24 Sep 2010 12:41:33 -0000	1.104
+++ ntdll.h	5 Oct 2010 16:41:14 -0000
@@ -1074,8 +1074,12 @@ extern "C"
      if (dirname)
        RtlInitCountedUnicodeString (dirname, path->Buffer, len * sizeof (WCHAR));
      if (basename)
-      RtlInitCountedUnicodeString (basename,&path->Buffer[len],
-				   path->Length - len * sizeof (WCHAR));
+      {
+	RtlInitCountedUnicodeString (basename,&path->Buffer[len],
+				     path->Length - len * sizeof (WCHAR));
+      	/* Preserve MaximumLength! */
+	basename->MaximumLength = path->MaximumLength - len * sizeof (WCHAR);
+      }
    }
    /* Check if prefix is a prefix of path. */
    inline
Index: path.cc
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/path.cc,v
retrieving revision 1.615
diff -u -p -r1.615 path.cc
--- path.cc	2 Oct 2010 19:03:44 -0000	1.615
+++ path.cc	5 Oct 2010 16:41:15 -0000
@@ -95,6 +95,8 @@ struct symlink_info
    _major_t major;
    _minor_t minor;
    _mode_t mode;
+  int xcheck (char *path, const suffix_info *suffixes, fs_info&fs,
+	      path_conv_handle&conv_hdl);
    int check (char *path, const suffix_info *suffixes, fs_info&fs,
  	     path_conv_handle&conv_hdl);
    int set (char *path);
@@ -105,6 +107,10 @@ struct symlink_info
    int check_nfs_symlink (HANDLE h);
    int posixify (char *srcbuf);
    bool set_error (int);
+  bool set_error_from_nt_status (NTSTATUS status)
+  {
+    return set_error (geterrno_from_win_error (RtlNtStatusToDosError (status)));
+  }
  };

  muto NO_COPY cwdstuff::cwd_lock;
@@ -435,7 +441,7 @@ get_nt_native_path (const char *path, UN
    if (dos)
      {
        /* Unfortunately we can't just use transform_chars with the tfx_rev_chars
-         table since only leading and trainlig spaces and dots are affected.
+         table since only leading and trailing spaces and dots are affected.
  	 So we step to every backslash and fix surrounding dots and spaces.
  	 That makes these broken filesystems a bit slower, but, hey. */
        PWCHAR cp = upath.Buffer + 7;
@@ -586,6 +592,8 @@ path_conv::check (const UNICODE_STRING *
    path_conv::check (path, opt, suffixes);
  }

+bool use_xcheck = false;
+
  void
  path_conv::check (const char *src, unsigned opt,
  		  const suffix_info *suffixes)
@@ -651,6 +659,8 @@ path_conv::check (const char *src, unsig
        error = ENOENT;
        return;
      }
+  if (use_xcheck)
+    opt |= PC_XCHECK;

    bool is_msdos = false;
    /* This loop handles symlink expansion.  */
@@ -860,7 +870,10 @@ path_conv::check (const char *src, unsig

is_fs_via_procsys:

-	  symlen = sym.check (full_path, suff, fs, conv_handle);
+	  if (opt&  PC_XCHECK)
+	    symlen = sym.xcheck (full_path, suff, fs, conv_handle);
+	  else
+	    symlen = sym.check (full_path, suff, fs, conv_handle);

is_virtual_symlink:

@@ -2199,6 +2212,186 @@ symlink_info::parse_device (const char *
    return isdevice = true;
  }

+/* Private class to store a single suffix.  Used by ucs_suffixes exclusively. */
+#define UCS_MAX_LEN 6
+class ucs_suffix
+{
+  UNICODE_STRING name;
+  WCHAR buf[UCS_MAX_LEN];
+  void set (const suffix_info *sin)
+  {
+    sys_mbstowcs (buf, UCS_MAX_LEN, sin->name);
+    RtlInitUnicodeString (&name, buf);
+  }
+  void add_lnk ()
+  { RtlInitUnicodeString (&name, wcscpy (buf, L".lnk")); }
+  bool check (PUNICODE_STRING fname)
+  { return RtlEqualUnicodePathSuffix (fname,&name, TRUE); }
+  PCWSTR get () const { return buf; }
+  friend class ucs_suffixes;
+};
+
+/* Replacement class to store the incoming suffixes to symlink_info::xcheck.
+   The suffixes are stored as UNICODE_STRINGS for quicker comparison.
+   Ultimately this can be used for symlink_info::check as well.  The
+   constructor also takes over the job of suffix_scan::has */
+#define UCS_MAX_SUFFIXES 8
+class ucs_suffixes
+{
+  ucs_suffix suf[UCS_MAX_SUFFIXES];
+  int cnt;
+  int lnk_idx;
+public:
+  ucs_suffixes (const suffix_info *sin, const char *path, char *&ext_here)
+  : cnt (0), lnk_idx (-1)
+  {
+    if (sin)
+      for (int i = 0; i<  UCS_MAX_SUFFIXES - 1&&  sin[i].name; ++i)
+	if (*sin[i].name)
+	  suf[cnt++].set (sin + i);
+
+    const char *fname = strrchr (path, '\\');
+    fname = fname ? fname + 1 : path;
+    ext_here = strrchr (fname, '.');
+    char *eopath = strchr (path, '\0');
+
+    if (ext_here)
+      {
+	if (sin)
+	  {
+	    /* Check if the extension matches a known extension */
+	    for (const suffix_info *ex = sin; ex->name != NULL; ex++)
+	      if (ascii_strcasematch (ext_here, ex->name))
+		{
+		  cnt = 0;
+		  return;
+		}
+	  }
+	/* Didn't match.  Use last resort -- .lnk. */
+	if (ascii_strcasematch (ext_here, ".lnk"))
+	  {
+	    cnt = 0;
+	    return;
+	  }
+      }
+    ext_here = eopath;
+    /* Avoid attaching suffixes if the resulting filename would be invalid. */
+    if (eopath - fname>  NAME_MAX - 4)
+      cnt = 0;
+  }
+  void add_lnk () { lnk_idx = cnt; suf[cnt++].add_lnk (); }
+  int check (PUNICODE_STRING fname)
+  {
+    for (int i = 0; i<  cnt; ++i)
+      if (suf[i].check (fname))
+      	return i;
+    return -1;
+  }
+  PCWSTR get (int i) const { return i<  0 || i>= cnt ? L"" : suf[i].get (); }
+  int count () const { return cnt; }
+  bool lnk_match (int i) const { return i>= 0&&  i == lnk_idx; }
+};
+
+/* This class is an abstraction for the various FILE_INFO classes used by
+   symlink_info::xcheck.  The constructor chooses the right info class
+   based on the information fetched from fs_info::update.  The idea here is
+   that symlink_info::xcheck doesn't have to know the actual info class
+   used to get the necessary information.  The caller has to provide the
+   buffer and the buffer size for the file info. */
+class file_info
+{
+  FILE_INFORMATION_CLASS fic;
+  PBYTE buffer;
+  PBYTE curptr;
+  size_t size;
+  UNICODE_STRING uname;
+public:
+  file_info (fs_info&fs, PVOID buf, size_t siz)
+  : buffer ((PBYTE) buf), curptr (NULL), size (siz)
+  {
+    if (fs.is_nfs ())
+      fic = FileNamesInformation;
+    else if (fs.hasgood_inode ()&&  wincap.has_fileid_dirinfo ()
+	&&  !fs.has_buggy_fileid_dirinfo ())
+      fic = FileIdBothDirectoryInformation;
+    else
+      fic = FileBothDirectoryInformation;
+  }
+  operator PVOID () const { return buffer; }
+  operator ULONG () const { return size; }
+  operator FILE_INFORMATION_CLASS () const { return fic; }
+  bool next ()
+  {
+    bool ret = true;
+
+    if (!curptr)
+      curptr = buffer;
+    else if (PFILE_NAMES_INFORMATION (curptr)->NextEntryOffset != 0)
+      curptr += PFILE_NAMES_INFORMATION (curptr)->NextEntryOffset;
+    else
+      {
+        curptr = NULL;
+	ret = false;
+      }
+    return ret;
+  }
+  PUNICODE_STRING name ()
+  {
+    if (!curptr)
+      return NULL;
+    switch (fic)
+      {
+      case FileNamesInformation:
+      	uname.Length = uname.MaximumLength
+	= PFILE_NAMES_INFORMATION (curptr)->FileNameLength;
+	uname.Buffer = PFILE_NAMES_INFORMATION (curptr)->FileName;
+	break;
+      case FileBothDirectoryInformation:
+      	uname.Length = uname.MaximumLength
+	= PFILE_BOTH_DIRECTORY_INFORMATION (curptr)->FileNameLength;
+	uname.Buffer = PFILE_BOTH_DIRECTORY_INFORMATION (curptr)->FileName;
+	break;
+      case FileIdBothDirectoryInformation:
+      	uname.Length = uname.MaximumLength
+	= PFILE_ID_BOTH_DIR_INFORMATION (curptr)->FileNameLength;
+	uname.Buffer = PFILE_ID_BOTH_DIR_INFORMATION (curptr)->FileName;
+	break;
+      default:
+      	return NULL;
+      }
+    return&uname;
+  }
+  ULONG file_attributes ()
+  {
+    if (curptr&&  fic != FileNamesInformation)
+      return PFILE_BOTH_DIRECTORY_INFORMATION (curptr)->FileAttributes;
+    return INVALID_FILE_ATTRIBUTES;
+  }
+  void copy (PFILE_CYGWIN_INFORMATION pfci, PUNICODE_STRING dir,
+	     PUNICODE_STRING file)
+  {
+    if (curptr&&  fic != FileNamesInformation)
+      {
+      	memcpy (pfci,&PFILE_ID_BOTH_DIR_INFORMATION (curptr)->CreationTime,
+		4 * sizeof (LARGE_INTEGER));
+	pfci->EndOfFile.QuadPart
+	  = PFILE_ID_BOTH_DIR_INFORMATION (curptr)->EndOfFile.QuadPart;
+	pfci->AllocationSize.QuadPart
+	  = PFILE_ID_BOTH_DIR_INFORMATION (curptr)->AllocationSize.QuadPart;
+	pfci->FileAttributes
+	  = PFILE_ID_BOTH_DIR_INFORMATION (curptr)->FileAttributes;
+	pfci->ReparseTag
+	  = PFILE_ID_BOTH_DIR_INFORMATION (curptr)->EaSize;
+	if (fic == FileIdBothDirectoryInformation)
+	  pfci->FileId.QuadPart
+	    = PFILE_ID_BOTH_DIR_INFORMATION (curptr)->FileId.QuadPart;
+	else
+	  pfci->FileId.QuadPart = hash_path_name (hash_path_name (0, dir),
+						  file);
+      }
+  }
+};
+
  /* Check if PATH is a symlink.  PATH must be a valid Win32 path name.

     If PATH is a symlink, put the value of the symlink--the file to
@@ -2217,6 +2410,325 @@ symlink_info::parse_device (const char *
     stored into BUF if PATH is a symlink.  */

  int
+symlink_info::xcheck (char *path, const suffix_info *suffixes, fs_info&fs,
+		      path_conv_handle&conv_hdl)
+{
+  int res = 0;
+  HANDLE dh;
+  NTSTATUS status;
+  UNICODE_STRING upath, dirname, basename, filter;
+  OBJECT_ATTRIBUTES attr;
+  IO_STATUS_BLOCK io;
+  bool is_root_dir = false;
+  const ULONG ci_flag = cygwin_shared->obcaseinsensitive
+			|| (pflags&  PATH_NOPOSIX) ? OBJ_CASE_INSENSITIVE : 0;
+
+  contents[0] = '\0';
+  issymlink = isdevice = ext_tacked_on = false;
+  error = major = minor = mode = 0;
+  fileattr = INVALID_FILE_ATTRIBUTES;
+
+  ucs_suffixes suff (suffixes, path, ext_here);
+  extn = ext_here - path;
+  pflags&= ~(PATH_SYMLINK | PATH_LNK | PATH_REP | PC_KEEP_HANDLE);
+  pflags |= PATH_NOACL | PATH_NOTEXEC | PATH_XCHECKED;
+
+  tmp_pathbuf tp;
+  tp.u_get (&upath);
+  get_nt_native_path (path, upath, pflags&  PATH_DOS);
+  /* First check if we the path is the root dir of a drive or share.
+     Checking the root dir info requires to open a handle to the root
+     dir directly since there's no (accessible) parent dir anyway. */
+  if (upath.Buffer[upath.Length / sizeof (WCHAR) - 1] == L'\\')	/* \??\X:\ */
+    is_root_dir = true;
+  else if (RtlEqualUnicodePathPrefix (&upath,&ro_u_uncp, FALSE))
+    {
+      /* The idea here is that a valid path to the root dir of a share has
+	 only one additional backslash following the UNC path prefix, while
+	 any deeper path has at least two following backslashes. */
+      PWCHAR p;
+      p = wcschr (upath.Buffer + (ro_u_uncp.Length / sizeof (WCHAR)), L'\\');
+      if (p&&  !wcschr (p + 1, L'\\'))
+      	is_root_dir = true;
+    }
+  if (is_root_dir)
+    {
+      InitializeObjectAttributes (&attr,&upath, ci_flag, NULL, NULL);
+      status = NtOpenFile (&dh,
+			   READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA,
+			&attr,&io, FILE_SHARE_VALID_FLAGS,
+			   FILE_OPEN_FOR_BACKUP_INTENT);
+      if (!NT_SUCCESS (status))
+      	{
+	  set_error_from_nt_status (status);
+	  return 0;
+	}
+      fs.update (&upath, dh);
+      if (fs.is_nfs ())
+	{
+	  status = nfs_fetch_fattr3 (dh, conv_hdl.nfsattr ());
+	  if (NT_SUCCESS (status))
+	    fileattr = ((conv_hdl.nfsattr ()->type&  7) == NF3DIR)
+			? FILE_ATTRIBUTE_DIRECTORY : 0;
+	}
+      else
+	{
+	  PFILE_CYGWIN_INFORMATION pfci = conv_hdl.finfo ();
+
+	  status = NtQueryInformationFile (dh,&io, pfci, sizeof *pfci,
+					   FileNetworkOpenInformation);
+	  if ((status == STATUS_INVALID_PARAMETER
+	       || status == STATUS_NOT_IMPLEMENTED)
+	&&  RtlEqualUnicodePathPrefix (&upath,&ro_u_uncp, FALSE))
+	    {
+	      /* This occurs when accessing SMB share root dirs hosted on
+		 NT4 (STATUS_INVALID_PARAMETER), or when trying to access
+		 SMB share root dirs from NT4 (STATUS_NOT_IMPLEMENTED). */
+	      FILE_BASIC_INFORMATION fbi;
+
+	      status = NtQueryInformationFile (dh,&io,&fbi, sizeof fbi,
+					       FileBasicInformation);
+	      if (NT_SUCCESS (status))
+		{
+		  memcpy (pfci,&fbi, 4 * sizeof (LARGE_INTEGER));
+		  pfci->EndOfFile.QuadPart
+		    = pfci->AllocationSize.QuadPart = 0;
+		  pfci->FileAttributes = fbi.FileAttributes;
+		}
+	    }
+	  pfci->ReparseTag = 0;
+	  if (!fs.hasgood_inode ()
+	      || !NT_SUCCESS (NtQueryInformationFile (dh,&io,&pfci->FileId,
+						      sizeof pfci->FileId,
+						      FileInternalInformation)))
+	    pfci->FileId.QuadPart = hash_path_name (0,&upath);
+	  if (NT_SUCCESS (status))
+	    fileattr = pfci->FileAttributes;
+	}
+      NtClose (dh);
+      if (!NT_SUCCESS (status))
+	set_error_from_nt_status (status);
+      return 0;
+    }
+  /* The default case.  We open the parent dir and fetch the file info by
+     NtQueryDirectoryFile. */
+  RtlSplitUnicodePath (&upath,&dirname,&basename);
+  InitializeObjectAttributes (&attr,&dirname, ci_flag, NULL, NULL);
+  status = NtOpenFile (&dh, SYNCHRONIZE | FILE_LIST_DIRECTORY,&attr,&io,
+		       FILE_SHARE_VALID_FLAGS,
+		       FILE_SYNCHRONOUS_IO_NONALERT
+		       | FILE_OPEN_FOR_BACKUP_INTENT
+		       | FILE_DIRECTORY_FILE);
+  if (!NT_SUCCESS (status))
+    {
+      set_error_from_nt_status (status);
+      return 0;
+    }
+  /* Check the fs info.  This tells us which info class to use. */
+  fs.update (&upath, dh);
+  /* Only add the .lnk suffix if not on NFS. */
+  if (!fs.is_nfs ())
+    suff.add_lnk ();
+  else if (fs.has_dos_filenames_only ()&&  !(pflags&  PATH_DOS))
+    {
+      /* Filesystem doesn't grok leading spaces or trailing dots or spaces.
+	 Regenerate unicode path accordingly. */
+      pflags |= PATH_DOS;
+      get_nt_native_path (path, upath, true);
+      RtlSplitUnicodePath (&upath,&dirname,&basename);
+    }
+  /* Copy basename into filter and check if we have to test for suffixes.
+     If so, append the wildcard expression.  We use a more or less undocumented
+     wildcard style, which is also used by CMD.  It allows to specify filter
+     expressions following the old DOS rules back when only 8+3 filenames
+     existed.  Our filter expression only allows the filename itself, plus
+     all files which start with filename, then a dot, then a maximum of three
+     characters.  This fortunately avoids the usage of the '*' wildcard which
+     could easily become a PITA in some directories... */
+  filter = basename;
+  if (!*ext_here&&  suff.count()>  0)
+    RtlAppendUnicodeToString (&filter, L"\">>>"); /* DOS_DOT, 3 * DOS_QM */
+
+  bool found = false;
+  int suff_idx = -1;
+  PUNICODE_STRING fname = NULL;
+  file_info fi (fs, tp.w_get (), USHRT_MAX);
+
+  /* Now call NtQueryDirectoryFile until we find the file or there is no more
+     file. */
+  for (BOOLEAN restart = TRUE; !found; restart = FALSE)
+    {
+      status = NtQueryDirectoryFile (dh, NULL, NULL, NULL,&io, fi, fi, fi,
+				     FALSE,&filter, restart);
+      if (!NT_SUCCESS (status))
+      	break;
+      while (fi.next ())
+	{
+	  fname = fi.name ();
+	  /* This expression *only* allows the filename itself, or any
+	     suffix with exactly three chars. */
+	  if (fname->Length != basename.Length
+	&&  fname->Length != basename.Length + 4 * sizeof (WCHAR))
+	    continue;
+	  /* Even if the directory handle has been opened case-sensitive,
+	     the filter expression is *always* evaluated case-insensitive.
+	     That means, if case-sensitivity is switched on, we have to
+	     check that the returned filename matches the case of our
+	     incoming filename.  Grrr! */
+	  if (!ci_flag&&  !RtlEqualUnicodePathPrefix (fname,&basename, FALSE))
+	    continue;
+	  if (fname->Length == basename.Length
+	      || (suff_idx = suff.check (fname))>= 0)
+	    {
+	      /* Yeah! */
+	      found = true;
+	      break;
+	    }
+	}
+    }
+  if (!found)
+    set_error (ENOENT);
+  else
+    {
+      HANDLE fh;
+
+      /* Append suffix (if any) to the incoming path buffer. */
+      if (suff_idx>= 0)
+	{
+	  sys_wcstombs (ext_here, 5, suff.get (suff_idx));
+	  ext_tacked_on = true;
+	}
+      if (fs.is_nfs ())
+	{
+	  /* On NFS, we need the real stat info.  So, given that we
+	     have to open the file anyway, this method is slower than
+	     symlink_info::check on NFS!  Something to keep in mind. */
+	  fileattr = 0;
+	  InitializeObjectAttributes (&attr, fname, ci_flag, dh, NULL);
+	  status = NtCreateFile (&fh, FILE_READ_EA,&attr,&io, NULL, 0,
+				 FILE_SHARE_VALID_FLAGS, FILE_OPEN,
+				 FILE_OPEN_FOR_BACKUP_INTENT,
+				&nfs_aol_ffei, sizeof nfs_aol_ffei);
+	  if (!NT_SUCCESS (status))
+	    {
+	      set_error_from_nt_status (status);
+	      goto out;
+	    }
+	  status = nfs_fetch_fattr3 (fh, conv_hdl.nfsattr ());
+	  if (NT_SUCCESS (status))
+	    {
+	      if ((conv_hdl.nfsattr ()->type&  7) == NF3DIR)
+		{
+		  if (ext_tacked_on)
+		    {
+		      fileattr = INVALID_FILE_ATTRIBUTES;
+		      set_error (ENOENT);
+		    }
+		  else
+		    fileattr = FILE_ATTRIBUTE_DIRECTORY;
+		}
+	      /* Eventually, check for symlink. */
+	      else if ((conv_hdl.nfsattr ()->type&  7) == NF3LNK)
+		res = check_nfs_symlink (fh);
+	    }
+	  NtClose (fh);
+	}
+      else
+	{
+	  PFILE_CYGWIN_INFORMATION pfci;
+
+	  /* Copy file info over to path_conv. */
+	  fileattr = fi.file_attributes ();
+	  if (ext_tacked_on&&  (fileattr&  FILE_ATTRIBUTE_DIRECTORY))
+	    {
+	      set_error (ENOENT);
+	      fileattr = INVALID_FILE_ATTRIBUTES;
+	      goto out;
+	    }
+	  fi.copy (pfci = conv_hdl.finfo (),&dirname, fname);
+	  /* Eventually, check for various symlink types. */
+	  if ((fileattr&  (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY))
+	      == FILE_ATTRIBUTE_READONLY&&  suff.lnk_match (suff_idx))
+	    {
+	      InitializeObjectAttributes (&attr, fname, ci_flag, dh, NULL);
+	      status = NtOpenFile (&fh, SYNCHRONIZE | GENERIC_READ,&attr,
+				&io, FILE_SHARE_VALID_FLAGS,
+				   FILE_OPEN_FOR_BACKUP_INTENT);
+	      if (!NT_SUCCESS (status))
+		goto out;
+	      res = check_shortcut (fh);
+	      if (!res)
+	      	{
+		  if (ext_tacked_on)
+		    {
+		      fileattr = INVALID_FILE_ATTRIBUTES;
+		      set_error (ENOENT);
+		    }
+		}
+	      else if (contents[0] != ':' || contents[1] != '\\')
+		 parse_device (contents);
+	      NtClose (fh);
+	    }
+	  else if (suff.lnk_match (suff_idx))
+	    {
+	      set_error (ENOENT);
+	      fileattr = INVALID_FILE_ATTRIBUTES;
+	      goto out;
+	    }
+	  else if ((fileattr&  FILE_ATTRIBUTE_REPARSE_POINT)
+		&&  !fs.is_remote_drive())
+	    {
+	      if (pfci->ReparseTag != IO_REPARSE_TAG_SYMLINK
+		&&  pfci->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
+		{
+		  fileattr&= ~FILE_ATTRIBUTE_REPARSE_POINT;
+		  goto out;
+		}
+	      InitializeObjectAttributes (&attr, fname, ci_flag, dh, NULL);
+	      status = NtOpenFile (&fh, READ_CONTROL,&attr,&io,
+				   FILE_SHARE_VALID_FLAGS,
+				   FILE_OPEN_REPARSE_POINT
+				   | FILE_OPEN_FOR_BACKUP_INTENT);
+	      if (!NT_SUCCESS (status))
+		{
+		  fileattr&= ~FILE_ATTRIBUTE_REPARSE_POINT;
+		  goto out;
+		}
+	      res = check_reparse_point (fh);
+	      if (res == -1)
+		{
+		  fs.update (&upath, NULL);
+		  res = 0;
+		}
+	      else if (res)
+		conv_hdl.finfo ()->FileAttributes&= ~FILE_ATTRIBUTE_DIRECTORY;
+	      NtClose (fh);
+	    }
+	  else if ((fileattr&  (FILE_ATTRIBUTE_SYSTEM
+				| FILE_ATTRIBUTE_DIRECTORY))
+		   == FILE_ATTRIBUTE_SYSTEM)
+	    {
+	      InitializeObjectAttributes (&attr, fname, ci_flag, dh, NULL);
+	      status = NtOpenFile (&fh, SYNCHRONIZE | GENERIC_READ,&attr,
+				&io, FILE_SHARE_VALID_FLAGS,
+				   FILE_OPEN_FOR_BACKUP_INTENT);
+	      if (!NT_SUCCESS (status))
+		goto out;
+	      res = check_sysfile (fh);
+	      NtClose (fh);
+	    }
+	}
+    }
+
+out:
+  NtClose (dh);
+  syscall_printf ("%d = symlink.xcheck (%s, %p) (%p)",
+		  res, path, contents, pflags);
+  issymlink = res>  0;
+  return res;
+}
+
+int
  symlink_info::check (char *path, const suffix_info *suffixes, fs_info&fs,
  		     path_conv_handle&conv_hdl)
  {
@@ -2411,9 +2923,9 @@ restart:
  	    }
  	  else
  	    {
-	      PFILE_NETWORK_OPEN_INFORMATION pfnoi = conv_hdl.fnoi ();
+	      PFILE_CYGWIN_INFORMATION pfci = conv_hdl.finfo ();

-	      status = NtQueryInformationFile (h,&io, pfnoi, sizeof *pfnoi,
+	      status = NtQueryInformationFile (h,&io, pfci, sizeof *pfci,
  					       FileNetworkOpenInformation);
  	      if ((status == STATUS_INVALID_PARAMETER
  		   || status == STATUS_NOT_IMPLEMENTED)
@@ -2428,14 +2940,16 @@ restart:
  						   FileBasicInformation);
  		  if (NT_SUCCESS (status))
  		    {
-		      memcpy (pfnoi,&fbi, 4 * sizeof (LARGE_INTEGER));
-		      pfnoi->EndOfFile.QuadPart
-			= pfnoi->AllocationSize.QuadPart = 0;
-		      pfnoi->FileAttributes = fbi.FileAttributes;
+		      memcpy (pfci,&fbi, 4 * sizeof (LARGE_INTEGER));
+		      pfci->EndOfFile.QuadPart
+			= pfci->AllocationSize.QuadPart = 0;
+		      pfci->FileAttributes = fbi.FileAttributes;
  		    }
  		}
+	      pfci->ReparseTag = 0;
+	      pfci->FileId.QuadPart = 0;
  	      if (NT_SUCCESS (status))
-		fileattr = pfnoi->FileAttributes;
+		fileattr = pfci->FileAttributes;
  	    }
  	}
        if (!NT_SUCCESS (status))
@@ -2525,18 +3039,22 @@ restart:
  		    }
  		  else
  		    {
-		      PFILE_NETWORK_OPEN_INFORMATION pfnoi = conv_hdl.fnoi ();
+		      PFILE_CYGWIN_INFORMATION pfci = conv_hdl.finfo ();

  		      fileattr = fdi_buf.fdi.FileAttributes;
-		      memcpy (pfnoi,&fdi_buf.fdi.CreationTime, sizeof *pfnoi);
+		      memcpy (pfci,&fdi_buf.fdi.CreationTime,
+			      4 * sizeof (LARGE_INTEGER));
  		      /* Amazing, but true:  The FILE_NETWORK_OPEN_INFORMATION
  		         structure has the AllocationSize and EndOfFile members
  			 interchanged relative to the directory information
  			 classes. */
-		      pfnoi->AllocationSize.QuadPart
+		      pfci->AllocationSize.QuadPart
  			= fdi_buf.fdi.AllocationSize.QuadPart;
-		      pfnoi->EndOfFile.QuadPart
+		      pfci->EndOfFile.QuadPart
  			= fdi_buf.fdi.EndOfFile.QuadPart;
+		      pfci->FileAttributes = fdi_buf.fdi.FileAttributes;
+		      pfci->ReparseTag = fdi_buf.fdi.EaSize;
+		      pfci->FileId.QuadPart = 0;
  		    }
  		}
  	      ext_tacked_on = !!*ext_here;
@@ -2625,7 +3143,7 @@ restart:
  	  else if (res)
  	    {
  	      /* A symlink is never a directory. */
-	      conv_hdl.fnoi ()->FileAttributes&= ~FILE_ATTRIBUTE_DIRECTORY;
+	      conv_hdl.finfo ()->FileAttributes&= ~FILE_ATTRIBUTE_DIRECTORY;
  	      break;
  	    }
  	}
Index: path.h
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/path.h,v
retrieving revision 1.151
diff -u -p -r1.151 path.h
--- path.h	5 Oct 2010 14:19:17 -0000	1.151
+++ path.h	5 Oct 2010 16:41:15 -0000
@@ -59,6 +59,7 @@ enum pathconv_arg
    PC_NULLEMPTY		= 0x0020,
    PC_POSIX		= 0x0080,
    PC_NOWARN		= 0x0100,
+  PC_XCHECK		= 0x00200000,
    PC_KEEP_HANDLE	= 0x00400000,
    PC_NO_ACCESS_CHECK	= 0x00800000
  };
@@ -86,28 +87,31 @@ enum path_types
    PATH_TEXT		= 0x02000000,
    PATH_REP		= 0x04000000,
    PATH_HAS_SYMLINKS	= 0x10000000,
+  PATH_XCHECKED		= 0x20000000,
    PATH_SOCKET		= 0x40000000
  };

  class symlink_info;
-struct _FILE_NETWORK_OPEN_INFORMATION;
+
+typedef struct _FILE_CYGWIN_INFORMATION
+{
+  LARGE_INTEGER CreationTime;
+  LARGE_INTEGER LastAccessTime;
+  LARGE_INTEGER LastWriteTime;
+  LARGE_INTEGER ChangeTime;
+  LARGE_INTEGER AllocationSize;
+  LARGE_INTEGER EndOfFile;
+  ULONG FileAttributes;
+  ULONG ReparseTag;
+  LARGE_INTEGER FileId;
+} FILE_CYGWIN_INFORMATION, *PFILE_CYGWIN_INFORMATION;

  class path_conv_handle
  {
    HANDLE      hdl;
    ACCESS_MASK acc;
    union {
-    /* Identical to FILE_NETWORK_OPEN_INFORMATION.  We don't want to pull in
-       ntdll.h here, though. */
-    struct {
-      LARGE_INTEGER CreationTime;
-      LARGE_INTEGER LastAccessTime;
-      LARGE_INTEGER LastWriteTime;
-      LARGE_INTEGER ChangeTime;
-      LARGE_INTEGER AllocationSize;
-      LARGE_INTEGER EndOfFile;
-      ULONG FileAttributes;
-    } _fnoi;
+    FILE_CYGWIN_INFORMATION fci;
      /* For NFS. */
      fattr3 _fattr3;
    } attribs;
@@ -132,8 +136,8 @@ public:
    }
    inline HANDLE handle () const { return hdl; }
    inline ACCESS_MASK access () const { return acc; }
-  inline struct _FILE_NETWORK_OPEN_INFORMATION *fnoi ()
-  { return (struct _FILE_NETWORK_OPEN_INFORMATION *)&attribs._fnoi; }
+  inline PFILE_CYGWIN_INFORMATION finfo ()
+  { return (PFILE_CYGWIN_INFORMATION)&attribs.fci; }
    inline struct fattr3 *nfsattr ()
    { return (struct fattr3 *)&attribs._fattr3; }
  };
@@ -175,6 +179,7 @@ class path_conv
        return O_TEXT;
      return 0;
    }
+  int xchecked () const { return path_flags&  PATH_XCHECKED; }
    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;}
@@ -326,7 +331,7 @@ class path_conv

    HANDLE handle () const { return conv_handle.handle (); }
    ACCESS_MASK access () const { return conv_handle.access (); }
-  struct _FILE_NETWORK_OPEN_INFORMATION *fnoi () { return conv_handle.fnoi (); }
+  PFILE_CYGWIN_INFORMATION finfo () { return conv_handle.finfo (); }
    struct fattr3 *nfsattr () { return conv_handle.nfsattr (); }
    void reset_conv_handle () { conv_handle.set (NULL, 0); }
    void close_conv_handle () { conv_handle.close (); }



Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]