* 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:
parent
51ec3f5c98
commit
5a0d1edba4
|
@ -1,3 +1,57 @@
|
|||
2010-06-15 Corinna Vinschen <corinna@vinschen.de>
|
||||
|
||||
* 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.
|
||||
|
||||
2010-06-15 Corinna Vinschen <corinna@vinschen.de>
|
||||
|
||||
* fhandler_disk_file.cc (fhandler_disk_file::fstatvfs): Fix indentation.
|
||||
|
|
|
@ -582,6 +582,7 @@ dtable::dup_worker (fhandler_base *oldfh, int flags)
|
|||
{
|
||||
*newfh = *oldfh;
|
||||
newfh->set_io_handle (NULL);
|
||||
newfh->pc.reset_conv_handle ();
|
||||
if (oldfh->dup (newfh))
|
||||
{
|
||||
delete newfh;
|
||||
|
|
|
@ -1124,7 +1124,7 @@ fhandler_base::fstatvfs (struct statvfs *sfs)
|
|||
{
|
||||
/* If we hit this base implementation, it's some device in /dev.
|
||||
Just call statvfs on /dev for simplicity. */
|
||||
path_conv pc ("/dev");
|
||||
path_conv pc ("/dev", PC_KEEP_HANDLE);
|
||||
fhandler_disk_file fh (pc);
|
||||
return fh.fstatvfs (sfs);
|
||||
}
|
||||
|
|
|
@ -181,6 +181,7 @@ class fhandler_base
|
|||
|
||||
int get_access () const { return access; }
|
||||
void set_access (int x) { access = x; }
|
||||
int get_stat_access () const { return pc.handle () ? pc.access () : access; }
|
||||
|
||||
int get_flags () { return openflags; }
|
||||
void set_flags (int x, int supplied_bin = 0);
|
||||
|
@ -355,6 +356,7 @@ class fhandler_base
|
|||
virtual HANDLE& get_handle () { return io_handle; }
|
||||
virtual HANDLE& get_io_handle () { return io_handle; }
|
||||
virtual HANDLE& get_output_handle () { return io_handle; }
|
||||
virtual HANDLE get_stat_handle () { return pc.handle () ?: io_handle; }
|
||||
virtual bool hit_eof () {return false;}
|
||||
virtual select_record *select_read (select_stuff *);
|
||||
virtual select_record *select_write (select_stuff *);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -868,6 +868,16 @@ typedef enum _EVENT_INFORMATION_CLASS
|
|||
#define NtCurrentProcess() ((HANDLE) 0xffffffff)
|
||||
#define NtCurrentThread() ((HANDLE) 0xfffffffe)
|
||||
|
||||
/* Helper macro for sync I/O with async handle. */
|
||||
inline NTSTATUS
|
||||
wait_pending (NTSTATUS status, HANDLE h, IO_STATUS_BLOCK &io)
|
||||
{
|
||||
if (status != STATUS_PENDING)
|
||||
return status;
|
||||
WaitForSingleObject (h, INFINITE);
|
||||
return io.Status;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
NTSTATUS NTAPI NtAdjustPrivilegesToken (HANDLE, BOOLEAN, PTOKEN_PRIVILEGES,
|
||||
|
|
|
@ -96,8 +96,8 @@ struct symlink_info
|
|||
_major_t major;
|
||||
_minor_t minor;
|
||||
_mode_t mode;
|
||||
int check (char *path, const suffix_info *suffixes, unsigned opt,
|
||||
fs_info &fs);
|
||||
int check (char *path, const suffix_info *suffixes, fs_info &fs,
|
||||
path_conv_handle &conv_hdl);
|
||||
int set (char *path);
|
||||
bool parse_device (const char *);
|
||||
int check_sysfile (HANDLE h);
|
||||
|
@ -639,6 +639,7 @@ path_conv::check (const char *src, unsigned opt,
|
|||
cfree (modifiable_path ());
|
||||
path = NULL;
|
||||
}
|
||||
close_conv_handle ();
|
||||
memset (&dev, 0, sizeof (dev));
|
||||
fs.clear ();
|
||||
if (normalized_path)
|
||||
|
@ -695,7 +696,9 @@ path_conv::check (const char *src, unsigned opt,
|
|||
|
||||
int symlen = 0;
|
||||
|
||||
for (unsigned pflags_or = opt & PC_NO_ACCESS_CHECK; ; pflags_or = 0)
|
||||
for (unsigned pflags_or = opt & (PC_NO_ACCESS_CHECK | PC_KEEP_HANDLE);
|
||||
;
|
||||
pflags_or = 0)
|
||||
{
|
||||
const suffix_info *suff;
|
||||
char *full_path;
|
||||
|
@ -823,7 +826,7 @@ path_conv::check (const char *src, unsigned opt,
|
|||
if (is_msdos)
|
||||
sym.pflags |= PATH_NOPOSIX | PATH_NOACL;
|
||||
|
||||
symlen = sym.check (full_path, suff, opt, fs);
|
||||
symlen = sym.check (full_path, suff, fs, conv_handle);
|
||||
|
||||
is_virtual_symlink:
|
||||
|
||||
|
@ -1124,6 +1127,7 @@ path_conv::~path_conv ()
|
|||
cfree (wide_path);
|
||||
wide_path = NULL;
|
||||
}
|
||||
close_conv_handle ();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -1683,55 +1687,50 @@ cmp_shortcut_header (win_shortcut_hdr *file_header)
|
|||
}
|
||||
|
||||
int
|
||||
symlink_info::check_shortcut (HANDLE in_h)
|
||||
symlink_info::check_shortcut (HANDLE h)
|
||||
{
|
||||
tmp_pathbuf tp;
|
||||
win_shortcut_hdr *file_header;
|
||||
char *buf, *cp;
|
||||
unsigned short len;
|
||||
int res = 0;
|
||||
OBJECT_ATTRIBUTES attr;
|
||||
NTSTATUS status;
|
||||
HANDLE h;
|
||||
IO_STATUS_BLOCK io;
|
||||
FILE_STANDARD_INFORMATION fsi;
|
||||
LARGE_INTEGER off = { QuadPart:0LL };
|
||||
|
||||
InitializeObjectAttributes (&attr, &ro_u_empty, 0, in_h, NULL);
|
||||
status = NtOpenFile (&h, FILE_READ_DATA | SYNCHRONIZE,
|
||||
&attr, &io, FILE_SHARE_VALID_FLAGS,
|
||||
FILE_OPEN_FOR_BACKUP_INTENT
|
||||
| FILE_SYNCHRONOUS_IO_NONALERT);
|
||||
if (!NT_SUCCESS (status))
|
||||
return 0;
|
||||
status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
|
||||
FileStandardInformation);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
set_error (EIO);
|
||||
goto out;
|
||||
return 0;
|
||||
}
|
||||
if (fsi.EndOfFile.QuadPart <= sizeof (win_shortcut_hdr)
|
||||
|| fsi.EndOfFile.QuadPart > 4 * 65536)
|
||||
goto out;
|
||||
return 0;
|
||||
if (fsi.EndOfFile.LowPart < NT_MAX_PATH * sizeof (WCHAR))
|
||||
buf = (char *) tp.w_get ();
|
||||
else
|
||||
buf = (char *) alloca (fsi.EndOfFile.LowPart + 1);
|
||||
if (!NT_SUCCESS (NtReadFile (h, NULL, NULL, NULL,
|
||||
&io, buf, fsi.EndOfFile.LowPart, NULL, NULL)))
|
||||
status = NtReadFile (h, NULL, NULL, NULL, &io, buf, fsi.EndOfFile.LowPart,
|
||||
&off, NULL);
|
||||
status = wait_pending (status, h, io);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
set_error (EIO);
|
||||
goto out;
|
||||
if (status != STATUS_END_OF_FILE)
|
||||
set_error (EIO);
|
||||
return 0;
|
||||
}
|
||||
file_header = (win_shortcut_hdr *) buf;
|
||||
if (io.Information != fsi.EndOfFile.LowPart
|
||||
|| !cmp_shortcut_header (file_header))
|
||||
goto out;
|
||||
return 0;
|
||||
cp = buf + sizeof (win_shortcut_hdr);
|
||||
if (file_header->flags & WSH_FLAG_IDLIST) /* Skip ITEMIDLIST */
|
||||
cp += *(unsigned short *) cp + 2;
|
||||
if (!(len = *(unsigned short *) cp))
|
||||
goto out;
|
||||
return 0;
|
||||
cp += 2;
|
||||
/* Check if this is a device file - these start with the sequence :\\ */
|
||||
if (strncmp (cp, ":\\", 2) == 0)
|
||||
|
@ -1751,11 +1750,11 @@ symlink_info::check_shortcut (HANDLE in_h)
|
|||
char *tmpbuf = tp.c_get ();
|
||||
if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) (cp + 2))
|
||||
> SYMLINK_MAX + 1)
|
||||
goto out;
|
||||
return 0;
|
||||
res = posixify (tmpbuf);
|
||||
}
|
||||
else if (len > SYMLINK_MAX)
|
||||
goto out;
|
||||
return 0;
|
||||
else
|
||||
{
|
||||
cp[len] = '\0';
|
||||
|
@ -1764,41 +1763,33 @@ symlink_info::check_shortcut (HANDLE in_h)
|
|||
}
|
||||
if (res) /* It's a symlink. */
|
||||
pflags |= PATH_SYMLINK | PATH_LNK;
|
||||
|
||||
out:
|
||||
NtClose (h);
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
symlink_info::check_sysfile (HANDLE in_h)
|
||||
symlink_info::check_sysfile (HANDLE h)
|
||||
{
|
||||
tmp_pathbuf tp;
|
||||
char cookie_buf[sizeof (SYMLINK_COOKIE) - 1];
|
||||
char *srcbuf = tp.c_get ();
|
||||
int res = 0;
|
||||
OBJECT_ATTRIBUTES attr;
|
||||
NTSTATUS status;
|
||||
HANDLE h;
|
||||
IO_STATUS_BLOCK io;
|
||||
bool interix_symlink = false;
|
||||
LARGE_INTEGER off = { QuadPart:0LL };
|
||||
|
||||
InitializeObjectAttributes (&attr, &ro_u_empty, 0, in_h, NULL);
|
||||
status = NtOpenFile (&h, FILE_READ_DATA | SYNCHRONIZE,
|
||||
&attr, &io, FILE_SHARE_VALID_FLAGS,
|
||||
FILE_OPEN_FOR_BACKUP_INTENT
|
||||
| FILE_SYNCHRONOUS_IO_NONALERT);
|
||||
status = NtReadFile (h, NULL, NULL, NULL, &io, cookie_buf,
|
||||
sizeof (cookie_buf), &off, NULL);
|
||||
status = wait_pending (status, h, io);
|
||||
if (!NT_SUCCESS (status))
|
||||
return 0;
|
||||
else if (!NT_SUCCESS (status = NtReadFile (h, NULL, NULL, NULL, &io,
|
||||
cookie_buf, sizeof (cookie_buf),
|
||||
NULL, NULL)))
|
||||
{
|
||||
debug_printf ("ReadFile1 failed %p", status);
|
||||
if (status != STATUS_END_OF_FILE)
|
||||
set_error (EIO);
|
||||
return 0;
|
||||
}
|
||||
else if (io.Information == sizeof (cookie_buf)
|
||||
off.QuadPart = io.Information;
|
||||
if (io.Information == sizeof (cookie_buf)
|
||||
&& memcmp (cookie_buf, SYMLINK_COOKIE, sizeof (cookie_buf)) == 0)
|
||||
{
|
||||
/* It's a symlink. */
|
||||
|
@ -1817,14 +1808,13 @@ symlink_info::check_sysfile (HANDLE in_h)
|
|||
/* Interix symlink cookies are shorter than Cygwin symlink cookies, so
|
||||
in case of an Interix symlink cooky we have read too far into the
|
||||
file. Set file pointer back to the position right after the cookie. */
|
||||
FILE_POSITION_INFORMATION fpi;
|
||||
fpi.CurrentByteOffset.QuadPart = sizeof (INTERIX_SYMLINK_COOKIE) - 1;
|
||||
NtSetInformationFile (h, &io, &fpi, sizeof fpi, FilePositionInformation);
|
||||
off.QuadPart = sizeof (INTERIX_SYMLINK_COOKIE) - 1;
|
||||
}
|
||||
if (pflags & PATH_SYMLINK)
|
||||
{
|
||||
status = NtReadFile (h, NULL, NULL, NULL, &io, srcbuf,
|
||||
NT_MAX_PATH, NULL, NULL);
|
||||
NT_MAX_PATH, &off, NULL);
|
||||
status = wait_pending (status, h, io);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
debug_printf ("ReadFile2 failed");
|
||||
|
@ -1852,7 +1842,6 @@ symlink_info::check_sysfile (HANDLE in_h)
|
|||
else
|
||||
res = posixify (srcbuf);
|
||||
}
|
||||
NtClose (h);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -1916,7 +1905,6 @@ symlink_info::check_nfs_symlink (HANDLE h)
|
|||
{
|
||||
tmp_pathbuf tp;
|
||||
NTSTATUS status;
|
||||
OBJECT_ATTRIBUTES attr;
|
||||
IO_STATUS_BLOCK io;
|
||||
struct {
|
||||
FILE_GET_EA_INFORMATION fgei;
|
||||
|
@ -1925,11 +1913,6 @@ symlink_info::check_nfs_symlink (HANDLE h)
|
|||
PFILE_FULL_EA_INFORMATION pffei;
|
||||
int res = 0;
|
||||
|
||||
InitializeObjectAttributes (&attr, &ro_u_empty, 0, h, NULL);
|
||||
status = NtOpenFile (&h, FILE_READ_EA, &attr, &io, FILE_SHARE_VALID_FLAGS,
|
||||
FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT);
|
||||
if (!NT_SUCCESS (status))
|
||||
return 0;
|
||||
/* To find out if the file is a symlink and to get the symlink target,
|
||||
try to fetch the NfsSymlinkTargetName EA. */
|
||||
fgei_buf.fgei.NextEntryOffset = 0;
|
||||
|
@ -1938,13 +1921,12 @@ symlink_info::check_nfs_symlink (HANDLE h)
|
|||
pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
|
||||
status = NtQueryEaFile (h, &io, pffei, NT_MAX_PATH * sizeof (WCHAR), TRUE,
|
||||
&fgei_buf.fgei, sizeof fgei_buf, NULL, TRUE);
|
||||
NtClose (h);
|
||||
if (NT_SUCCESS (status) && pffei->EaValueLength > 0)
|
||||
{
|
||||
PWCHAR spath = (PWCHAR)
|
||||
(pffei->EaName + pffei->EaNameLength + 1);
|
||||
res = sys_wcstombs (contents, SYMLINK_MAX + 1,
|
||||
spath, pffei->EaValueLength);
|
||||
spath, pffei->EaValueLength) - 1;
|
||||
pflags |= PATH_SYMLINK;
|
||||
}
|
||||
return res;
|
||||
|
@ -2197,8 +2179,8 @@ symlink_info::parse_device (const char *contents)
|
|||
stored into BUF if PATH is a symlink. */
|
||||
|
||||
int
|
||||
symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt,
|
||||
fs_info &fs)
|
||||
symlink_info::check (char *path, const suffix_info *suffixes, fs_info &fs,
|
||||
path_conv_handle &conv_hdl)
|
||||
{
|
||||
int res;
|
||||
HANDLE h;
|
||||
|
@ -2238,6 +2220,10 @@ restart:
|
|||
PVOID eabuf = &nfs_aol_ffei;
|
||||
ULONG easize = sizeof nfs_aol_ffei;
|
||||
|
||||
# define MIN_STAT_ACCESS (READ_CONTROL | FILE_READ_ATTRIBUTES)
|
||||
# define FULL_STAT_ACCESS (SYNCHRONIZE | GENERIC_READ)
|
||||
ACCESS_MASK access = 0;
|
||||
|
||||
bool had_ext = !!*ext_here;
|
||||
while (suffix.next ())
|
||||
{
|
||||
|
@ -2255,14 +2241,22 @@ restart:
|
|||
symlink (which would spoil the task of this method quite a bit).
|
||||
Fortunately it's ignored on most other file systems so we don't have
|
||||
to special case NFS too much. */
|
||||
status = NtCreateFile (&h,
|
||||
READ_CONTROL | FILE_READ_ATTRIBUTES,
|
||||
&attr, &io, NULL, 0, FILE_SHARE_VALID_FLAGS,
|
||||
FILE_OPEN,
|
||||
status = NtCreateFile (&h, access = FULL_STAT_ACCESS, &attr, &io, NULL,
|
||||
0, FILE_SHARE_VALID_FLAGS, FILE_OPEN,
|
||||
FILE_OPEN_REPARSE_POINT
|
||||
| FILE_OPEN_FOR_BACKUP_INTENT,
|
||||
eabuf, easize);
|
||||
debug_printf ("%p = NtCreateFile (%S)", status, &upath);
|
||||
if (status == STATUS_ACCESS_DENIED)
|
||||
{
|
||||
status = NtCreateFile (&h, access = MIN_STAT_ACCESS, &attr, &io,
|
||||
NULL, 0, FILE_SHARE_VALID_FLAGS, FILE_OPEN,
|
||||
FILE_OPEN_REPARSE_POINT
|
||||
| FILE_OPEN_FOR_BACKUP_INTENT,
|
||||
eabuf, easize);
|
||||
debug_printf ("%p = NtCreateFile (2:%S)", status, &upath);
|
||||
}
|
||||
else
|
||||
debug_printf ("%p = NtCreateFile (1:%S)", status, &upath);
|
||||
/* No right to access EAs or EAs not supported? */
|
||||
if (!NT_SUCCESS (status)
|
||||
&& (status == STATUS_ACCESS_DENIED
|
||||
|
@ -2282,11 +2276,20 @@ restart:
|
|||
eabuf = NULL;
|
||||
easize = 0;
|
||||
}
|
||||
status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
|
||||
&attr, &io, FILE_SHARE_VALID_FLAGS,
|
||||
status = NtOpenFile (&h, access = FULL_STAT_ACCESS, &attr, &io,
|
||||
FILE_SHARE_VALID_FLAGS,
|
||||
FILE_OPEN_REPARSE_POINT
|
||||
| FILE_OPEN_FOR_BACKUP_INTENT);
|
||||
debug_printf ("%p = NtOpenFile (no-EA, %S)", status, &upath);
|
||||
if (status == STATUS_ACCESS_DENIED)
|
||||
{
|
||||
status = NtOpenFile (&h, access = MIN_STAT_ACCESS, &attr, &io,
|
||||
FILE_SHARE_VALID_FLAGS,
|
||||
FILE_OPEN_REPARSE_POINT
|
||||
| FILE_OPEN_FOR_BACKUP_INTENT);
|
||||
debug_printf ("%p = NtOpenFile (no-EAs 2:%S)", status, &upath);
|
||||
}
|
||||
else
|
||||
debug_printf ("%p = NtOpenFile (no-EA 1:%S)", status, &upath);
|
||||
}
|
||||
if (status == STATUS_OBJECT_NAME_NOT_FOUND)
|
||||
{
|
||||
|
@ -2478,7 +2481,10 @@ restart:
|
|||
if ((fileattr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY))
|
||||
== FILE_ATTRIBUTE_READONLY && suffix.lnk_match ())
|
||||
{
|
||||
res = check_shortcut (h);
|
||||
if (!(access & GENERIC_READ))
|
||||
res = 0;
|
||||
else
|
||||
res = check_shortcut (h);
|
||||
if (!res)
|
||||
{
|
||||
/* If searching for `foo' and then finding a `foo.lnk' which is
|
||||
|
@ -2538,17 +2544,23 @@ restart:
|
|||
else if ((fileattr & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY))
|
||||
== FILE_ATTRIBUTE_SYSTEM)
|
||||
{
|
||||
res = check_sysfile (h);
|
||||
if (!(access & GENERIC_READ))
|
||||
res = 0;
|
||||
else
|
||||
res = check_sysfile (h);
|
||||
if (res)
|
||||
break;
|
||||
}
|
||||
|
||||
/* If the file could be opened with FILE_READ_EA, and if it's on a
|
||||
NFS share, check if it's a symlink. Only files can be symlinks
|
||||
/* If the file is on an NFS share and could be opened with extended
|
||||
attributes, check if it's a symlink. Only files can be symlinks
|
||||
(which can be symlinks to directories). */
|
||||
else if (fs.is_nfs () && !no_ea && !(fileattr & FILE_ATTRIBUTE_DIRECTORY))
|
||||
{
|
||||
res = check_nfs_symlink (h);
|
||||
if (!(access & GENERIC_READ))
|
||||
res = 0;
|
||||
else
|
||||
res = check_nfs_symlink (h);
|
||||
if (res)
|
||||
break;
|
||||
}
|
||||
|
@ -2562,7 +2574,12 @@ restart:
|
|||
}
|
||||
|
||||
if (h)
|
||||
NtClose (h);
|
||||
{
|
||||
if (pflags & PC_KEEP_HANDLE)
|
||||
conv_hdl.set (h, access);
|
||||
else
|
||||
NtClose (h);
|
||||
}
|
||||
|
||||
syscall_printf ("%d = symlink.check (%s, %p) (%p)",
|
||||
res, suffix.path, contents, pflags);
|
||||
|
|
|
@ -57,6 +57,7 @@ enum pathconv_arg
|
|||
PC_CHECK_EA = 0x0040,
|
||||
PC_POSIX = 0x0080,
|
||||
PC_NOWARN = 0x0100,
|
||||
PC_KEEP_HANDLE = 0x00400000,
|
||||
PC_NO_ACCESS_CHECK = 0x00800000
|
||||
};
|
||||
|
||||
|
@ -86,6 +87,33 @@ enum path_types
|
|||
PATH_SOCKET = 0x40000000
|
||||
};
|
||||
|
||||
class path_conv_handle
|
||||
{
|
||||
HANDLE hdl;
|
||||
ACCESS_MASK acc;
|
||||
public:
|
||||
path_conv_handle () : hdl (NULL), acc (0) {}
|
||||
inline void set (HANDLE h, ACCESS_MASK a) { hdl = h; acc = a; }
|
||||
inline void close ()
|
||||
{
|
||||
if (hdl)
|
||||
CloseHandle (hdl);
|
||||
set (NULL, 0);
|
||||
}
|
||||
inline void dup (path_conv_handle &pch)
|
||||
{
|
||||
if (!DuplicateHandle (GetCurrentProcess (), pch.handle (),
|
||||
GetCurrentProcess (), &hdl,
|
||||
0, TRUE, DUPLICATE_SAME_ACCESS))
|
||||
{
|
||||
hdl = NULL;
|
||||
acc = 0;
|
||||
}
|
||||
}
|
||||
inline HANDLE handle () const { return hdl; }
|
||||
inline ACCESS_MASK access () const { return acc; }
|
||||
};
|
||||
|
||||
class symlink_info;
|
||||
|
||||
class path_conv
|
||||
|
@ -98,6 +126,7 @@ class path_conv
|
|||
void add_ext_from_sym (symlink_info&);
|
||||
DWORD symlink_length;
|
||||
const char *path;
|
||||
path_conv_handle conv_handle;
|
||||
public:
|
||||
unsigned path_flags;
|
||||
const char *known_suffix;
|
||||
|
@ -220,6 +249,7 @@ class path_conv
|
|||
{
|
||||
memcpy (this, &pc, sizeof pc);
|
||||
path = cstrdup (pc.path);
|
||||
conv_handle.dup (pc.conv_handle);
|
||||
normalized_path = cstrdup(pc.normalized_path);
|
||||
wide_path = NULL;
|
||||
return *this;
|
||||
|
@ -250,6 +280,11 @@ class path_conv
|
|||
}
|
||||
bool is_binary ();
|
||||
|
||||
HANDLE handle () const { return conv_handle.handle (); }
|
||||
ACCESS_MASK access () const { return conv_handle.access (); }
|
||||
void reset_conv_handle () { conv_handle.set (NULL, 0); }
|
||||
void close_conv_handle () { conv_handle.close (); }
|
||||
|
||||
__ino64_t get_ino_by_handle (HANDLE h);
|
||||
#if 0 /* obsolete, method still exists in fhandler_disk_file.cc */
|
||||
unsigned __stdcall ndisk_links (DWORD);
|
||||
|
|
|
@ -419,7 +419,8 @@ acl_worker (const char *path, int cmd, int nentries, __aclent32_t *aclbufp,
|
|||
unsigned fmode)
|
||||
{
|
||||
int res = -1;
|
||||
fhandler_base *fh = build_fh_name (path, fmode, stat_suffixes);
|
||||
fhandler_base *fh = build_fh_name (path, fmode | PC_KEEP_HANDLE,
|
||||
stat_suffixes);
|
||||
if (fh->error ())
|
||||
{
|
||||
debug_printf ("got %d error from build_fh_name", fh->error ());
|
||||
|
|
|
@ -1169,7 +1169,8 @@ link (const char *oldpath, const char *newpath)
|
|||
int res = -1;
|
||||
fhandler_base *fh;
|
||||
|
||||
if (!(fh = build_fh_name (oldpath, PC_SYM_NOFOLLOW, stat_suffixes)))
|
||||
if (!(fh = build_fh_name (oldpath, PC_SYM_NOFOLLOW | PC_KEEP_HANDLE,
|
||||
stat_suffixes)))
|
||||
goto error;
|
||||
|
||||
if (fh->error ())
|
||||
|
@ -1542,7 +1543,7 @@ extern "C" int
|
|||
stat64 (const char *name, struct __stat64 *buf)
|
||||
{
|
||||
syscall_printf ("entering");
|
||||
path_conv pc (name, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes);
|
||||
path_conv pc (name, PC_SYM_FOLLOW | PC_POSIX | PC_KEEP_HANDLE, stat_suffixes);
|
||||
return stat_worker (pc, buf);
|
||||
}
|
||||
|
||||
|
@ -1581,7 +1582,8 @@ extern "C" int
|
|||
lstat64 (const char *name, struct __stat64 *buf)
|
||||
{
|
||||
syscall_printf ("entering");
|
||||
path_conv pc (name, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
|
||||
path_conv pc (name, PC_SYM_NOFOLLOW | PC_POSIX | PC_KEEP_HANDLE,
|
||||
stat_suffixes);
|
||||
return stat_worker (pc, buf);
|
||||
}
|
||||
|
||||
|
@ -2561,7 +2563,8 @@ statvfs (const char *name, struct statvfs *sfs)
|
|||
if (efault.faulted (EFAULT))
|
||||
goto error;
|
||||
|
||||
if (!(fh = build_fh_name (name, PC_SYM_FOLLOW, stat_suffixes)))
|
||||
if (!(fh = build_fh_name (name, PC_SYM_FOLLOW | PC_KEEP_HANDLE,
|
||||
stat_suffixes)))
|
||||
goto error;
|
||||
|
||||
if (fh->error ())
|
||||
|
|
Loading…
Reference in New Issue