cygwin: unify reparse point checking code into single function
So far we had two functions checking the content of a reparse point, readdir_check_reparse_point in fhandler_disk_file.cc for the sake of readdir, and symlink_info::check_reparse_point for the sake of generic path checking. * Rename check_reparse_point_target helper to check_reparse_point_string and convert to static function. * Create new check_reparse_point_target helper containing the core reparse point checking code * Just call check_reparse_point_target from readdir_check_reparse_point and symlink_info::check_reparse_point and only perform the unique task in those functions. Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
parent
42f1be581c
commit
3e80cefb16
|
@ -161,62 +161,25 @@ path_conv::isgood_inode (ino_t ino) const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check reparse point to determine if it should be treated as a posix symlink
|
/* Check reparse point to determine if it should be treated as a
|
||||||
or as a normal file/directory. Mount points are treated as normal directories
|
posix symlink or as a normal file/directory. Logic is explained
|
||||||
to match behavior of other systems. Unknown reparse tags are used for
|
in detail in check_reparse_point_target in path.cc. */
|
||||||
things other than links (HSM, compression, dedup), and generally should be
|
|
||||||
treated as a normal file/directory. Native symlinks and mount points are
|
|
||||||
treated as posix symlinks, depending on the prefix of the target name.
|
|
||||||
This logic needs to agree with equivalent logic in path.cc
|
|
||||||
symlink_info::check_reparse_point() .
|
|
||||||
*/
|
|
||||||
static inline bool
|
static inline bool
|
||||||
readdir_check_reparse_point (POBJECT_ATTRIBUTES attr, bool remote)
|
readdir_check_reparse_point (POBJECT_ATTRIBUTES attr, bool remote)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
NTSTATUS status;
|
||||||
IO_STATUS_BLOCK io;
|
|
||||||
HANDLE reph;
|
HANDLE reph;
|
||||||
UNICODE_STRING subst;
|
IO_STATUS_BLOCK io;
|
||||||
|
tmp_pathbuf tp;
|
||||||
|
UNICODE_STRING symbuf;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
if (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, attr, &io,
|
status = NtOpenFile (&reph, READ_CONTROL, attr, &io, FILE_SHARE_VALID_FLAGS,
|
||||||
FILE_SHARE_VALID_FLAGS,
|
FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
|
||||||
FILE_OPEN_FOR_BACKUP_INTENT
|
if (NT_SUCCESS (status))
|
||||||
| FILE_OPEN_REPARSE_POINT)))
|
|
||||||
{
|
{
|
||||||
PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER)
|
PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
|
||||||
alloca (MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
|
ret = (check_reparse_point_target (reph, remote, rp, &symbuf) > 0);
|
||||||
if (NT_SUCCESS (NtFsControlFile (reph, NULL, NULL, NULL,
|
|
||||||
&io, FSCTL_GET_REPARSE_POINT, NULL, 0,
|
|
||||||
(LPVOID) rp, MAXIMUM_REPARSE_DATA_BUFFER_SIZE)))
|
|
||||||
{
|
|
||||||
/* If reparse point is stored on a remote volume, lstat returns
|
|
||||||
them as normal files or dirs, not as symlink. For a description,
|
|
||||||
see the comment preceeding remote check in
|
|
||||||
symlink_info::check_reparse_point. */
|
|
||||||
if (!remote && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
|
|
||||||
{
|
|
||||||
RtlInitCountedUnicodeString (&subst,
|
|
||||||
(WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
|
|
||||||
+ rp->MountPointReparseBuffer.SubstituteNameOffset),
|
|
||||||
rp->MountPointReparseBuffer.SubstituteNameLength);
|
|
||||||
if (check_reparse_point_target (&subst))
|
|
||||||
ret = true;
|
|
||||||
}
|
|
||||||
else if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
|
|
||||||
{
|
|
||||||
if (rp->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE)
|
|
||||||
ret = true;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RtlInitCountedUnicodeString (&subst,
|
|
||||||
(WCHAR *)((char *)rp->SymbolicLinkReparseBuffer.PathBuffer
|
|
||||||
+ rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
|
|
||||||
rp->SymbolicLinkReparseBuffer.SubstituteNameLength);
|
|
||||||
if (check_reparse_point_target (&subst))
|
|
||||||
ret = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NtClose (reph);
|
NtClose (reph);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -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
|
Win32 prefix for long pathnames! So we have to tack off
|
||||||
the prefix and convert the path to the "normal" syntax
|
the prefix and convert the path to the "normal" syntax
|
||||||
for ParseDisplayName. */
|
for ParseDisplayName. */
|
||||||
WCHAR *wc = wc_path + 4;
|
PWCHAR wc = wc_path + 4;
|
||||||
if (wc[1] != L':') /* native UNC path */
|
if (wc[1] != L':') /* native UNC path */
|
||||||
*(wc += 2) = L'\\';
|
*(wc += 2) = L'\\';
|
||||||
HRESULT res;
|
HRESULT res;
|
||||||
|
@ -2261,8 +2261,8 @@ symlink_info::check_sysfile (HANDLE h)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
static bool
|
||||||
check_reparse_point_target (PUNICODE_STRING subst)
|
check_reparse_point_string (PUNICODE_STRING subst)
|
||||||
{
|
{
|
||||||
/* Native mount points, or native non-relative symbolic links,
|
/* Native mount points, or native non-relative symbolic links,
|
||||||
can be treated as posix symlinks only if the SubstituteName
|
can be treated as posix symlinks only if the SubstituteName
|
||||||
|
@ -2286,15 +2286,17 @@ check_reparse_point_target (PUNICODE_STRING subst)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return values:
|
||||||
|
<0: Negative errno.
|
||||||
|
0: No symlink.
|
||||||
|
1: Symlink.
|
||||||
|
*/
|
||||||
int
|
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;
|
NTSTATUS status;
|
||||||
IO_STATUS_BLOCK io;
|
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
|
/* On remote drives or under heavy load, NtFsControlFile can return with
|
||||||
STATUS_PENDING. If so, instead of creating an event object, just set
|
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)
|
the followup call to NtFsControlFile(FSCTL_GET_REPARSE_POINT)
|
||||||
returns with STATUS_NOT_A_REPARSE_POINT. That's quite buggy, but
|
returns with STATUS_NOT_A_REPARSE_POINT. That's quite buggy, but
|
||||||
we cope here with this scenario by not setting an error code. */
|
we cope here with this scenario by not setting an error code. */
|
||||||
if (status != STATUS_NOT_A_REPARSE_POINT)
|
if (status == STATUS_NOT_A_REPARSE_POINT)
|
||||||
set_error (EIO);
|
return 0;
|
||||||
return 0;
|
return -EIO;
|
||||||
}
|
}
|
||||||
if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
|
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
|
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
|
C:\foo. That comes in handy since that's how symlinks are treated under
|
||||||
POSIX as well. */
|
POSIX as well. */
|
||||||
RtlInitCountedUnicodeString (&subst,
|
RtlInitCountedUnicodeString (psymbuf,
|
||||||
(WCHAR *)((char *)rp->SymbolicLinkReparseBuffer.PathBuffer
|
(PWCHAR)((PBYTE) rp->SymbolicLinkReparseBuffer.PathBuffer
|
||||||
+ rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
|
+ rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
|
||||||
rp->SymbolicLinkReparseBuffer.SubstituteNameLength);
|
rp->SymbolicLinkReparseBuffer.SubstituteNameLength);
|
||||||
if (!(rp->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) &&
|
if ((rp->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) ||
|
||||||
!check_reparse_point_target (&subst))
|
check_reparse_point_string (psymbuf))
|
||||||
{
|
return 1;
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (!remote && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
|
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
|
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
|
point to. If we handle it as symlink, it will be mistreated as
|
||||||
pointing to a dir on the local system. */
|
pointing to a dir on the local system. */
|
||||||
RtlInitCountedUnicodeString (&subst,
|
RtlInitCountedUnicodeString (psymbuf,
|
||||||
(WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
|
(PWCHAR)((PBYTE) rp->MountPointReparseBuffer.PathBuffer
|
||||||
+ rp->MountPointReparseBuffer.SubstituteNameOffset),
|
+ rp->MountPointReparseBuffer.SubstituteNameOffset),
|
||||||
rp->MountPointReparseBuffer.SubstituteNameLength);
|
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
|
/* Volume mount point. Not treated as symlink. The return
|
||||||
treated as symlink. The return value of -1 indicates name needs
|
value -EPERM is a hint for the caller to treat this as a
|
||||||
to be opened without FILE_OPEN_REPARSE_POINT flag. */
|
volume mount point. */
|
||||||
return -1;
|
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.
|
/* 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.
|
Drop REPARSE attribute so we don't try to use the flag accidentally.
|
||||||
It's just some arbitrary file or directory for us. */
|
It's just some arbitrary file or directory for us. */
|
||||||
fileattr &= ~FILE_ATTRIBUTE_REPARSE_POINT;
|
fileattr &= ~FILE_ATTRIBUTE_REPARSE_POINT;
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
sys_wcstombs (srcbuf, SYMLINK_MAX + 7, subst.Buffer,
|
/* ret is > 0, so it's a reparse point, path in symbuf. */
|
||||||
subst.Length / sizeof (WCHAR));
|
sys_wcstombs (srcbuf, SYMLINK_MAX + 7, symbuf.Buffer,
|
||||||
|
symbuf.Length / sizeof (WCHAR));
|
||||||
pflags |= PATH_SYMLINK | PATH_REP;
|
pflags |= PATH_SYMLINK | PATH_REP;
|
||||||
/* A symlink is never a directory. */
|
/* A symlink is never a directory. */
|
||||||
fileattr &= ~FILE_ATTRIBUTE_DIRECTORY;
|
fileattr &= ~FILE_ATTRIBUTE_DIRECTORY;
|
||||||
|
@ -2993,7 +3010,7 @@ restart:
|
||||||
filesystem information again, but with a NULL handle.
|
filesystem information again, but with a NULL handle.
|
||||||
This does what we want because fs_info::update opens the
|
This does what we want because fs_info::update opens the
|
||||||
handle without FILE_OPEN_REPARSE_POINT. */
|
handle without FILE_OPEN_REPARSE_POINT. */
|
||||||
if (res == -1)
|
if (res < 0)
|
||||||
fs.update (&upath, NULL);
|
fs.update (&upath, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,8 @@ enum path_types
|
||||||
};
|
};
|
||||||
|
|
||||||
NTSTATUS file_get_fai (HANDLE, PFILE_ALL_INFORMATION);
|
NTSTATUS file_get_fai (HANDLE, PFILE_ALL_INFORMATION);
|
||||||
bool check_reparse_point_target (PUNICODE_STRING);
|
int check_reparse_point_target (HANDLE, bool, PREPARSE_DATA_BUFFER,
|
||||||
|
PUNICODE_STRING);
|
||||||
|
|
||||||
class symlink_info;
|
class symlink_info;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue