From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2155) id 282DC3852A47; Mon, 12 Dec 2022 20:59:43 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 282DC3852A47 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1670878783; bh=kDQDht7CV2hbafa6Xx3fFfLvmsRhbd6sz2D2qO+55mY=; h=From:To:Subject:Date:From; b=ak5O38g6WHzhpT9QSScOjIrDYRBB7Yl7Mfc2Sq6uc5oCm5KwysYAkzXS/7Jk+PhGf wbexEf3aXwKmRp0Aj2jdF9Lo3Wufq5GgPf7OHZ2RIulp+OTlStB3RXseN30vhsh5k3 Ju60n/zgrisH5jgbFu2vjMOU9OyHjnTZxgF8ZI6w= Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable From: Corinna Vinschen To: cygwin-cvs@sourceware.org Subject: [newlib-cygwin] Cygwin: path handling: never substitute virtual drive with target path X-Act-Checkin: newlib-cygwin X-Git-Author: Corinna Vinschen X-Git-Refname: refs/heads/master X-Git-Oldrev: 45794e0d66866d928349134ed3b91aeec5477009 X-Git-Newrev: 5ba5e09b9d39feaedc9be0dcd88d085b256c5d40 Message-Id: <20221212205943.282DC3852A47@sourceware.org> Date: Mon, 12 Dec 2022 20:59:43 +0000 (GMT) List-Id: https://sourceware.org/git/gitweb.cgi?p=3Dnewlib-cygwin.git;h=3D5ba5e09b9d3= 9feaedc9be0dcd88d085b256c5d40 commit 5ba5e09b9d39feaedc9be0dcd88d085b256c5d40 Author: Corinna Vinschen AuthorDate: Mon Dec 12 21:56:14 2022 +0100 Commit: Corinna Vinschen CommitDate: Mon Dec 12 21:56:14 2022 +0100 Cygwin: path handling: never substitute virtual drive with target path =20 Move the drive substitution code after the call to GetFinalPathNameByHandleW into a local function revert_virtual_drive and add code to handle non-remote virtual drives, i. e., those created with the subst command. (Try to) make sure that virtual drives are never treated like symlinks. =20 Fixes: 19d59ce75d53 ("Cygwin: path_conv: Rework handling native symlink= s as inner path components") Signed-off-by: Corinna Vinschen Diff: --- winsup/cygwin/path.cc | 149 +++++++++++++++++++++++++++++-----------------= ---- 1 file changed, 87 insertions(+), 62 deletions(-) diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index e7619270a269..c044d5295ed5 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -3024,6 +3024,65 @@ symlink_info::parse_device (const char *contents) return isdevice =3D true; } =20 +/* Probably we have a virtual drive input path and the resulting full path + starts with the substitution. Retrieve the target path of the virtual + drive and try to revert what GetFinalPathNameByHandleW did to the + drive letter. */ +static bool +revert_virtual_drive (PUNICODE_STRING upath, PUNICODE_STRING fpath, + bool is_remote, ULONG ci_flag) +{ + /* Get the drive's target path. */ + WCHAR drive[3] =3D {(WCHAR) towupper (upath->Buffer[4]), L':', L'\0'}; + WCHAR target[MAX_PATH]; + UNICODE_STRING tpath; + WCHAR *p; + + DWORD remlen =3D QueryDosDeviceW (drive, target, MAX_PATH); + if (remlen < 3) + return false; + remlen -=3D 2; /* Two L'\0' */ + + if (target[remlen - 1] =3D=3D L'\\') + remlen--; + RtlInitCountedUnicodeString (&tpath, target, remlen * sizeof (WCHAR)); + + const USHORT uncp_len =3D is_remote ? ro_u_uncp.Length / sizeof (WCHAR) = - 1 : 0; + + if (is_remote) + { + /* target path starts with \??\UNC\. */ + if (RtlEqualUnicodePathPrefix (&tpath, &ro_u_uncp, TRUE)) + { + remlen -=3D uncp_len; + p =3D target + uncp_len; + } + /* target path starts with \Device\. */ + else if ((p =3D wcschr (target, L';')) + && p + 3 < target + remlen + && wcsncmp (p + 1, drive, 2) =3D=3D 0 + && (p =3D wcschr (p + 3, L'\\'))) + remlen -=3D p - target; + else + return false; + if (wcsncasecmp (fpath->Buffer + uncp_len, p, remlen)) + return false; + } + else if (!RtlEqualUnicodePathPrefix (fpath, &tpath, TRUE)) + return false; + /* Replace fpath with source drive letter and append reminder of + final path after skipping target path */ + fpath->Buffer[4] =3D drive[0]; /* Drive letter */ + fpath->Buffer[5] =3D L':'; + WCHAR *to =3D fpath->Buffer + 6; /* Next to L':' */ + WCHAR *from =3D fpath->Buffer + uncp_len + remlen; + memmove (to, from, (wcslen (from) + 1) * sizeof (WCHAR)); + fpath->Length -=3D (from - to) * sizeof (WCHAR); + if (RtlEqualUnicodeString (upath, fpath, !!ci_flag)) + return false; + return true; +} + /* Check if PATH is a symlink. PATH must be a valid Win32 path name. =20 If PATH is a symlink, put the value of the symlink--the file to @@ -3343,6 +3402,14 @@ restart: continue; } =20 + /* Consider the situation where a virtual drive points to a native + symlink. Opening the virtual drive with FILE_OPEN_REPARSE_POINT + actually opens the symlink. If this symlink points to another + directory using a relative path, symlink evaluation goes totally + awry. We never want a virtual drive evaluated as symlink. */ + if (upath.Length <=3D 14) + goto file_not_symlink; + /* Reparse points are potentially symlinks. This check must be performed before checking the SYSTEM attribute for sysfile symlinks, since reparse points can have this flag set, too. */ @@ -3490,80 +3557,38 @@ restart: RtlInitCountedUnicodeString (&fpath, fpbuf, ret * sizeof (WCHAR)); if (!RtlEqualUnicodeString (&upath, &fpath, !!ci_flag)) { - /* Check if the final path is an UNC path and the incoming - path isn't. If so... */ - if (RtlEqualUnicodePathPrefix (&fpath, &ro_u_uncp, TRUE) - && !RtlEqualUnicodePathPrefix (&upath, &ro_u_uncp, TRUE)) + /* If the incoming path is a local drive letter path... */ + if (!RtlEqualUnicodePathPrefix (&upath, &ro_u_uncp, TRUE)) { - /* ...get the remote path, replace remote path - with drive letter, check again. */ - WCHAR drive[3] =3D - {(WCHAR) towupper (upath.Buffer[4]), L':', L'\0'}; - WCHAR remote[MAX_PATH]; - - DWORD remlen =3D QueryDosDeviceW (drive, remote, MAX_PATH); - if (remlen < 3) - goto file_not_symlink; /* fallback */ - remlen -=3D 2; /* Two L'\0' */ - - if (remote[remlen - 1] =3D=3D L'\\') - remlen--; - WCHAR *p; - UNICODE_STRING rpath; - RtlInitCountedUnicodeString (&rpath, remote, - remlen * sizeof (WCHAR)); - const USHORT uncp_len =3D - ro_u_uncp.Length / sizeof (WCHAR) - 1; - if (RtlEqualUnicodePathPrefix (&rpath, &ro_u_uncp, TRUE)) + /* ...and the final path is an UNC path, revert to the + drive letter path syntax. */ + if (RtlEqualUnicodePathPrefix (&fpath, &ro_u_uncp, TRUE)) + { + if (!revert_virtual_drive (&upath, &fpath, true, + ci_flag)) + goto file_not_symlink; + } + /* ...otherwise, if the final path changes the drive + letter, let revert_virtual_drive check for a + virtual drive and revert that. */ + else if (upath.Buffer[5] =3D=3D L':' + && (WCHAR) towupper (upath.Buffer[4]) + !=3D (WCHAR) towupper (fpath.Buffer[4])) { - remlen -=3D uncp_len; - p =3D remote + uncp_len; + if (!revert_virtual_drive (&upath, &fpath, false, + ci_flag)) + goto file_not_symlink; } - else if ((p =3D wcschr (remote, L';')) - && p + 3 < remote + remlen - && wcsncmp (p + 1, drive, 2) =3D=3D 0 - && (p =3D wcschr (p + 3, L'\\'))) - remlen -=3D p - remote; - else - goto file_not_symlink; /* fallback */ - if (wcsncasecmp (fpath.Buffer + uncp_len, p, remlen)) - goto file_not_symlink; /* fallback (not expected) */ - /* Hackfest */ - fpath.Buffer[4] =3D drive[0]; /* Drive letter */ - fpath.Buffer[5] =3D L':'; - WCHAR *to =3D fpath.Buffer + 6; /* Next to L':' */ - WCHAR *from =3D fpath.Buffer + uncp_len + remlen; - memmove (to, from, - (wcslen (from) + 1) * sizeof (WCHAR)); - fpath.Length -=3D (from - to) * sizeof (WCHAR); - if (RtlEqualUnicodeString (&upath, &fpath, !!ci_flag)) - goto file_not_symlink; } issymlink =3D true; /* upath.Buffer is big enough and unused from this point on. Reuse it here, avoiding yet another buffer allocation. */ char *nfpath =3D (char *) upath.Buffer; sys_wcstombs (nfpath, NT_MAX_PATH, fpbuf); - res =3D posixify (nfpath); - - /* If the incoming path consisted of a drive prefix only, - we just handle a virtual drive, created with, e.g. - - subst X: C:\foo\bar - - Treat it like a symlink. This is required to tell an - lstat caller that the "drive" is actually pointing - somewhere else, thus, it's a symlink in POSIX speak. */ - if (upath.Length =3D=3D 14) /* \??\X:\ */ - { - fileattr &=3D ~FILE_ATTRIBUTE_DIRECTORY; - path_flags |=3D PATH_SYMLINK; - } /* For final paths differing in inner path components return length as negative value. This informs path_conv::check to skip realpath handling on the last path component. */ - else - res =3D -res; + res =3D -posixify (nfpath); break; } }