* 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:
Corinna Vinschen 2010-06-15 12:05:15 +00:00
parent 51ec3f5c98
commit 5a0d1edba4
10 changed files with 260 additions and 157 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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