summaryrefslogtreecommitdiffstats
path: root/winsup/cygwin/path.cc
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/path.cc')
-rw-r--r--winsup/cygwin/path.cc89
1 files changed, 53 insertions, 36 deletions
diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
index 29d60b869..475c99599 100644
--- a/winsup/cygwin/path.cc
+++ b/winsup/cygwin/path.cc
@@ -1898,7 +1898,7 @@ symlink_worker (const char *oldpath, const char *newpath, bool isdevice)
Win32 prefix for long pathnames! So we have to tack off
the prefix and convert the path to the "normal" syntax
for ParseDisplayName. */
- WCHAR *wc = wc_path + 4;
+ PWCHAR wc = wc_path + 4;
if (wc[1] != L':') /* native UNC path */
*(wc += 2) = L'\\';
HRESULT res;
@@ -2261,8 +2261,8 @@ symlink_info::check_sysfile (HANDLE h)
return res;
}
-bool
-check_reparse_point_target (PUNICODE_STRING subst)
+static bool
+check_reparse_point_string (PUNICODE_STRING subst)
{
/* Native mount points, or native non-relative symbolic links,
can be treated as posix symlinks only if the SubstituteName
@@ -2286,15 +2286,17 @@ check_reparse_point_target (PUNICODE_STRING subst)
return false;
}
+/* Return values:
+ <0: Negative errno.
+ 0: No symlink.
+ 1: Symlink.
+*/
int
-symlink_info::check_reparse_point (HANDLE h, bool remote)
+check_reparse_point_target (HANDLE h, bool remote, PREPARSE_DATA_BUFFER rp,
+ PUNICODE_STRING psymbuf)
{
- tmp_pathbuf tp;
NTSTATUS status;
IO_STATUS_BLOCK io;
- PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
- UNICODE_STRING subst;
- char srcbuf[SYMLINK_MAX + 7];
/* On remote drives or under heavy load, NtFsControlFile can return with
STATUS_PENDING. If so, instead of creating an event object, just set
@@ -2319,9 +2321,9 @@ symlink_info::check_reparse_point (HANDLE h, bool remote)
the followup call to NtFsControlFile(FSCTL_GET_REPARSE_POINT)
returns with STATUS_NOT_A_REPARSE_POINT. That's quite buggy, but
we cope here with this scenario by not setting an error code. */
- if (status != STATUS_NOT_A_REPARSE_POINT)
- set_error (EIO);
- return 0;
+ if (status == STATUS_NOT_A_REPARSE_POINT)
+ return 0;
+ return -EIO;
}
if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
{
@@ -2329,18 +2331,13 @@ symlink_info::check_reparse_point (HANDLE h, bool remote)
to, say, C:\foo, it will be handled as if the target is the local file
C:\foo. That comes in handy since that's how symlinks are treated under
POSIX as well. */
- RtlInitCountedUnicodeString (&subst,
- (WCHAR *)((char *)rp->SymbolicLinkReparseBuffer.PathBuffer
- + rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
- rp->SymbolicLinkReparseBuffer.SubstituteNameLength);
- if (!(rp->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) &&
- !check_reparse_point_target (&subst))
- {
- /* Unsupport native symlink target prefix. Not treated as symlink.
- The return value of -1 indicates name needs to be opened without
- FILE_OPEN_REPARSE_POINT flag. */
- return -1;
- }
+ RtlInitCountedUnicodeString (psymbuf,
+ (PWCHAR)((PBYTE) rp->SymbolicLinkReparseBuffer.PathBuffer
+ + rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
+ rp->SymbolicLinkReparseBuffer.SubstituteNameLength);
+ if ((rp->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) ||
+ check_reparse_point_string (psymbuf))
+ return 1;
}
else if (!remote && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
{
@@ -2349,28 +2346,48 @@ symlink_info::check_reparse_point (HANDLE h, bool remote)
target of the junction is the remote directory it is supposed to
point to. If we handle it as symlink, it will be mistreated as
pointing to a dir on the local system. */
- RtlInitCountedUnicodeString (&subst,
- (WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
- + rp->MountPointReparseBuffer.SubstituteNameOffset),
+ RtlInitCountedUnicodeString (psymbuf,
+ (PWCHAR)((PBYTE) rp->MountPointReparseBuffer.PathBuffer
+ + rp->MountPointReparseBuffer.SubstituteNameOffset),
rp->MountPointReparseBuffer.SubstituteNameLength);
- if (!check_reparse_point_target (&subst))
+ if (RtlEqualUnicodePathPrefix (psymbuf, &ro_u_volume, TRUE))
{
- /* Volume mount point, or unsupported native target prefix. Not
- treated as symlink. The return value of -1 indicates name needs
- to be opened without FILE_OPEN_REPARSE_POINT flag. */
- return -1;
+ /* Volume mount point. Not treated as symlink. The return
+ value -EPERM is a hint for the caller to treat this as a
+ volume mount point. */
+ return -EPERM;
}
+ if (check_reparse_point_string (psymbuf))
+ return 1;
}
- else
+ return 0;
+}
+
+int
+symlink_info::check_reparse_point (HANDLE h, bool remote)
+{
+ tmp_pathbuf tp;
+ PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
+ UNICODE_STRING symbuf;
+ char srcbuf[SYMLINK_MAX + 7];
+
+ int ret = check_reparse_point_target (h, remote, rp, &symbuf);
+ if (ret <= 0)
{
+ if (ret == -EIO)
+ {
+ set_error (EIO);
+ return 0;
+ }
/* Maybe it's a reparse point, but it's certainly not one we recognize.
Drop REPARSE attribute so we don't try to use the flag accidentally.
It's just some arbitrary file or directory for us. */
fileattr &= ~FILE_ATTRIBUTE_REPARSE_POINT;
- return 0;
+ return ret;
}
- sys_wcstombs (srcbuf, SYMLINK_MAX + 7, subst.Buffer,
- subst.Length / sizeof (WCHAR));
+ /* ret is > 0, so it's a reparse point, path in symbuf. */
+ sys_wcstombs (srcbuf, SYMLINK_MAX + 7, symbuf.Buffer,
+ symbuf.Length / sizeof (WCHAR));
pflags |= PATH_SYMLINK | PATH_REP;
/* A symlink is never a directory. */
fileattr &= ~FILE_ATTRIBUTE_DIRECTORY;
@@ -2993,7 +3010,7 @@ restart:
filesystem information again, but with a NULL handle.
This does what we want because fs_info::update opens the
handle without FILE_OPEN_REPARSE_POINT. */
- if (res == -1)
+ if (res < 0)
fs.update (&upath, NULL);
}
}