* fhandler_procsys.cc (fhandler_procsys::exists): Rewrite.

(fhandler_procsys::fill_filebuf): Fill buffer with valid string even if
	reading the symlink fails.
This commit is contained in:
Corinna Vinschen 2011-03-04 17:51:42 +00:00
parent dfbab84c51
commit d5071ad7f5
2 changed files with 141 additions and 61 deletions

View File

@ -1,3 +1,9 @@
2011-03-04 Corinna Vinschen <corinna@vinschen.de>
* fhandler_procsys.cc (fhandler_procsys::exists): Rewrite.
(fhandler_procsys::fill_filebuf): Fill buffer with valid string even if
reading the symlink fails.
2011-03-03 Corinna Vinschen <corinna@vinschen.de> 2011-03-03 Corinna Vinschen <corinna@vinschen.de>
* posix_ipc.cc (ipc_cond_timedwait): If ipc_mutex_unlock fails, return * posix_ipc.cc (ipc_cond_timedwait): If ipc_mutex_unlock fails, return

View File

@ -46,22 +46,74 @@ const size_t procsys_len = sizeof (procsys) - 1;
virtual_ftype_t virtual_ftype_t
fhandler_procsys::exists (struct __stat64 *buf) fhandler_procsys::exists (struct __stat64 *buf)
{ {
UNICODE_STRING path; \ UNICODE_STRING path;
UNICODE_STRING dir;
OBJECT_ATTRIBUTES attr; OBJECT_ATTRIBUTES attr;
IO_STATUS_BLOCK io; IO_STATUS_BLOCK io;
NTSTATUS status; NTSTATUS status;
HANDLE h; HANDLE h;
FILE_BASIC_INFORMATION fbi; FILE_BASIC_INFORMATION fbi;
bool internal = false;
bool desperate_parent_check = false;
/* Default device type is character device. */ /* Default device type is character device. */
virtual_ftype_t file_type = virt_chr; virtual_ftype_t file_type = virt_chr;
if (strlen (get_name ()) == procsys_len) if (strlen (get_name ()) == procsys_len)
return virt_rootdir; return virt_rootdir;
mk_unicode_path (&path); mk_unicode_path (&path);
/* First try to open as file/device to get more info. */
/* Try to open parent dir. If it works, the object is definitely
an object within the internal namespace. We don't need to test
it for being a file or dir on the filesystem anymore. If the
error is STATUS_OBJECT_TYPE_MISMATCH, we know that the file
itself is external. Otherwise we don't know. */
RtlSplitUnicodePath (&path, &dir, NULL);
/* RtlSplitUnicodePath preserves the trailing backslash in dir. Don't
preserve it to open the dir, unless it's the object root. */
if (dir.Length > sizeof (WCHAR))
dir.Length -= sizeof (WCHAR);
InitializeObjectAttributes (&attr, &dir, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = NtOpenDirectoryObject (&h, DIRECTORY_QUERY, &attr);
debug_printf ("NtOpenDirectoryObject: %p", status);
if (NT_SUCCESS (status))
{
internal = true;
NtClose (h);
}
/* First check if the object is a symlink. */
InitializeObjectAttributes (&attr, &path, OBJ_CASE_INSENSITIVE, NULL, NULL); InitializeObjectAttributes (&attr, &path, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = NtOpenSymbolicLinkObject (&h, READ_CONTROL | SYMBOLIC_LINK_QUERY,
&attr);
debug_printf ("NtOpenSymbolicLinkObject: %p", status);
if (NT_SUCCESS (status))
{
/* If requested, check permissions. */
if (buf)
get_object_attribute (h, &buf->st_uid, &buf->st_gid, &buf->st_mode);
NtClose (h);
return virt_symlink;
}
else if (status == STATUS_ACCESS_DENIED)
return virt_symlink;
/* Then check if it's an object directory. */
status = NtOpenDirectoryObject (&h, READ_CONTROL | DIRECTORY_QUERY, &attr);
debug_printf ("NtOpenDirectoryObject: %p", status);
if (NT_SUCCESS (status))
{
/* If requested, check permissions. */
if (buf)
get_object_attribute (h, &buf->st_uid, &buf->st_gid, &buf->st_mode);
NtClose (h);
return virt_directory;
}
else if (status == STATUS_ACCESS_DENIED)
return virt_directory;
/* Next try to open as file/device. */
status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES, &attr, &io, status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES, &attr, &io,
FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT); FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT);
debug_printf ("NtOpenFile: %p", status);
/* Name is invalid, that's nothing. */
if (status == STATUS_OBJECT_NAME_INVALID) if (status == STATUS_OBJECT_NAME_INVALID)
return virt_none; return virt_none;
/* If no media is found, or we get this dreaded sharing violation, let /* If no media is found, or we get this dreaded sharing violation, let
@ -72,72 +124,86 @@ fhandler_procsys::exists (struct __stat64 *buf)
/* If file or path can't be found, let caller try again as normal file. */ /* If file or path can't be found, let caller try again as normal file. */
if (status == STATUS_OBJECT_PATH_NOT_FOUND if (status == STATUS_OBJECT_PATH_NOT_FOUND
|| status == STATUS_OBJECT_NAME_NOT_FOUND) || status == STATUS_OBJECT_NAME_NOT_FOUND)
file_type = virt_fsfile; return virt_fsfile;
/* Check for pipe errors, which make a good hint... */ /* Check for pipe errors, which make a good hint... */
else if (status >= STATUS_PIPE_NOT_AVAILABLE && status <= STATUS_PIPE_BUSY) if (status >= STATUS_PIPE_NOT_AVAILABLE && status <= STATUS_PIPE_BUSY)
file_type = virt_pipe; return virt_pipe;
else if (status == STATUS_ACCESS_DENIED) if (status == STATUS_ACCESS_DENIED && !internal)
{ {
/* Check if this is just some file or dir on a real FS to circumvent /* Check if this is just some file or dir on a real FS to circumvent
most permission problems. */ most permission problems. Don't try that on internal objects,
since NtQueryAttributesFile might crash the machine if the underlying
driver is badly written. */
status = NtQueryAttributesFile (&attr, &fbi); status = NtQueryAttributesFile (&attr, &fbi);
debug_printf ("NtQueryAttributesFile: %p", status);
if (NT_SUCCESS (status)) if (NT_SUCCESS (status))
return (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) return (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
? virt_fsdir : virt_fsfile; ? virt_fsdir : virt_fsfile;
} /* Ok, so we're desperate and the file still maybe on some filesystem.
else if (NT_SUCCESS (status)) To check this, we now split the path until we can finally access any
of the parent's. Then we fall through to check the parent type. In
contrast to the first parent check, we now check explicitely with
trailing backslash. This will fail for directories in the internal
namespace, so we won't accidentally test those. */
dir = path;
InitializeObjectAttributes (&attr, &dir, OBJ_CASE_INSENSITIVE,
NULL, NULL);
do
{
RtlSplitUnicodePath (&dir, &dir, NULL);
status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
&attr, &io, FILE_SHARE_VALID_FLAGS,
FILE_OPEN_FOR_BACKUP_INTENT);
debug_printf ("NtOpenDirectoryObject: %p", status);
if (dir.Length > sizeof (WCHAR))
dir.Length -= sizeof (WCHAR);
}
while (dir.Length > sizeof (WCHAR) && !NT_SUCCESS (status));
desperate_parent_check = true;
}
if (NT_SUCCESS (status))
{ {
NTSTATUS dev_stat;
FILE_FS_DEVICE_INFORMATION ffdi; FILE_FS_DEVICE_INFORMATION ffdi;
/* If requested, check permissions. */ /* If requested, check permissions. If this is a parent handle from
if (buf) the above desperate parent check, skip. */
if (buf && !desperate_parent_check)
get_object_attribute (h, &buf->st_uid, &buf->st_gid, &buf->st_mode); get_object_attribute (h, &buf->st_uid, &buf->st_gid, &buf->st_mode);
/* Check for the device type. */ /* Check for the device type. */
dev_stat = NtQueryVolumeInformationFile (h, &io, &ffdi, sizeof ffdi, status = NtQueryVolumeInformationFile (h, &io, &ffdi, sizeof ffdi,
FileFsDeviceInformation); FileFsDeviceInformation);
/* And check for file attributes. If we get them, we peeked into debug_printf ("NtQueryVolumeInformationFile: %p", status);
a real FS through /proc/sys. */ /* Don't call NtQueryInformationFile unless we know it's a safe type.
status = NtQueryInformationFile (h, &io, &fbi, sizeof fbi, The call is known to crash machines, if the underlying driver is
FileBasicInformation); badly written. */
NtClose (h); if (!NT_SUCCESS (status))
if (NT_SUCCESS (dev_stat))
{ {
if (ffdi.DeviceType == FILE_DEVICE_NAMED_PIPE) NtClose (h);
file_type = NT_SUCCESS (status) ? virt_pipe : virt_blk; return file_type;
else if (NT_SUCCESS (status)) }
file_type = (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) if (ffdi.DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM)
? virt_fsdir : virt_fsfile; file_type = virt_blk;
else if (ffdi.DeviceType == FILE_DEVICE_NAMED_PIPE)
file_type = internal ? virt_blk : virt_pipe;
else if (ffdi.DeviceType == FILE_DEVICE_DISK else if (ffdi.DeviceType == FILE_DEVICE_DISK
|| ffdi.DeviceType == FILE_DEVICE_CD_ROM || ffdi.DeviceType == FILE_DEVICE_CD_ROM
|| ffdi.DeviceType == FILE_DEVICE_DFS || ffdi.DeviceType == FILE_DEVICE_DFS
|| ffdi.DeviceType == FILE_DEVICE_VIRTUAL_DISK) || ffdi.DeviceType == FILE_DEVICE_VIRTUAL_DISK)
{
/* Check for file attributes. If we get them, we peeked
into a real FS through /proc/sys. */
status = NtQueryInformationFile (h, &io, &fbi, sizeof fbi,
FileBasicInformation);
debug_printf ("NtQueryInformationFile: %p", status);
if (!NT_SUCCESS (status))
file_type = virt_blk; file_type = virt_blk;
else
file_type = (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
? virt_fsdir : virt_fsfile;
} }
}
/* Then check if it's a symlink. */
status = NtOpenSymbolicLinkObject (&h, READ_CONTROL | SYMBOLIC_LINK_QUERY,
&attr);
if (NT_SUCCESS (status))
{
/* If requested, check permissions. */
if (buf)
get_object_attribute (h, &buf->st_uid, &buf->st_gid, &buf->st_mode);
NtClose (h); NtClose (h);
return virt_symlink;
} }
/* Eventually, test if it's an object directory. */
status = NtOpenDirectoryObject (&h, READ_CONTROL | DIRECTORY_QUERY, &attr);
if (NT_SUCCESS (status))
{
/* If requested, check permissions. */
if (buf)
get_object_attribute (h, &buf->st_uid, &buf->st_gid, &buf->st_mode);
NtClose (h);
return virt_directory;
}
else if (status == STATUS_ACCESS_DENIED)
return virt_directory;
/* That's it. Return type we found above. */ /* That's it. Return type we found above. */
return file_type; return file_type;
} }
@ -153,6 +219,8 @@ fhandler_procsys::fhandler_procsys ():
{ {
} }
#define UNREADABLE_SYMLINK_CONTENT "<access denied>"
bool bool
fhandler_procsys::fill_filebuf () fhandler_procsys::fill_filebuf ()
{ {
@ -162,6 +230,7 @@ fhandler_procsys::fill_filebuf ()
NTSTATUS status; NTSTATUS status;
HANDLE h; HANDLE h;
tmp_pathbuf tp; tmp_pathbuf tp;
size_t len;
mk_unicode_path (&path); mk_unicode_path (&path);
if (path.Buffer[path.Length / sizeof (WCHAR) - 1] == L'\\') if (path.Buffer[path.Length / sizeof (WCHAR) - 1] == L'\\')
@ -169,21 +238,26 @@ fhandler_procsys::fill_filebuf ()
InitializeObjectAttributes (&attr, &path, OBJ_CASE_INSENSITIVE, NULL, NULL); InitializeObjectAttributes (&attr, &path, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = NtOpenSymbolicLinkObject (&h, SYMBOLIC_LINK_QUERY, &attr); status = NtOpenSymbolicLinkObject (&h, SYMBOLIC_LINK_QUERY, &attr);
if (!NT_SUCCESS (status)) if (!NT_SUCCESS (status))
return false; goto unreadable;
RtlInitEmptyUnicodeString (&target, tp.w_get (), RtlInitEmptyUnicodeString (&target, tp.w_get (),
(NT_MAX_PATH - 1) * sizeof (WCHAR)); (NT_MAX_PATH - 1) * sizeof (WCHAR));
status = NtQuerySymbolicLinkObject (h, &target, NULL); status = NtQuerySymbolicLinkObject (h, &target, NULL);
NtClose (h); NtClose (h);
if (!NT_SUCCESS (status)) if (!NT_SUCCESS (status))
return false; goto unreadable;
size_t len = sys_wcstombs (NULL, 0, target.Buffer, len = sys_wcstombs (NULL, 0, target.Buffer, target.Length / sizeof (WCHAR));
target.Length / sizeof (WCHAR));
filebuf = (char *) crealloc_abort (filebuf, procsys_len + len + 1); filebuf = (char *) crealloc_abort (filebuf, procsys_len + len + 1);
sys_wcstombs (fnamep = stpcpy (filebuf, procsys), len + 1, target.Buffer, sys_wcstombs (fnamep = stpcpy (filebuf, procsys), len + 1, target.Buffer,
target.Length / sizeof (WCHAR)); target.Length / sizeof (WCHAR));
while ((fnamep = strchr (fnamep, '\\'))) while ((fnamep = strchr (fnamep, '\\')))
*fnamep = '/'; *fnamep = '/';
return true; return true;
unreadable:
filebuf = (char *) crealloc_abort (filebuf,
sizeof (UNREADABLE_SYMLINK_CONTENT));
strcpy (filebuf, UNREADABLE_SYMLINK_CONTENT);
return false;
} }
int int