This is the mail archive of the
cygwin-developers
mailing list for the Cygwin project.
Re: Cygwin Filesystem Performance degradation 1.7.5 vs 1.7.7, and methods for improving performance
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 (); }
--
Corinna Vinschen Please, send mails regarding Cygwin to
Cygwin Project Co-Leader cygwin AT cygwin DOT com
Red Hat