* dtable.cc (dtable::dup_worker): Reset path_conv handle in duplicated
fhandler. * fhandler.cc (fhandler_base::fstatvfs): Keep handle in created path_conv. * fhandler.h (fhandler_base::get_stat_access): New method. (fhandler_base::get_stat_handle): New method. * fhandler_disk_file.cc (fhandler_base::fstat_by_handle): Use handle returned by get_stat_handle. Only request inode from system if it isn't already set in the fhandler, and only for filesystems supporting them. (fhandler_base::fstat_fs): Use handle returned by get_stat_handle. Change the way open_fs is called. Explain why. (fhandler_base::fstat_helper): Use handle returned by get_stat_handle. Never use 0 inode number. Simplify executable recognition by re-using get_stat_handle if file could be opened with sufficient rights. (fhandler_disk_file::fstatvfs): Use handle returned by get_stat_handle. (fhandler_disk_file::facl): Use handle returned by get_stat_handle in GETACL and GETACLCNT cases. (fhandler_disk_file::link): Use handle returned by get_stat_handle instead of opening file here again. Add comment. (readdir_get_ino): Keep handle in created path_conv and drop opening file. * ntdll.h (wait_pending): New helper function. * path.cc (symlink_info::check): Drop unused 'opt' parameter from declaration. Add path_conv_handle argument. (path_conv::check): Make sure conv_handle is closed. Keep PC_KEEP_HANDLE flag in pflags_or. Accommodate call to sym.check to new args. (path_conv::~path_conv): Close conv_handle. (symlink_info::check_shortcut): Don't re-open file here, just use incoming handle. Drop goto's and label out. (symlink_info::check_sysfile): Don't re-open file here, just use incoming handle. Keep track of file position to accommodate the fact that file has been opened asynchronously in calling function. (symlink_info::check_nfs_symlink): Don't re-open file here, just use incoming handle. (symlink_info::check): Drop unused 'opt' parameter. Add path_conv_handle argument. Always try to open file with GENERIC_READ rights first to allow reading file content w/o having to re-open the file. Drop back to READ_CONTROL | FILE_READ_ATTRIBUTES otherwise. Call symlink test functions (except for check_reparse_point) only if file could be opened with GENERIC_READ. Keep file handle open if PC_KEEP_HANDLE is set in pflags. * path.h (enum pathconv_arg): Add PC_KEEP_HANDLE flag. (class path_conv_handle): New class. (class path_conv): Add conv_handle member. (path_conv::operator =): Duplicate conv_handle. (path_conv::handle): New method. (path_conv::access): New method. (path_conv::reset_conv_handle): New method. (path_conv::close_conv_handle): New method.
This commit is contained in:
@@ -333,6 +333,7 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf)
|
||||
{
|
||||
NTSTATUS status;
|
||||
IO_STATUS_BLOCK io;
|
||||
HANDLE h = get_stat_handle ();
|
||||
|
||||
if (pc.fs_is_nfs ())
|
||||
return fstat_by_nfs_ea (buf);
|
||||
@@ -351,22 +352,22 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf)
|
||||
|
||||
if (pc.has_buggy_basic_info ())
|
||||
{
|
||||
status = NtQueryInformationFile (get_handle (), &io, &fi, sizeof fi,
|
||||
status = NtQueryInformationFile (h, &io, &fi, sizeof fi,
|
||||
FileNetworkOpenInformation);
|
||||
/* The timestamps are in the same relative memory location, only
|
||||
the DOS attributes have to be moved. */
|
||||
fi.fbi.FileAttributes = fi.fnoi.FileAttributes;
|
||||
}
|
||||
else
|
||||
status = NtQueryInformationFile (get_handle (), &io, &fi.fbi, sizeof fi.fbi,
|
||||
FileBasicInformation);
|
||||
status = NtQueryInformationFile (h, &io, &fi.fbi,
|
||||
sizeof fi.fbi, FileBasicInformation);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
debug_printf ("%p = NtQueryInformationFile(%S, FileBasicInformation)",
|
||||
status, pc.get_nt_native_path ());
|
||||
return -1;
|
||||
}
|
||||
status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
|
||||
status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
|
||||
FileStandardInformation);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
@@ -374,13 +375,17 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf)
|
||||
status, pc.get_nt_native_path ());
|
||||
return -1;
|
||||
}
|
||||
status = NtQueryInformationFile (get_handle (), &io, &fii, sizeof fii,
|
||||
FileInternalInformation);
|
||||
if (!NT_SUCCESS (status))
|
||||
if (!ino && pc.hasgood_inode ())
|
||||
{
|
||||
debug_printf ("%p = NtQueryInformationFile(%S, FileInternalInformation)",
|
||||
status, pc.get_nt_native_path ());
|
||||
return -1;
|
||||
status = NtQueryInformationFile (h, &io, &fii, sizeof fii,
|
||||
FileInternalInformation);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
debug_printf ("%p = NtQueryInformationFile(%S, FileInternalInformation)",
|
||||
status, pc.get_nt_native_path ());
|
||||
return -1;
|
||||
}
|
||||
ino = fii.FileId.QuadPart;
|
||||
}
|
||||
/* If the change time is 0, it's a file system which doesn't
|
||||
support a change timestamp. In that case use the LastWriteTime
|
||||
@@ -397,7 +402,7 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf)
|
||||
get_dev (),
|
||||
fsi.EndOfFile.QuadPart,
|
||||
fsi.AllocationSize.QuadPart,
|
||||
fii.FileId.QuadPart,
|
||||
ino,
|
||||
fsi.NumberOfLinks,
|
||||
fi.fbi.FileAttributes);
|
||||
}
|
||||
@@ -492,7 +497,7 @@ fhandler_base::fstat_fs (struct __stat64 *buf)
|
||||
int oret;
|
||||
int open_flags = O_RDONLY | O_BINARY;
|
||||
|
||||
if (get_handle ())
|
||||
if (get_stat_handle ())
|
||||
{
|
||||
if (!nohandle () && !is_fs_special ())
|
||||
res = fstat_by_handle (buf);
|
||||
@@ -500,8 +505,16 @@ fhandler_base::fstat_fs (struct __stat64 *buf)
|
||||
res = fstat_by_name (buf);
|
||||
return res;
|
||||
}
|
||||
query_open (query_read_attributes);
|
||||
/* First try to open with generic read access. This allows to read the file
|
||||
in fstat_helper (when checking for executability) without having to
|
||||
re-open it. Opening a file can take a lot of time on network drives
|
||||
so we try to avoid that. */
|
||||
oret = open_fs (open_flags, 0);
|
||||
if (!oret)
|
||||
{
|
||||
query_open (query_read_attributes);
|
||||
oret = open_fs (open_flags, 0);
|
||||
}
|
||||
if (oret)
|
||||
{
|
||||
/* We now have a valid handle, regardless of the "nohandle" state.
|
||||
@@ -546,6 +559,7 @@ fhandler_base::fstat_helper (struct __stat64 *buf,
|
||||
{
|
||||
IO_STATUS_BLOCK st;
|
||||
FILE_COMPRESSION_INFORMATION fci;
|
||||
HANDLE h = get_stat_handle ();
|
||||
|
||||
to_timestruc_t ((PFILETIME) LastAccessTime, &buf->st_atim);
|
||||
to_timestruc_t ((PFILETIME) LastWriteTime, &buf->st_mtim);
|
||||
@@ -563,7 +577,7 @@ fhandler_base::fstat_helper (struct __stat64 *buf,
|
||||
#endif
|
||||
|
||||
/* Enforce namehash as inode number on untrusted file systems. */
|
||||
if (pc.isgood_inode (nFileIndex))
|
||||
if (nFileIndex && pc.isgood_inode (nFileIndex))
|
||||
buf->st_ino = (__ino64_t) nFileIndex;
|
||||
else
|
||||
buf->st_ino = get_ino ();
|
||||
@@ -576,9 +590,9 @@ fhandler_base::fstat_helper (struct __stat64 *buf,
|
||||
buf->st_blocks = (nAllocSize + S_BLKSIZE - 1) / S_BLKSIZE;
|
||||
else if (::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_COMPRESSED
|
||||
| FILE_ATTRIBUTE_SPARSE_FILE)
|
||||
&& get_handle () && !is_fs_special ()
|
||||
&& !NtQueryInformationFile (get_handle (), &st, (PVOID) &fci,
|
||||
sizeof fci, FileCompressionInformation))
|
||||
&& h && !is_fs_special ()
|
||||
&& !NtQueryInformationFile (h, &st, (PVOID) &fci, sizeof fci,
|
||||
FileCompressionInformation))
|
||||
/* Otherwise we request the actual amount of bytes allocated for
|
||||
compressed and sparsed files. */
|
||||
buf->st_blocks = (fci.CompressedFileSize.QuadPart + S_BLKSIZE - 1)
|
||||
@@ -597,15 +611,14 @@ fhandler_base::fstat_helper (struct __stat64 *buf,
|
||||
buf->st_size = pc.get_symlink_length ();
|
||||
/* symlinks are everything for everyone! */
|
||||
buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
|
||||
get_file_attribute (get_handle (), pc, NULL,
|
||||
get_file_attribute (h, pc, NULL,
|
||||
&buf->st_uid, &buf->st_gid);
|
||||
goto done;
|
||||
}
|
||||
else if (pc.issocket ())
|
||||
buf->st_mode = S_IFSOCK;
|
||||
|
||||
if (!get_file_attribute (is_fs_special () && !pc.issocket ()
|
||||
? NULL : get_handle (), pc,
|
||||
if (!get_file_attribute (is_fs_special () && !pc.issocket () ? NULL : h, pc,
|
||||
&buf->st_mode, &buf->st_uid, &buf->st_gid))
|
||||
{
|
||||
/* If read-only attribute is set, modify ntsec return value */
|
||||
@@ -659,50 +672,26 @@ fhandler_base::fstat_helper (struct __stat64 *buf,
|
||||
shebang scripts. */
|
||||
if (pc.exec_state () == dont_know_if_executable)
|
||||
{
|
||||
OBJECT_ATTRIBUTES attr;
|
||||
LARGE_INTEGER off = { QuadPart:0LL };
|
||||
char magic[3];
|
||||
NTSTATUS status;
|
||||
HANDLE h;
|
||||
IO_STATUS_BLOCK io;
|
||||
|
||||
/* The NWFS implementation is frighteningly incomplete. When
|
||||
re-opening a file by handle, the subsequent NtReadFile
|
||||
returns with the weird status STATUS_FILE_IS_A_DIRECTORY.
|
||||
We're still using the re-open by handle method for all
|
||||
other filesystems since it's 8-10% faster than opening
|
||||
by name. */
|
||||
if (pc.fs_is_nwfs ())
|
||||
InitializeObjectAttributes (&attr, pc.get_nt_native_path (),
|
||||
OBJ_CASE_INSENSITIVE, NULL, NULL)
|
||||
else
|
||||
InitializeObjectAttributes (&attr, &ro_u_empty, 0,
|
||||
get_handle (), NULL);
|
||||
status = NtOpenFile (&h, SYNCHRONIZE | FILE_READ_DATA,
|
||||
&attr, &io, FILE_SHARE_VALID_FLAGS,
|
||||
FILE_SYNCHRONOUS_IO_NONALERT);
|
||||
if (NT_SUCCESS (status))
|
||||
if (get_stat_access () & (GENERIC_READ | FILE_READ_DATA))
|
||||
{
|
||||
LARGE_INTEGER off = { QuadPart:0LL };
|
||||
char magic[3];
|
||||
|
||||
status = NtReadFile (h, NULL, NULL, NULL, &io, magic,
|
||||
3, &off, NULL);
|
||||
if (NT_SUCCESS (status))
|
||||
{
|
||||
if (has_exec_chars (magic, io.Information))
|
||||
{
|
||||
/* Heureka, it's an executable */
|
||||
pc.set_exec ();
|
||||
buf->st_mode |= STD_XBITS;
|
||||
}
|
||||
}
|
||||
else
|
||||
status = NtReadFile (h, NULL, NULL, NULL,
|
||||
&io, magic, 3, &off, NULL);
|
||||
status = wait_pending (status, h, io);
|
||||
if (!NT_SUCCESS (status))
|
||||
debug_printf ("%p = NtReadFile(%S)", status,
|
||||
pc.get_nt_native_path ());
|
||||
NtClose (h);
|
||||
else if (has_exec_chars (magic, io.Information))
|
||||
{
|
||||
/* Heureka, it's an executable */
|
||||
pc.set_exec ();
|
||||
buf->st_mode |= STD_XBITS;
|
||||
}
|
||||
}
|
||||
else
|
||||
debug_printf ("%p = NtOpenFile(%S)", status,
|
||||
pc.get_nt_native_path ());
|
||||
}
|
||||
}
|
||||
if (pc.exec_state () == is_executable)
|
||||
@@ -740,7 +729,7 @@ fhandler_disk_file::fstatvfs (struct statvfs *sfs)
|
||||
IO_STATUS_BLOCK io;
|
||||
FILE_FS_FULL_SIZE_INFORMATION full_fsi;
|
||||
FILE_FS_SIZE_INFORMATION fsi;
|
||||
HANDLE fh = get_handle ();
|
||||
HANDLE fh = get_stat_handle ();
|
||||
|
||||
if (!fh)
|
||||
{
|
||||
@@ -1050,7 +1039,8 @@ cant_access_acl:
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!get_handle ())
|
||||
if ((cmd == SETACL && !get_handle ())
|
||||
|| (cmd != SETACL && !get_stat_handle ()))
|
||||
{
|
||||
query_open (cmd == SETACL ? query_write_control : query_read_control);
|
||||
if (!(oret = open (O_BINARY, 0)))
|
||||
@@ -1087,10 +1077,10 @@ cant_access_acl:
|
||||
if (!aclbufp)
|
||||
set_errno(EFAULT);
|
||||
else
|
||||
res = getacl (get_handle (), pc, nentries, aclbufp);
|
||||
res = getacl (get_stat_handle (), pc, nentries, aclbufp);
|
||||
break;
|
||||
case GETACLCNT:
|
||||
res = getacl (get_handle (), pc, 0, NULL);
|
||||
res = getacl (get_stat_handle (), pc, 0, NULL);
|
||||
break;
|
||||
default:
|
||||
set_errno (EINVAL);
|
||||
@@ -1277,17 +1267,13 @@ fhandler_disk_file::link (const char *newpath)
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE fh;
|
||||
NTSTATUS status;
|
||||
OBJECT_ATTRIBUTES attr;
|
||||
IO_STATUS_BLOCK io;
|
||||
status = NtOpenFile (&fh, READ_CONTROL,
|
||||
pc.get_object_attr (attr, sec_none_nih), &io,
|
||||
FILE_SHARE_VALID_FLAGS,
|
||||
FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
|
||||
if (!NT_SUCCESS (status))
|
||||
/* We only need READ_CONTROL access so the handle returned in pc is
|
||||
sufficient. And if the file couldn't be opened with READ_CONTROL
|
||||
access in path_conv, we won't be able to do it here anyway. */
|
||||
HANDLE fh = get_stat_handle ();
|
||||
if (!fh)
|
||||
{
|
||||
__seterrno_from_nt_status (status);
|
||||
set_errno (EACCES);
|
||||
return -1;
|
||||
}
|
||||
PUNICODE_STRING tgt = newpc.get_nt_native_path ();
|
||||
@@ -1296,8 +1282,10 @@ fhandler_disk_file::link (const char *newpath)
|
||||
pfli->ReplaceIfExists = FALSE;
|
||||
pfli->RootDirectory = NULL;
|
||||
memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length);
|
||||
|
||||
NTSTATUS status;
|
||||
IO_STATUS_BLOCK io;
|
||||
status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
|
||||
NtClose (fh);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
if (status == STATUS_INVALID_DEVICE_REQUEST)
|
||||
@@ -1764,8 +1752,6 @@ readdir_get_ino (const char *path, bool dot_dot)
|
||||
char *fname;
|
||||
struct __stat64 st;
|
||||
HANDLE hdl;
|
||||
OBJECT_ATTRIBUTES attr;
|
||||
IO_STATUS_BLOCK io;
|
||||
__ino64_t ino = 0;
|
||||
|
||||
if (dot_dot)
|
||||
@@ -1777,7 +1763,7 @@ readdir_get_ino (const char *path, bool dot_dot)
|
||||
strcpy (c, "..");
|
||||
path = fname;
|
||||
}
|
||||
path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_NOWARN);
|
||||
path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_NOWARN | PC_KEEP_HANDLE);
|
||||
if (pc.isspecial ())
|
||||
{
|
||||
if (!stat_worker (pc, &st))
|
||||
@@ -1785,17 +1771,11 @@ readdir_get_ino (const char *path, bool dot_dot)
|
||||
}
|
||||
else if (!pc.hasgood_inode ())
|
||||
ino = hash_path_name (0, pc.get_nt_native_path ());
|
||||
else if (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))))
|
||||
else if ((hdl = pc.handle ()) != NULL)
|
||||
{
|
||||
ino = pc.get_ino_by_handle (hdl);
|
||||
if (!ino)
|
||||
ino = hash_path_name (0, pc.get_nt_native_path ());
|
||||
NtClose (hdl);
|
||||
}
|
||||
return ino;
|
||||
}
|
||||
|
Reference in New Issue
Block a user