* 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,16 +170,25 @@ 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)
{
RtlInitCountedUnicodeString (&subst,
(WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer (WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
+ rp->MountPointReparseBuffer.SubstituteNameOffset), + rp->MountPointReparseBuffer.SubstituteNameOffset),
rp->MountPointReparseBuffer.SubstituteNameLength), rp->MountPointReparseBuffer.SubstituteNameLength);
RtlEqualUnicodePathPrefix (&subst, &ro_u_volume, TRUE))) /* Only volume mountpoints are treated as directories. */
ret = true; 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); 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)
{
/* Volume mountpoints are treated as directories. We have to fix
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_SHARE_VALID_FLAGS,
FILE_OPEN_FOR_BACKUP_INTENT)))) FILE_OPEN_FOR_BACKUP_INTENT)))
{ {
de->d_ino = pc.get_ino_by_handle (reph); de->d_ino = pc.get_ino_by_handle (reph);
NtClose (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))
} {
/* Untrusted file system. Don't try to fetch inode number again. */ if (pc.isgood_inode (fai.FileId.QuadPart))
if (de->d_ino == 0) de->d_ino = fai.FileId.QuadPart;
else
/* Untrusted file system. Don't try to fetch inode
number again. */
dir->__flags &= ~dirent_set_d_ino; 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;