Compatibility improvements to reparse point handling.
This commit is contained in:
parent
ec86124748
commit
7a4e299a18
|
@ -161,15 +161,19 @@ path_conv::isgood_inode (ino_t ino) const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check reparse point for type. IO_REPARSE_TAG_MOUNT_POINT types are
|
/* Check reparse point to determine if it should be treated as a posix symlink
|
||||||
either volume mount points, which are treated as directories, or they
|
or as a normal file/directory. Mount points are treated as normal directories
|
||||||
are directory mount points, which are treated as symlinks.
|
to match behavior of other systems. Unknown reparse tags are used for
|
||||||
IO_REPARSE_TAG_SYMLINK types are always symlinks. We don't know
|
things other than links (HSM, compression, dedup), and generally should be
|
||||||
anything about other reparse points, so they are treated as unknown. */
|
treated as a normal file/directory. Native symlinks and mount points are
|
||||||
static inline uint8_t
|
treated as posix symlinks, depending on the prefix of the target name.
|
||||||
readdir_check_reparse_point (POBJECT_ATTRIBUTES attr)
|
This logic needs to agree with equivalent logic in path.cc
|
||||||
|
symlink_info::check_reparse_point() .
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
readdir_check_reparse_point (POBJECT_ATTRIBUTES attr, bool remote)
|
||||||
{
|
{
|
||||||
uint8_t ret = DT_UNKNOWN;
|
bool ret = false;
|
||||||
IO_STATUS_BLOCK io;
|
IO_STATUS_BLOCK io;
|
||||||
HANDLE reph;
|
HANDLE reph;
|
||||||
UNICODE_STRING subst;
|
UNICODE_STRING subst;
|
||||||
|
@ -185,20 +189,29 @@ readdir_check_reparse_point (POBJECT_ATTRIBUTES attr)
|
||||||
&io, FSCTL_GET_REPARSE_POINT, NULL, 0,
|
&io, FSCTL_GET_REPARSE_POINT, NULL, 0,
|
||||||
(LPVOID) rp, MAXIMUM_REPARSE_DATA_BUFFER_SIZE)))
|
(LPVOID) rp, MAXIMUM_REPARSE_DATA_BUFFER_SIZE)))
|
||||||
{
|
{
|
||||||
if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
|
if (!remote && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
|
||||||
{
|
{
|
||||||
RtlInitCountedUnicodeString (&subst,
|
RtlInitCountedUnicodeString (&subst,
|
||||||
(WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
|
(WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
|
||||||
+ rp->MountPointReparseBuffer.SubstituteNameOffset),
|
+ rp->MountPointReparseBuffer.SubstituteNameOffset),
|
||||||
rp->MountPointReparseBuffer.SubstituteNameLength);
|
rp->MountPointReparseBuffer.SubstituteNameLength);
|
||||||
/* Only volume mountpoints are treated as directories. */
|
if (check_reparse_point_target (&subst))
|
||||||
if (RtlEqualUnicodePathPrefix (&subst, &ro_u_volume, TRUE))
|
ret = true;
|
||||||
ret = DT_DIR;
|
|
||||||
else
|
|
||||||
ret = DT_LNK;
|
|
||||||
}
|
}
|
||||||
else if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
|
else if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
|
||||||
ret = DT_LNK;
|
{
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1995,8 +2008,7 @@ fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
|
||||||
/* Set d_type if type can be determined from file attributes. For .lnk
|
/* Set d_type if type can be determined from file attributes. For .lnk
|
||||||
symlinks, d_type will be reset below. Reparse points can be NTFS
|
symlinks, d_type will be reset below. Reparse points can be NTFS
|
||||||
symlinks, even if they have the FILE_ATTRIBUTE_DIRECTORY flag set. */
|
symlinks, even if they have the FILE_ATTRIBUTE_DIRECTORY flag set. */
|
||||||
if (attr &&
|
if (attr && !(attr & ~FILE_ATTRIBUTE_VALID_FLAGS))
|
||||||
!(attr & (~FILE_ATTRIBUTE_VALID_FLAGS | FILE_ATTRIBUTE_REPARSE_POINT)))
|
|
||||||
{
|
{
|
||||||
if (attr & FILE_ATTRIBUTE_DIRECTORY)
|
if (attr & FILE_ATTRIBUTE_DIRECTORY)
|
||||||
de->d_type = DT_DIR;
|
de->d_type = DT_DIR;
|
||||||
|
@ -2005,19 +2017,22 @@ fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
|
||||||
de->d_type = DT_REG;
|
de->d_type = DT_REG;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for directory reparse point. These may be treated as a posix
|
/* Check for reparse points that can be treated as posix symlinks.
|
||||||
symlink, or as mount point, so need to figure out whether to return
|
Mountpoints and unknown or unhandled reparse points will be treated
|
||||||
a directory or link type. In all cases, returning the INO of the
|
as normal file/directory/unknown. In all cases, returning the INO of
|
||||||
reparse point (not of the target) matches behavior of posix systems.
|
the reparse point (not of the target) matches behavior of posix systems.
|
||||||
*/
|
*/
|
||||||
if ((attr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
|
if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
|
||||||
== (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
|
|
||||||
{
|
{
|
||||||
OBJECT_ATTRIBUTES oattr;
|
OBJECT_ATTRIBUTES oattr;
|
||||||
|
|
||||||
InitializeObjectAttributes (&oattr, fname, pc.objcaseinsensitive (),
|
InitializeObjectAttributes (&oattr, fname, pc.objcaseinsensitive (),
|
||||||
get_handle (), NULL);
|
get_handle (), NULL);
|
||||||
de->d_type = readdir_check_reparse_point (&oattr);
|
/* FUTURE: Ideally would know at this point if reparse point
|
||||||
|
is stored on a remote volume. Without this, may return DT_LNK
|
||||||
|
for remote names that end up lstat-ing as a normal directory. */
|
||||||
|
if (readdir_check_reparse_point (&oattr, false/*remote*/))
|
||||||
|
de->d_type = DT_LNK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for Windows shortcut. If it's a Cygwin or U/WIN symlink, drop the
|
/* Check for Windows shortcut. If it's a Cygwin or U/WIN symlink, drop the
|
||||||
|
|
|
@ -2261,6 +2261,31 @@ symlink_info::check_sysfile (HANDLE h)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
check_reparse_point_target (PUNICODE_STRING subst)
|
||||||
|
{
|
||||||
|
/* Native mount points, or native non-relative symbolic links,
|
||||||
|
can be treated as posix symlinks only if the SubstituteName
|
||||||
|
can be converted from a native NT object namespace name to
|
||||||
|
a win32 name. We only know how to convert names with two
|
||||||
|
prefixes :
|
||||||
|
"\??\UNC\..."
|
||||||
|
"\??\X:..."
|
||||||
|
Other reparse points will be treated as files or
|
||||||
|
directories, not as posix symlinks.
|
||||||
|
*/
|
||||||
|
if (RtlEqualUnicodePathPrefix (subst, &ro_u_natp, FALSE))
|
||||||
|
{
|
||||||
|
if (subst->Length >= 6*sizeof(WCHAR) && subst->Buffer[5] == L':' &&
|
||||||
|
(subst->Length == 6*sizeof(WCHAR) || subst->Buffer[6] == L'\\'))
|
||||||
|
return true;
|
||||||
|
else if (subst->Length >= 8*sizeof(WCHAR) &&
|
||||||
|
wcsncmp (subst->Buffer + 4, L"UNC\\", 4) == 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
symlink_info::check_reparse_point (HANDLE h, bool remote)
|
symlink_info::check_reparse_point (HANDLE h, bool remote)
|
||||||
{
|
{
|
||||||
|
@ -2299,14 +2324,24 @@ symlink_info::check_reparse_point (HANDLE h, bool remote)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
|
if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
|
||||||
/* Windows evaluates native symlink literally. If a remote symlink points
|
{
|
||||||
to, say, C:\foo, it will be handled as if the target is the local file
|
/* Windows evaluates native symlink literally. If a remote symlink points
|
||||||
C:\foo. That comes in handy since that's how symlinks are treated under
|
to, say, C:\foo, it will be handled as if the target is the local file
|
||||||
POSIX as well. */
|
C:\foo. That comes in handy since that's how symlinks are treated under
|
||||||
RtlInitCountedUnicodeString (&subst,
|
POSIX as well. */
|
||||||
(WCHAR *)((char *)rp->SymbolicLinkReparseBuffer.PathBuffer
|
RtlInitCountedUnicodeString (&subst,
|
||||||
+ rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
|
(WCHAR *)((char *)rp->SymbolicLinkReparseBuffer.PathBuffer
|
||||||
rp->SymbolicLinkReparseBuffer.SubstituteNameLength);
|
+ 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (!remote && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
|
else if (!remote && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
|
||||||
{
|
{
|
||||||
/* Don't handle junctions on remote filesystems as symlinks. This type
|
/* Don't handle junctions on remote filesystems as symlinks. This type
|
||||||
|
@ -2318,11 +2353,11 @@ symlink_info::check_reparse_point (HANDLE h, bool remote)
|
||||||
(WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
|
(WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
|
||||||
+ rp->MountPointReparseBuffer.SubstituteNameOffset),
|
+ rp->MountPointReparseBuffer.SubstituteNameOffset),
|
||||||
rp->MountPointReparseBuffer.SubstituteNameLength);
|
rp->MountPointReparseBuffer.SubstituteNameLength);
|
||||||
if (RtlEqualUnicodePathPrefix (&subst, &ro_u_volume, TRUE))
|
if (!check_reparse_point_target (&subst))
|
||||||
{
|
{
|
||||||
/* Volume mount point. Not treated as symlink. The return
|
/* Volume mount point, or unsupported native target prefix. Not
|
||||||
value of -1 is a hint for the caller to treat this as a
|
treated as symlink. The return value of -1 indicates name needs
|
||||||
volume mount point. */
|
to be opened without FILE_OPEN_REPARSE_POINT flag. */
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,7 @@ 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);
|
||||||
|
|
||||||
class symlink_info;
|
class symlink_info;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue