* fhandler_disk_file.cc (readdir_check_reparse_point): Rename from
is_volume_mountpoint. Return valid d_type value for underlying reparse point type. (readdir_get_ino): Don't rely on the handle set in pc.check. Open file here if pc.handle() is NULL. (fhandler_disk_file::readdir_helper): Try to set a correct d_type value more diligent. (fhandler_disk_file::readdir): Don't reset dirent_set_d_ino unless we're really sure it's due to an untrusted FS. Simplify usage of FileAttributes, which is 0 if buf is NULL, anyway. Set d_type correctly for faked "." and ".." entries. Improve debug output. * path.cc (symlink_info::check): Don't keep handle to volume mount point open. Explain why.
This commit is contained in:
parent
dd442880af
commit
a6c83569dc
|
@ -1,3 +1,19 @@
|
||||||
|
2010-08-20 Corinna Vinschen <corinna@vinschen.de>
|
||||||
|
|
||||||
|
* fhandler_disk_file.cc (readdir_check_reparse_point): Rename from
|
||||||
|
is_volume_mountpoint. Return valid d_type value for underlying
|
||||||
|
reparse point type.
|
||||||
|
(readdir_get_ino): Don't rely on the handle set in pc.check. Open
|
||||||
|
file here if pc.handle() is NULL.
|
||||||
|
(fhandler_disk_file::readdir_helper): Try to set a correct d_type value
|
||||||
|
more diligent.
|
||||||
|
(fhandler_disk_file::readdir): Don't reset dirent_set_d_ino unless
|
||||||
|
we're really sure it's due to an untrusted FS. Simplify usage of
|
||||||
|
FileAttributes, which is 0 if buf is NULL, anyway. Set d_type
|
||||||
|
correctly for faked "." and ".." entries. Improve debug output.
|
||||||
|
* path.cc (symlink_info::check): Don't keep handle to volume mount
|
||||||
|
point open. Explain why.
|
||||||
|
|
||||||
2010-08-20 Corinna Vinschen <corinna@vinschen.de>
|
2010-08-20 Corinna Vinschen <corinna@vinschen.de>
|
||||||
|
|
||||||
* fhandler_disk_file.cc (fhandler_disk_file::fstatvfs): Revert usage
|
* fhandler_disk_file.cc (fhandler_disk_file::fstatvfs): Revert usage
|
||||||
|
|
|
@ -148,10 +148,15 @@ path_conv::isgood_inode (__ino64_t ino) const
|
||||||
return hasgood_inode () && (ino > UINT32_MAX || !isremote () || fs_is_nfs ());
|
return hasgood_inode () && (ino > UINT32_MAX || !isremote () || fs_is_nfs ());
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool
|
/* Check reparse point for type. IO_REPARSE_TAG_MOUNT_POINT types are
|
||||||
is_volume_mountpoint (POBJECT_ATTRIBUTES attr)
|
either volume mount points, which are treated as directories, or they
|
||||||
|
are directory mount points, which are treated as symlinks.
|
||||||
|
IO_REPARSE_TAG_SYMLINK types are always symlinks. We don't know
|
||||||
|
anything about other reparse points, so they are treated as unknown. */
|
||||||
|
static inline int
|
||||||
|
readdir_check_reparse_point (POBJECT_ATTRIBUTES attr)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
DWORD ret = DT_UNKNOWN;
|
||||||
IO_STATUS_BLOCK io;
|
IO_STATUS_BLOCK io;
|
||||||
HANDLE reph;
|
HANDLE reph;
|
||||||
UNICODE_STRING subst;
|
UNICODE_STRING subst;
|
||||||
|
@ -165,15 +170,24 @@ is_volume_mountpoint (POBJECT_ATTRIBUTES attr)
|
||||||
alloca (MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
|
alloca (MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
|
||||||
if (NT_SUCCESS (NtFsControlFile (reph, NULL, NULL, NULL,
|
if (NT_SUCCESS (NtFsControlFile (reph, NULL, NULL, NULL,
|
||||||
&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)))
|
||||||
&& rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT
|
{
|
||||||
&& (RtlInitCountedUnicodeString (&subst,
|
if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
|
||||||
(WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
|
{
|
||||||
+ rp->MountPointReparseBuffer.SubstituteNameOffset),
|
RtlInitCountedUnicodeString (&subst,
|
||||||
rp->MountPointReparseBuffer.SubstituteNameLength),
|
(WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
|
||||||
RtlEqualUnicodePathPrefix (&subst, &ro_u_volume, TRUE)))
|
+ rp->MountPointReparseBuffer.SubstituteNameOffset),
|
||||||
ret = true;
|
rp->MountPointReparseBuffer.SubstituteNameLength);
|
||||||
NtClose (reph);
|
/* Only volume mountpoints are treated as directories. */
|
||||||
|
if (RtlEqualUnicodePathPrefix (&subst, &ro_u_volume, TRUE))
|
||||||
|
ret = DT_DIR;
|
||||||
|
else
|
||||||
|
ret = DT_LNK;
|
||||||
|
}
|
||||||
|
else if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
|
||||||
|
ret = DT_LNK;
|
||||||
|
NtClose (reph);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1783,6 +1797,8 @@ readdir_get_ino (const char *path, bool dot_dot)
|
||||||
char *fname;
|
char *fname;
|
||||||
struct __stat64 st;
|
struct __stat64 st;
|
||||||
HANDLE hdl;
|
HANDLE hdl;
|
||||||
|
OBJECT_ATTRIBUTES attr;
|
||||||
|
IO_STATUS_BLOCK io;
|
||||||
__ino64_t ino = 0;
|
__ino64_t ino = 0;
|
||||||
|
|
||||||
if (dot_dot)
|
if (dot_dot)
|
||||||
|
@ -1802,7 +1818,14 @@ readdir_get_ino (const char *path, bool dot_dot)
|
||||||
}
|
}
|
||||||
else if (!pc.hasgood_inode ())
|
else if (!pc.hasgood_inode ())
|
||||||
ino = hash_path_name (0, pc.get_nt_native_path ());
|
ino = hash_path_name (0, pc.get_nt_native_path ());
|
||||||
else if ((hdl = pc.handle ()) != NULL)
|
else if ((hdl = pc.handle ()) != NULL
|
||||||
|
|| NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
|
||||||
|
pc.get_object_attr (attr, sec_none_nih),
|
||||||
|
&io, FILE_SHARE_VALID_FLAGS,
|
||||||
|
FILE_OPEN_FOR_BACKUP_INTENT
|
||||||
|
| (pc.is_rep_symlink ()
|
||||||
|
? FILE_OPEN_REPARSE_POINT : 0)))
|
||||||
|
)
|
||||||
{
|
{
|
||||||
ino = pc.get_ino_by_handle (hdl);
|
ino = pc.get_ino_by_handle (hdl);
|
||||||
if (!ino)
|
if (!ino)
|
||||||
|
@ -1830,17 +1853,16 @@ fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
|
||||||
dir->__flags &= ~dirent_set_d_ino;
|
dir->__flags &= ~dirent_set_d_ino;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set d_type if type can be determined from file attributes.
|
/* Set d_type if type can be determined from file attributes. For .lnk
|
||||||
FILE_ATTRIBUTE_SYSTEM ommitted to leave DT_UNKNOWN for old symlinks.
|
symlinks, d_type will be reset below. Reparse points can be NTFS
|
||||||
For new symlinks, d_type will be reset to DT_UNKNOWN below. */
|
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)))
|
||||||
| FILE_ATTRIBUTE_SYSTEM
|
|
||||||
| FILE_ATTRIBUTE_REPARSE_POINT)))
|
|
||||||
{
|
{
|
||||||
if (attr & FILE_ATTRIBUTE_DIRECTORY)
|
if (attr & FILE_ATTRIBUTE_DIRECTORY)
|
||||||
de->d_type = DT_DIR;
|
de->d_type = DT_DIR;
|
||||||
else
|
/* FILE_ATTRIBUTE_SYSTEM might denote system-bit type symlinks. */
|
||||||
|
else if (!(attr & FILE_ATTRIBUTE_SYSTEM))
|
||||||
de->d_type = DT_REG;
|
de->d_type = DT_REG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1855,19 +1877,29 @@ fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
|
||||||
|
|
||||||
InitializeObjectAttributes (&attr, fname, pc.objcaseinsensitive (),
|
InitializeObjectAttributes (&attr, fname, pc.objcaseinsensitive (),
|
||||||
get_handle (), NULL);
|
get_handle (), NULL);
|
||||||
if (is_volume_mountpoint (&attr)
|
de->d_type = readdir_check_reparse_point (&attr);
|
||||||
&& (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, &attr, &io,
|
if (de->d_type == DT_DIR)
|
||||||
FILE_SHARE_VALID_FLAGS,
|
|
||||||
FILE_OPEN_FOR_BACKUP_INTENT))))
|
|
||||||
{
|
{
|
||||||
de->d_ino = pc.get_ino_by_handle (reph);
|
/* Volume mountpoints are treated as directories. We have to fix
|
||||||
NtClose (reph);
|
the inode number, otherwise we have the inode number of the
|
||||||
|
mount point, rather than the inode number of the toplevel
|
||||||
|
directory of the mounted drive. */
|
||||||
|
if (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, &attr, &io,
|
||||||
|
FILE_SHARE_VALID_FLAGS,
|
||||||
|
FILE_OPEN_FOR_BACKUP_INTENT)))
|
||||||
|
{
|
||||||
|
de->d_ino = pc.get_ino_by_handle (reph);
|
||||||
|
NtClose (reph);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for Windows shortcut. If it's a Cygwin or U/WIN
|
/* Check for Windows shortcut. If it's a Cygwin or U/WIN symlink, drop the
|
||||||
symlink, drop the .lnk suffix. */
|
.lnk suffix and set d_type accordingly. */
|
||||||
if ((attr & FILE_ATTRIBUTE_READONLY) && fname->Length > 4 * sizeof (WCHAR))
|
if ((attr & (FILE_ATTRIBUTE_DIRECTORY
|
||||||
|
| FILE_ATTRIBUTE_REPARSE_POINT
|
||||||
|
| FILE_ATTRIBUTE_READONLY)) == FILE_ATTRIBUTE_READONLY
|
||||||
|
&& fname->Length > 4 * sizeof (WCHAR))
|
||||||
{
|
{
|
||||||
UNICODE_STRING uname;
|
UNICODE_STRING uname;
|
||||||
|
|
||||||
|
@ -1892,10 +1924,20 @@ fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
|
||||||
fbuf.Length -= 2 * sizeof (WCHAR);
|
fbuf.Length -= 2 * sizeof (WCHAR);
|
||||||
}
|
}
|
||||||
path_conv fpath (&fbuf, PC_SYM_NOFOLLOW);
|
path_conv fpath (&fbuf, PC_SYM_NOFOLLOW);
|
||||||
if (fpath.issymlink () || fpath.is_fs_special ())
|
if (fpath.issymlink ())
|
||||||
{
|
{
|
||||||
fname->Length -= 4 * sizeof (WCHAR);
|
fname->Length -= 4 * sizeof (WCHAR);
|
||||||
de->d_type = DT_UNKNOWN;
|
de->d_type = DT_LNK;
|
||||||
|
}
|
||||||
|
else if (fpath.isfifo ())
|
||||||
|
{
|
||||||
|
fname->Length -= 4 * sizeof (WCHAR);
|
||||||
|
de->d_type = DT_FIFO;
|
||||||
|
}
|
||||||
|
else if (fpath.is_fs_special ())
|
||||||
|
{
|
||||||
|
fname->Length -= 4 * sizeof (WCHAR);
|
||||||
|
de->d_type = S_ISCHR (fpath.dev.mode) ? DT_CHR : DT_BLK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2094,23 +2136,35 @@ go_ahead:
|
||||||
| FILE_OPEN_REPARSE_POINT);
|
| FILE_OPEN_REPARSE_POINT);
|
||||||
if (NT_SUCCESS (f_status))
|
if (NT_SUCCESS (f_status))
|
||||||
{
|
{
|
||||||
de->d_ino = pc.get_ino_by_handle (hdl);
|
/* We call NtQueryInformationFile here, rather than
|
||||||
|
pc.get_ino_by_handle(), otherwise we can't short-circuit
|
||||||
|
dirent_set_d_ino correctly. */
|
||||||
|
FILE_INTERNAL_INFORMATION fai;
|
||||||
|
f_status = NtQueryInformationFile (hdl, &io, &fai, sizeof fai,
|
||||||
|
FileInternalInformation);
|
||||||
NtClose (hdl);
|
NtClose (hdl);
|
||||||
|
if (NT_SUCCESS (f_status))
|
||||||
|
{
|
||||||
|
if (pc.isgood_inode (fai.FileId.QuadPart))
|
||||||
|
de->d_ino = fai.FileId.QuadPart;
|
||||||
|
else
|
||||||
|
/* Untrusted file system. Don't try to fetch inode
|
||||||
|
number again. */
|
||||||
|
dir->__flags &= ~dirent_set_d_ino;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Untrusted file system. Don't try to fetch inode number again. */
|
|
||||||
if (de->d_ino == 0)
|
|
||||||
dir->__flags &= ~dirent_set_d_ino;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
|
if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
|
||||||
buf ? FileAttributes : 0, &fname)))
|
FileAttributes, &fname)))
|
||||||
dir->__d_position++;
|
dir->__d_position++;
|
||||||
else if (!(dir->__flags & dirent_saw_dot))
|
else if (!(dir->__flags & dirent_saw_dot))
|
||||||
{
|
{
|
||||||
strcpy (de->d_name , ".");
|
strcpy (de->d_name , ".");
|
||||||
de->d_ino = pc.get_ino_by_handle (get_handle ());
|
de->d_ino = pc.get_ino_by_handle (get_handle ());
|
||||||
|
de->d_type = DT_DIR;
|
||||||
dir->__d_position++;
|
dir->__d_position++;
|
||||||
dir->__flags |= dirent_saw_dot;
|
dir->__flags |= dirent_saw_dot;
|
||||||
res = 0;
|
res = 0;
|
||||||
|
@ -2122,13 +2176,15 @@ go_ahead:
|
||||||
de->d_ino = readdir_get_ino (get_name (), true);
|
de->d_ino = readdir_get_ino (get_name (), true);
|
||||||
else
|
else
|
||||||
de->d_ino = pc.get_ino_by_handle (get_handle ());
|
de->d_ino = pc.get_ino_by_handle (get_handle ());
|
||||||
|
de->d_type = DT_DIR;
|
||||||
dir->__d_position++;
|
dir->__d_position++;
|
||||||
dir->__flags |= dirent_saw_dot_dot;
|
dir->__flags |= dirent_saw_dot_dot;
|
||||||
res = 0;
|
res = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
syscall_printf ("%d = readdir (%p, %p) (L\"%lS\" > \"%ls\")", res, dir, &de,
|
syscall_printf ("%d = readdir (%p, %p) (L\"%lS\" > \"%ls\") (attr %p > type %d)",
|
||||||
res ? NULL : &fname, res ? "***" : de->d_name);
|
res, dir, &de, res ? NULL : &fname, res ? "***" : de->d_name,
|
||||||
|
FileAttributes, de->d_type);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2533,6 +2533,11 @@ restart:
|
||||||
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. */
|
||||||
fs.update (&upath, NULL);
|
fs.update (&upath, NULL);
|
||||||
|
/* Make sure the open handle is not used in later stat calls.
|
||||||
|
The handle has been opened with the FILE_OPEN_REPARSE_POINT
|
||||||
|
flag, so it's a handle to the reparse point, not a handle
|
||||||
|
to the volumes root dir. */
|
||||||
|
pflags &= ~PC_KEEP_HANDLE;
|
||||||
}
|
}
|
||||||
else if (res)
|
else if (res)
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue