* 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:
Corinna Vinschen 2010-08-20 11:18:58 +00:00
parent dd442880af
commit a6c83569dc
3 changed files with 115 additions and 38 deletions

View File

@ -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

View File

@ -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;
} }

View File

@ -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;