* Makefile.in (DLL_OFILES): Add nfs.o.

* fhandler.cc (fhandler_base::open): Open files on NFS shares with
	correct access flags and EAs.
	* fhandler.h (fhandler_base::fstat_by_nfs_ea): Declare.
	* fhandler_disk_file.cc (fhandler_base::fstat_by_nfs_ea): New method.
	(fhandler_base::fstat_by_handle): Call fstat_by_nfs_ea for files on
	NFS shares.
	(fhandler_disk_file::fchmod): Use NFS specific method to set mode for
	files on NFS shares.  Don't overrule errno from call to
	set_file_attribute with errno from NtSetInformationFile call.
	(fhandler_disk_file::fchown): Add comment.
	* mount.cc (fillout_mntent): Accommodate change in second parameter
	to fs_info::update.
	* nfs.cc: New file.
	* nfs.h: New file.
	* path.cc (fs_info::update): Take handle instead of bool as second
	parameter.  Use that handle if it's not NULL.  Only close handle if
	it has been opened here.  Use static defined buffers instead of
	alloca'd buffers.
	(path_conv::check): Call symlink_info::check with reference to fs.
	Don't call fs.update here if file exists.
	(conv_path_list): Prefer tmp_pathbuf buffer over using alloca.
	(symlink_worker): Use NFS specific method to create symlinks on NFS
	shares.  Prefer tmp_pathbuf buffer over using alloca.
	(symlink_info::check_shortcut): Reopen file from incoming handle
	with necessary FILE_GENERIC_READ flag.  Prefer tmp_pathbuf buffer over
	using alloca.
	(symlink_info::check_sysfile): Ditto.
	(symlink_info::check_reparse_point): Use tmp_pathbuf buffer to
	allocate REPARSE_DATA_BUFFER.
	(symlink_info::check_nfs_symlink): New method.
	(enum symlink_t): Remove.
	(symlink_info::check): Don't use NtQueryAttributesFile.  Rather, open
	file with necessary access flags and call NtQueryInformationFile.  Fix
	error handling in case file can't be opened.  For existing files, call
	fs_info::update here.  Restructure symlink checking to accommodate the
	fact that the file is already open.  Add case for NFS symlinks.
	* path.h (fs_info::update): Take handle instead of bool as second
	parameter.
This commit is contained in:
Corinna Vinschen 2008-05-20 15:11:23 +00:00
parent 2f33b79950
commit fe6934da14
10 changed files with 531 additions and 161 deletions

View File

@ -1,3 +1,45 @@
2008-05-20 Corinna Vinschen <corinna@vinschen.de>
* Makefile.in (DLL_OFILES): Add nfs.o.
* fhandler.cc (fhandler_base::open): Open files on NFS shares with
correct access flags and EAs.
* fhandler.h (fhandler_base::fstat_by_nfs_ea): Declare.
* fhandler_disk_file.cc (fhandler_base::fstat_by_nfs_ea): New method.
(fhandler_base::fstat_by_handle): Call fstat_by_nfs_ea for files on
NFS shares.
(fhandler_disk_file::fchmod): Use NFS specific method to set mode for
files on NFS shares. Don't overrule errno from call to
set_file_attribute with errno from NtSetInformationFile call.
(fhandler_disk_file::fchown): Add comment.
* mount.cc (fillout_mntent): Accommodate change in second parameter
to fs_info::update.
* nfs.cc: New file.
* nfs.h: New file.
* path.cc (fs_info::update): Take handle instead of bool as second
parameter. Use that handle if it's not NULL. Only close handle if
it has been opened here. Use static defined buffers instead of
alloca'd buffers.
(path_conv::check): Call symlink_info::check with reference to fs.
Don't call fs.update here if file exists.
(conv_path_list): Prefer tmp_pathbuf buffer over using alloca.
(symlink_worker): Use NFS specific method to create symlinks on NFS
shares. Prefer tmp_pathbuf buffer over using alloca.
(symlink_info::check_shortcut): Reopen file from incoming handle
with necessary FILE_GENERIC_READ flag. Prefer tmp_pathbuf buffer over
using alloca.
(symlink_info::check_sysfile): Ditto.
(symlink_info::check_reparse_point): Use tmp_pathbuf buffer to
allocate REPARSE_DATA_BUFFER.
(symlink_info::check_nfs_symlink): New method.
(enum symlink_t): Remove.
(symlink_info::check): Don't use NtQueryAttributesFile. Rather, open
file with necessary access flags and call NtQueryInformationFile. Fix
error handling in case file can't be opened. For existing files, call
fs_info::update here. Restructure symlink checking to accommodate the
fact that the file is already open. Add case for NFS symlinks.
* path.h (fs_info::update): Take handle instead of bool as second
parameter.
2008-05-20 Corinna Vinschen <corinna@vinschen.de>
* syscalls.cc (rename): Fix condition when to start a transaction.

View File

@ -137,7 +137,7 @@ DLL_OFILES:=assert.o autoload.o bsdlib.o ctype.o cxx.o cygheap.o cygthread.o \
fhandler_zero.o flock.o fnmatch.o fork.o fts.o ftw.o getopt.o glob.o \
grp.o heap.o hookapi.o inet_addr.o inet_network.o init.o ioctl.o ipc.o \
kernel32.o localtime.o lsearch.o malloc_wrapper.o minires-os-if.o \
minires.o miscfuncs.o mktemp.o mmap.o msg.o mount.o net.o netdb.o \
minires.o miscfuncs.o mktemp.o mmap.o msg.o mount.o net.o netdb.o nfs.o \
nftw.o ntea.o passwd.o path.o pinfo.o pipe.o poll.o posix_ipc.o \
pthread.o random.o regcomp.o regerror.o regexec.o regfree.o registry.o \
resource.o rexec.o rcmd.o scandir.o sched.o sec_acl.o sec_auth.o \

View File

@ -28,6 +28,7 @@ details. */
#include "ntdll.h"
#include "cygtls.h"
#include "sigproc.h"
#include "nfs.h"
static NO_COPY const int CHUNK_SIZE = 1024; /* Used for crlf conversions */
@ -469,6 +470,8 @@ fhandler_base::open (int flags, mode_t mode)
OBJECT_ATTRIBUTES attr;
IO_STATUS_BLOCK io;
NTSTATUS status;
PFILE_FULL_EA_INFORMATION p = NULL;
ULONG plen = 0;
syscall_printf ("(%S, %p)", pc.get_nt_native_path (), flags);
@ -522,6 +525,18 @@ fhandler_base::open (int flags, mode_t mode)
break;
}
if (query_open () && pc.fs_is_nfs ())
{
/* Make sure we can read EAs of files on an NFS share. Also make
sure that we're going to act on the file itself, even if it'a
a symlink. */
access |= FILE_READ_EA;
if (query_open () >= query_write_control)
access |= FILE_WRITE_EA;
plen = sizeof nfs_aol_ffei;
p = (PFILE_FULL_EA_INFORMATION) &nfs_aol_ffei;
}
if ((flags & O_TRUNC) && ((flags & O_ACCMODE) != O_RDONLY))
{
if (flags & O_CREAT)
@ -578,13 +593,33 @@ fhandler_base::open (int flags, mode_t mode)
set_security_attribute (mode, &sa, sd);
attr.SecurityDescriptor = sa.lpSecurityDescriptor;
}
else if (pc.fs_is_nfs ())
{
/* When creating a file on an NFS share, we have to set the
file mode by writing a NFS fattr3 structure with the
correct mode bits set. */
access |= FILE_WRITE_EA;
plen = sizeof (FILE_FULL_EA_INFORMATION) + sizeof (NFS_V3_ATTR)
+ sizeof (fattr3);
p = (PFILE_FULL_EA_INFORMATION) alloca (plen);
p->NextEntryOffset = 0;
p->Flags = 0;
p->EaNameLength = sizeof (NFS_V3_ATTR) - 1;
p->EaValueLength = sizeof (fattr3);
strcpy (p->EaName, NFS_V3_ATTR);
fattr3 *nfs_attr = (fattr3 *) (p->EaName
+ p->EaNameLength + 1);
memset (nfs_attr, 0, sizeof (fattr3));
nfs_attr->type = NF3REG;
nfs_attr->mode = mode;
}
/* The file attributes are needed for later use in, e.g. fchmod. */
pc.file_attributes (file_attributes);
}
}
status = NtCreateFile (&x, access, &attr, &io, NULL, file_attributes, shared,
create_disposition, create_options, NULL, 0);
create_disposition, create_options, p, plen);
if (!NT_SUCCESS (status))
{
/* Trying to create a directory should return EISDIR, not ENOENT. */

View File

@ -281,6 +281,7 @@ class fhandler_base
DWORD nNumberOfLinks,
DWORD dwFileAttributes)
__attribute__ ((regparm (3)));
int __stdcall fstat_by_nfs_ea (struct __stat64 *buf) __attribute__ ((regparm (2)));
int __stdcall fstat_by_handle (struct __stat64 *buf) __attribute__ ((regparm (2)));
int __stdcall fstat_by_name (struct __stat64 *buf) __attribute__ ((regparm (2)));
virtual int __stdcall fstatvfs (struct statvfs *buf) __attribute__ ((regparm (2)));

View File

@ -23,6 +23,7 @@ details. */
#include "pinfo.h"
#include "ntdll.h"
#include "tls_pbuf.h"
#include "nfs.h"
#include <winioctl.h>
#define _COMPILING_NEWLIB
@ -264,11 +265,70 @@ path_conv::isgood_inode (__ino64_t ino) const
return hasgood_inode () && (ino > UINT32_MAX || !isremote () || fs_is_nfs ());
}
/* For files on NFS shares, we request an EA of type NfsV3Attributes.
This returns the content of a struct fattr3 as defined in RFC 1813.
The content is the NFS equivalent of struct stat. so there's not much
to do here except for copying. */
int __stdcall
fhandler_base::fstat_by_nfs_ea (struct __stat64 *buf)
{
NTSTATUS status;
IO_STATUS_BLOCK io;
struct {
FILE_FULL_EA_INFORMATION ffei;
char buf[sizeof (NFS_V3_ATTR) + sizeof (fattr3)];
} ffei_buf;
struct {
FILE_GET_EA_INFORMATION fgei;
char buf[sizeof (NFS_V3_ATTR)];
} fgei_buf;
fgei_buf.fgei.NextEntryOffset = 0;
fgei_buf.fgei.EaNameLength = sizeof (NFS_V3_ATTR) - 1;
stpcpy (fgei_buf.fgei.EaName, NFS_V3_ATTR);
status = NtQueryEaFile (get_handle (), &io,
&ffei_buf.ffei, sizeof ffei_buf, TRUE,
&fgei_buf.fgei, sizeof fgei_buf, NULL, TRUE);
if (NT_SUCCESS (status))
{
fattr3 *nfs_attr = (fattr3 *) (ffei_buf.ffei.EaName
+ ffei_buf.ffei.EaNameLength + 1);
buf->st_dev = nfs_attr->fsid;
buf->st_ino = nfs_attr->fileid;
buf->st_mode = (nfs_attr->mode & 0xfff)
| nfs_type_mapping[nfs_attr->type & 7];
buf->st_nlink = nfs_attr->nlink;
/* FIXME: How to convert UNIX uid/gid to Windows SIDs? */
#if 0
buf->st_uid = nfs_attr->uid;
buf->st_gid = nfs_attr->gid;
#else
buf->st_uid = myself->uid;
buf->st_gid = myself->gid;
#endif
buf->st_rdev = makedev (nfs_attr->rdev.specdata1,
nfs_attr->rdev.specdata2);
buf->st_size = nfs_attr->size;
buf->st_blksize = PREFERRED_IO_BLKSIZE;
buf->st_blocks = nfs_attr->used / 512;
buf->st_atim = nfs_attr->atime;
buf->st_mtim = nfs_attr->mtime;
buf->st_ctim = nfs_attr->ctime;
return 0;
}
debug_printf ("%p = NtQueryEaFile(%S)", status, pc.get_nt_native_path ());
return -1;
}
int __stdcall
fhandler_base::fstat_by_handle (struct __stat64 *buf)
{
NTSTATUS status;
IO_STATUS_BLOCK io;
if (pc.fs_is_nfs ())
return fstat_by_nfs_ea (buf);
/* The entries potentially contain a name of MAX_PATH wide characters. */
const DWORD fai_size = (NAME_MAX + 1) * sizeof (WCHAR)
+ sizeof (FILE_ALL_INFORMATION);
@ -730,6 +790,8 @@ fhandler_disk_file::fchmod (mode_t mode)
extern int chmod_device (path_conv& pc, mode_t mode);
int res = -1;
int oret = 0;
NTSTATUS status;
IO_STATUS_BLOCK io;
if (pc.is_fs_special ())
return chmod_device (pc, mode);
@ -749,6 +811,34 @@ fhandler_disk_file::fchmod (mode_t mode)
}
}
if (pc.fs_is_nfs ())
{
/* chmod on NFS shares works by writing an EA of type NfsV3Attributes.
Only type and mode have to be set. Apparently type isn't checked
for consistency, so it's sufficent to set it to NF3REG all the time. */
struct {
FILE_FULL_EA_INFORMATION ffei;
char buf[sizeof (NFS_V3_ATTR) + sizeof (fattr3)];
} ffei_buf;
ffei_buf.ffei.NextEntryOffset = 0;
ffei_buf.ffei.Flags = 0;
ffei_buf.ffei.EaNameLength = sizeof (NFS_V3_ATTR) - 1;
ffei_buf.ffei.EaValueLength = sizeof (fattr3);
strcpy (ffei_buf.ffei.EaName, NFS_V3_ATTR);
fattr3 *nfs_attr = (fattr3 *) (ffei_buf.ffei.EaName
+ ffei_buf.ffei.EaNameLength + 1);
memset (nfs_attr, 0, sizeof (fattr3));
nfs_attr->type = NF3REG;
nfs_attr->mode = mode;
status = NtSetEaFile (get_handle (), &io,
&ffei_buf.ffei, sizeof ffei_buf);
if (!NT_SUCCESS (status))
__seterrno_from_nt_status (status);
else
res = 0;
goto out;
}
if (allow_ntsec && pc.has_acls ())
{
if (pc.isdir ())
@ -767,19 +857,22 @@ fhandler_disk_file::fchmod (mode_t mode)
if (mode & S_IFSOCK)
pc |= (DWORD) FILE_ATTRIBUTE_SYSTEM;
IO_STATUS_BLOCK io;
FILE_BASIC_INFORMATION fbi;
fbi.CreationTime.QuadPart = fbi.LastAccessTime.QuadPart
= fbi.LastWriteTime.QuadPart = fbi.ChangeTime.QuadPart = 0LL;
fbi.FileAttributes = pc.file_attributes () ?: FILE_ATTRIBUTE_NORMAL;
NTSTATUS status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
FileBasicInformation);
if (!NT_SUCCESS (status))
__seterrno_from_nt_status (status);
else if (!allow_ntsec || !pc.has_acls ())
/* Correct NTFS security attributes have higher priority */
res = 0;
status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
FileBasicInformation);
/* Correct NTFS security attributes have higher priority */
if (!allow_ntsec || !pc.has_acls ())
{
if (!NT_SUCCESS (status))
__seterrno_from_nt_status (status);
else
res = 0;
}
out:
if (oret)
close_fs ();
@ -795,6 +888,7 @@ fhandler_disk_file::fchown (__uid32_t uid, __gid32_t gid)
{
/* fake - if not supported, pretend we're like win95
where it just works */
/* FIXME: Could be supported on NFS when user->uid mapping is in place. */
return 0;
}

View File

@ -1273,7 +1273,7 @@ fillout_mntent (const char *native_path, const char *posix_path, unsigned flags)
get_nt_native_path (native_path, unat, flags & MOUNT_ENC);
if (append_bs)
RtlAppendUnicodeToString (&unat, L"\\");
mntinfo.update (&unat, true); /* this pulls from a cache, usually. */
mntinfo.update (&unat, NULL);
if (mntinfo.is_samba())
strcpy (_my_tls.locals.mnt_type, (char *) "smbfs");

18
winsup/cygwin/nfs.cc Normal file
View File

@ -0,0 +1,18 @@
/* nfs.cc
Copyright 2008 Red Hat, Inc.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include "winsup.h"
#include "sys/fcntl.h"
#include "nfs.h"
struct nfs_aol_ffei_t nfs_aol_ffei = { 0, 0, sizeof (NFS_ACT_ON_LINK) - 1, 0,
NFS_ACT_ON_LINK };
uint32_t nfs_type_mapping[] = { 0, S_IFREG, S_IFDIR, S_IFBLK,
S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO };

55
winsup/cygwin/nfs.h Normal file
View File

@ -0,0 +1,55 @@
/* nfs.h
Copyright 2008 Red Hat, Inc.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#define NFS_ACT_ON_LINK "NfsActOnLink"
#define NFS_SYML_TARGET "NfsSymlinkTargetName"
#define NFS_V3_ATTR "NfsV3Attributes"
/* NFS datastructures per RFC 1813, as returned by SFU NFS. */
enum ftype3 {
NF3REG = 1,
NF3DIR = 2,
NF3BLK = 3,
NF3CHR = 4,
NF3LNK = 5,
NF3SOCK = 6,
NF3FIFO = 7
};
struct fattr3 {
uint32_t type;
uint32_t mode;
uint32_t nlink;
uint32_t uid;
uint32_t gid;
uint32_t filler1;
uint64_t size;
uint64_t used;
struct
{
uint32_t specdata1;
uint32_t specdata2;
} rdev;
uint64_t fsid;
uint64_t fileid;
timestruc_t atime;
timestruc_t mtime;
timestruc_t ctime;
};
struct nfs_aol_ffei_t {
ULONG NextEntryOffset;
UCHAR Flags;
UCHAR EaNameLength;
USHORT EaValueLength;
CHAR EaName[sizeof (NFS_ACT_ON_LINK)];
};
extern struct nfs_aol_ffei_t nfs_aol_ffei;
extern uint32_t nfs_type_mapping[];

View File

@ -67,6 +67,7 @@ details. */
#include "cygtls.h"
#include "tls_pbuf.h"
#include "environ.h"
#include "nfs.h"
#include <assert.h>
#include <ntdll.h>
#include <wchar.h>
@ -88,13 +89,15 @@ struct symlink_info
_major_t major;
_minor_t minor;
_mode_t mode;
int check (char *path, const suffix_info *suffixes, unsigned opt);
int check (char *path, const suffix_info *suffixes, unsigned opt,
fs_info &fs);
int set (char *path);
bool parse_device (const char *);
bool case_check (char *path);
int check_sysfile (HANDLE h);
int check_shortcut (HANDLE h);
int check_reparse_point (HANDLE h);
int check_nfs_symlink (HANDLE h);
int posixify (char *srcbuf);
bool set_error (int);
};
@ -368,7 +371,7 @@ struct smb_extended_info {
#pragma pack(pop)
bool
fs_info::update (PUNICODE_STRING upath, bool exists)
fs_info::update (PUNICODE_STRING upath, HANDLE in_vol)
{
NTSTATUS status = STATUS_OBJECT_NAME_NOT_FOUND;
HANDLE vol;
@ -377,45 +380,54 @@ fs_info::update (PUNICODE_STRING upath, bool exists)
bool no_media = false;
FILE_FS_DEVICE_INFORMATION ffdi;
FILE_FS_OBJECTID_INFORMATION ffoi;
PFILE_FS_ATTRIBUTE_INFORMATION pffai;
const DWORD fvi_size = (NAME_MAX + 1) * sizeof (WCHAR)
+ sizeof (FILE_FS_VOLUME_INFORMATION);
PFILE_FS_VOLUME_INFORMATION pfvi = (PFILE_FS_VOLUME_INFORMATION)
alloca (fvi_size);
struct {
FILE_FS_ATTRIBUTE_INFORMATION ffai;
WCHAR buf[NAME_MAX + 1];
} ffai_buf;
struct {
FILE_FS_VOLUME_INFORMATION ffvi;
WCHAR buf[NAME_MAX + 1];
} ffvi_buf;
UNICODE_STRING fsname, testname;
InitializeObjectAttributes (&attr, upath, OBJ_CASE_INSENSITIVE, NULL, NULL);
if (exists)
status = NtOpenFile (&vol, READ_CONTROL, &attr, &io, FILE_SHARE_VALID_FLAGS,
FILE_OPEN_FOR_BACKUP_INTENT);
while (!NT_SUCCESS (status)
&& (attr.ObjectName->Length > 7 * sizeof (WCHAR)
|| status == STATUS_NO_MEDIA_IN_DEVICE))
if (in_vol)
vol = in_vol;
else
{
UNICODE_STRING dir;
RtlSplitUnicodePath (attr.ObjectName, &dir, NULL);
attr.ObjectName = &dir;
if (status == STATUS_NO_MEDIA_IN_DEVICE)
InitializeObjectAttributes (&attr, upath, OBJ_CASE_INSENSITIVE, NULL,
NULL);
while (!NT_SUCCESS (status)
&& (attr.ObjectName->Length > 7 * sizeof (WCHAR)
|| status == STATUS_NO_MEDIA_IN_DEVICE))
{
no_media = true;
dir.Length = 6 * sizeof (WCHAR);
UNICODE_STRING dir;
RtlSplitUnicodePath (attr.ObjectName, &dir, NULL);
attr.ObjectName = &dir;
if (status == STATUS_NO_MEDIA_IN_DEVICE)
{
no_media = true;
dir.Length = 6 * sizeof (WCHAR);
}
else if (dir.Length > 7 * sizeof (WCHAR))
dir.Length -= sizeof (WCHAR);
status = NtOpenFile (&vol, READ_CONTROL, &attr, &io,
FILE_SHARE_VALID_FLAGS,
FILE_OPEN_FOR_BACKUP_INTENT);
}
if (!NT_SUCCESS (status))
{
debug_printf ("Cannot access path %S, status %08lx",
attr.ObjectName, status);
clear ();
NtClose (vol);
return false;
}
else if (dir.Length > 7 * sizeof (WCHAR))
dir.Length -= sizeof (WCHAR);
status = NtOpenFile (&vol, READ_CONTROL, &attr, &io,
FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT);
}
if (!NT_SUCCESS (status))
{
debug_printf ("Cannot access path %S, status %08lx", attr.ObjectName,
status);
clear ();
NtClose (vol);
return false;
}
status = NtQueryVolumeInformationFile (vol, &io, pfvi, fvi_size,
status = NtQueryVolumeInformationFile (vol, &io, &ffvi_buf.ffvi,
sizeof ffvi_buf,
FileFsVolumeInformation);
sernum = NT_SUCCESS (status) ? pfvi->VolumeSerialNumber : 0;
sernum = NT_SUCCESS (status) ? ffvi_buf.ffvi.VolumeSerialNumber : 0;
status = NtQueryVolumeInformationFile (vol, &io, &ffdi, sizeof ffdi,
FileFsDeviceInformation);
if (!NT_SUCCESS (status))
@ -429,23 +441,20 @@ fs_info::update (PUNICODE_STRING upath, bool exists)
is_remote_drive (false);
if (!no_media)
{
const ULONG size = sizeof (FILE_FS_ATTRIBUTE_INFORMATION)
+ NAME_MAX * sizeof (WCHAR);
pffai = (PFILE_FS_ATTRIBUTE_INFORMATION) alloca (size);
status = NtQueryVolumeInformationFile (vol, &io, pffai, size,
FileFsAttributeInformation);
}
status = NtQueryVolumeInformationFile (vol, &io, &ffai_buf.ffai,
sizeof ffai_buf,
FileFsAttributeInformation);
if (no_media || !NT_SUCCESS (status))
{
debug_printf ("Cannot get volume attributes (%S), %08lx",
attr.ObjectName, status);
has_buggy_open (false);
flags (0);
NtClose (vol);
if (!in_vol)
NtClose (vol);
return false;
}
flags (pffai->FileSystemAttributes);
flags (ffai_buf.ffai.FileSystemAttributes);
/* Should be reevaluated for each new OS. Right now this mask is valid up
to Vista. The important point here is to test only flags indicating
capabilities and to ignore flags indicating a specific state of this
@ -469,8 +478,8 @@ fs_info::update (PUNICODE_STRING upath, bool exists)
| FILE_UNICODE_ON_DISK \
| FILE_PERSISTENT_ACLS \
| FILE_NAMED_STREAMS)
RtlInitCountedUnicodeString (&fsname, pffai->FileSystemName,
pffai->FileSystemNameLength);
RtlInitCountedUnicodeString (&fsname, ffai_buf.ffai.FileSystemName,
ffai_buf.ffai.FileSystemNameLength);
is_fat (RtlEqualUnicodePathPrefix (&fsname, L"FAT", TRUE));
RtlInitUnicodeString (&testname, L"NTFS");
if (is_remote_drive ())
@ -513,7 +522,8 @@ fs_info::update (PUNICODE_STRING upath, bool exists)
RtlInitUnicodeString (&testname, L"SUNWNFS");
has_buggy_open (RtlEqualUnicodeString (&fsname, &testname, FALSE));
NtClose (vol);
if (!in_vol)
NtClose (vol);
return true;
}
@ -992,7 +1002,7 @@ path_conv::check (const char *src, unsigned opt,
full_path[3] = '\0';
}
symlen = sym.check (full_path, suff, opt);
symlen = sym.check (full_path, suff, opt, fs);
is_virtual_symlink:
@ -1213,7 +1223,8 @@ out:
}
}
if (fs.update (get_nt_native_path (), exists ()))
/* FS has been checked already for existing files. */
if (exists () || fs.update (get_nt_native_path (), NULL))
{
debug_printf ("this->path(%s), has_acls(%d)", path, fs.has_acls ());
if (fs.has_acls () && allow_ntsec)
@ -1434,8 +1445,10 @@ nofinalslash (const char *src, char *dst)
static int
conv_path_list (const char *src, char *dst, size_t size, int to_posix)
{
tmp_pathbuf tp;
char src_delim, dst_delim;
cygwin_conv_path_t conv_fn;
size_t len;
if (to_posix)
{
@ -1450,7 +1463,12 @@ conv_path_list (const char *src, char *dst, size_t size, int to_posix)
conv_fn = CCP_POSIX_TO_WIN_A | CCP_RELATIVE;
}
char *srcbuf = (char *) alloca (strlen (src) + 1);
char *srcbuf;
len = strlen (src) + 1;
if (len <= NT_MAX_PATH * sizeof (WCHAR))
srcbuf = (char *) tp.w_get ();
else
srcbuf = (char *) alloca (len);
int err = 0;
char *d = dst - 1;
@ -1554,7 +1572,8 @@ symlink_worker (const char *oldpath, const char *newpath, bool use_winsym,
}
/* We need the normalized full path below. */
win32_newpath.check (newpath, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
if (use_winsym && !win32_newpath.exists ())
if (use_winsym && !win32_newpath.exists ()
&& (isdevice || !win32_newpath.fs_is_nfs ()))
{
char *newplnk = tp.c_get ();
stpcpy (stpcpy (newplnk, newpath), ".lnk");
@ -1577,6 +1596,34 @@ symlink_worker (const char *oldpath, const char *newpath, bool use_winsym,
goto done;
}
if (!isdevice && win32_newpath.fs_is_nfs ())
{
/* On NFS, create symlinks by calling NtCreateFile with an EA of type
NfsSymlinkTargetName containing ... the symlink target name. */
PFILE_FULL_EA_INFORMATION pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
pffei->NextEntryOffset = 0;
pffei->Flags = 0;
pffei->EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
char *EaValue = stpcpy (pffei->EaName, NFS_SYML_TARGET) + 1;
pffei->EaValueLength = sizeof (WCHAR) *
(sys_mbstowcs ((PWCHAR) EaValue, NT_MAX_PATH, oldpath) - 1);
status = NtCreateFile (&fh, FILE_WRITE_DATA | FILE_WRITE_EA | SYNCHRONIZE,
win32_newpath.get_object_attr (attr, sa),
&io, NULL, FILE_ATTRIBUTE_SYSTEM,
FILE_SHARE_VALID_FLAGS, FILE_CREATE,
FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_FOR_BACKUP_INTENT,
pffei, NT_MAX_PATH);
if (!NT_SUCCESS (status))
{
__seterrno_from_nt_status (status);
goto done;
}
NtClose (fh);
res = 0;
goto done;
}
if (use_winsym)
{
ITEMIDLIST *pidl = NULL;
@ -1664,7 +1711,10 @@ symlink_worker (const char *oldpath, const char *newpath, bool use_winsym,
full_len += sizeof (unsigned short) + relpath_len;
full_len += sizeof (unsigned short) + oldpath_len;
/* 1 byte more for trailing 0 written by stpcpy. */
buf = (char *) alloca (full_len + 1);
if (full_len < NT_MAX_PATH * sizeof (WCHAR))
buf = (char *) tp.w_get ();
else
buf = (char *) alloca (full_len + 1);
/* Create shortcut header */
win_shortcut_hdr *shortcut_header = (win_shortcut_hdr *) buf;
@ -1702,8 +1752,7 @@ symlink_worker (const char *oldpath, const char *newpath, bool use_winsym,
else
{
/* This is the old technique creating a symlink. */
unsigned short oldpath_len = strlen (oldpath);
buf = (char *) alloca (sizeof (SYMLINK_COOKIE) + oldpath_len + 1);
buf = tp.c_get ();
/* Note that the terminating nul is written. */
cp = stpcpy (stpcpy (buf, SYMLINK_COOKIE), oldpath) + 1;
}
@ -1726,7 +1775,7 @@ symlink_worker (const char *oldpath, const char *newpath, bool use_winsym,
NtClose (fh);
if (!NT_SUCCESS (status))
{
__seterrno_from_nt_status (status);
__seterrno_from_nt_status (status);
goto done;
}
}
@ -1791,32 +1840,49 @@ cmp_shortcut_header (win_shortcut_hdr *file_header)
}
int
symlink_info::check_shortcut (HANDLE h)
symlink_info::check_shortcut (HANDLE in_h)
{
tmp_pathbuf tp;
win_shortcut_hdr *file_header;
char *buf, *cp;
unsigned short len;
int res = 0;
UNICODE_STRING same = { 0, 0, (PWCHAR) L"" };
OBJECT_ATTRIBUTES attr;
NTSTATUS status;
HANDLE h;
IO_STATUS_BLOCK io;
FILE_STANDARD_INFORMATION fsi;
status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
FileStandardInformation);
InitializeObjectAttributes (&attr, &same, 0, in_h, NULL);
status = NtOpenFile (&h, FILE_GENERIC_READ,
&attr, &io, FILE_SHARE_VALID_FLAGS,
FILE_OPEN_FOR_BACKUP_INTENT
| FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS (status))
{
set_error (EIO);
return 0;
}
status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
FileStandardInformation);
if (!NT_SUCCESS (status))
{
set_error (EIO);
goto out;
}
if (fsi.EndOfFile.QuadPart <= sizeof (win_shortcut_hdr)
|| fsi.EndOfFile.QuadPart > 4 * 65536)
return 0;
buf = (char *) alloca (fsi.EndOfFile.LowPart + 1);
goto out;
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)))
{
set_error (EIO);
return 0;
goto out;
}
file_header = (win_shortcut_hdr *) buf;
if (io.Information != fsi.EndOfFile.LowPart
@ -1854,21 +1920,33 @@ file_not_symlink:
/* Not a symlink, see if executable. */
if (!(pflags & PATH_ALL_EXEC) && has_exec_chars ((const char *) &file_header, io.Information))
pflags |= PATH_EXEC;
out:
NtClose (h);
return 0;
}
int
symlink_info::check_sysfile (HANDLE h)
symlink_info::check_sysfile (HANDLE in_h)
{
char cookie_buf[sizeof (SYMLINK_COOKIE) - 1];
char srcbuf[SYMLINK_MAX + 2];
NTSTATUS status;
IO_STATUS_BLOCK io;
int res = 0;
UNICODE_STRING same = { 0, 0, (PWCHAR) L"" };
OBJECT_ATTRIBUTES attr;
NTSTATUS status;
HANDLE h;
IO_STATUS_BLOCK io;
status = NtReadFile (h, NULL, NULL, NULL, &io, cookie_buf,
sizeof (cookie_buf), NULL, NULL);
InitializeObjectAttributes (&attr, &same, 0, in_h, NULL);
status = NtOpenFile (&h, FILE_GENERIC_READ,
&attr, &io, FILE_SHARE_VALID_FLAGS,
FILE_OPEN_FOR_BACKUP_INTENT
| FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS (status))
set_error (EIO);
if (!NT_SUCCESS (status = NtReadFile (h, NULL, NULL, NULL, &io, cookie_buf,
sizeof (cookie_buf), NULL, NULL)))
{
debug_printf ("ReadFile1 failed");
if (status != STATUS_END_OF_FILE)
@ -1906,16 +1984,17 @@ symlink_info::check_sysfile (HANDLE h)
else
pflags |= PATH_NOTEXEC;
}
NtClose (h);
return res;
}
int
symlink_info::check_reparse_point (HANDLE h)
{
tmp_pathbuf tp;
NTSTATUS status;
IO_STATUS_BLOCK io;
PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER)
alloca (MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
char srcbuf[SYMLINK_MAX + 7];
status = NtFsControlFile (h, NULL, NULL, NULL, &io, FSCTL_GET_REPARSE_POINT,
@ -1954,6 +2033,39 @@ symlink_info::check_reparse_point (HANDLE h)
return posixify (srcbuf);
}
int
symlink_info::check_nfs_symlink (HANDLE h)
{
tmp_pathbuf tp;
NTSTATUS status;
IO_STATUS_BLOCK io;
struct {
FILE_GET_EA_INFORMATION fgei;
char buf[sizeof (NFS_SYML_TARGET)];
} fgei_buf;
PFILE_FULL_EA_INFORMATION pffei;
int res = 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;
fgei_buf.fgei.EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
stpcpy (fgei_buf.fgei.EaName, NFS_SYML_TARGET);
pffei = (PFILE_FULL_EA_INFORMATION) tp.c_get ();
status = NtQueryEaFile (h, &io, pffei, NT_MAX_PATH, TRUE,
&fgei_buf.fgei, sizeof fgei_buf,
NULL, TRUE);
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);
pflags = PATH_SYMLINK;
}
return res;
}
int
symlink_info::posixify (char *srcbuf)
{
@ -2200,17 +2312,11 @@ symlink_info::parse_device (const char *contents)
Return -1 on error, 0 if PATH is not a symlink, or the length
stored into BUF if PATH is a symlink. */
enum symlink_t {
is_no_symlink,
is_shortcut_symlink,
is_reparse_symlink,
is_sysfile_symlink
};
int
symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt)
symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt,
fs_info &fs)
{
HANDLE h;
HANDLE h = NULL;
int res = 0;
suffix_scan suffix;
contents[0] = '\0';
@ -2238,15 +2344,47 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt)
FILE_BASIC_INFORMATION fbi;
NTSTATUS status;
IO_STATUS_BLOCK io;
bool no_ea = false;
error = 0;
get_nt_native_path (suffix.path, upath, pflags & MOUNT_ENC);
status = NtQueryAttributesFile (&attr, &fbi);
if (NT_SUCCESS (status))
if (h)
{
NtClose (h);
h = NULL;
}
/* The EA given to NtCreateFile allows to get a handle to a symlink on
an NFS share, rather than getting a handle to the target of the
symlink (which would spoil the task of this method quite a bit).
Fortunately it's ignored on other file systems so we don't have
to special case NFS too much. */
status = NtCreateFile (&h,
READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA,
&attr, &io, NULL, FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_VALID_FLAGS, FILE_OPEN,
FILE_OPEN_REPARSE_POINT
| FILE_OPEN_FOR_BACKUP_INTENT,
&nfs_aol_ffei, sizeof nfs_aol_ffei);
if (!NT_SUCCESS (status))
{
no_ea = true;
status = NtCreateFile (&h,
READ_CONTROL | FILE_READ_ATTRIBUTES,
&attr, &io, NULL, FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_VALID_FLAGS, FILE_OPEN,
FILE_OPEN_REPARSE_POINT
| FILE_OPEN_FOR_BACKUP_INTENT,
&nfs_aol_ffei, sizeof nfs_aol_ffei);
}
if (NT_SUCCESS (status)
&& NT_SUCCESS (status
= NtQueryInformationFile (h, &io, &fbi, sizeof fbi,
FileBasicInformation)))
fileattr = fbi.FileAttributes;
else
{
debug_printf ("%p = NtQueryAttributesFile (%S)", status, &upath);
debug_printf ("%p = NtQueryInformationFile (%S)", status, &upath);
h = NULL;
fileattr = INVALID_FILE_ATTRIBUTES;
/* One of the inner path components is invalid, or the path contains
@ -2266,7 +2404,7 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt)
goto file_not_symlink;
}
if (status != STATUS_OBJECT_NAME_NOT_FOUND
&& status != STATUS_NO_SUCH_FILE) /* File not found on 9x share */
&& status != STATUS_NO_SUCH_FILE) /* ENOENT on NFS or 9x share */
{
/* The file exists, but the user can't access it for one reason
or the other. To get the file attributes we try to access the
@ -2312,29 +2450,43 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt)
ext_tacked_on = !!*ext_here;
goto file_not_symlink;
}
if (set_error (geterrno_from_win_error
(RtlNtStatusToDosError (status), EACCES)))
continue;
set_error (ENOENT);
continue;
}
/* Check file system while we're having the file open anyway.
This speeds up path_conv noticably (~10%). */
fs.update (&upath, h);
ext_tacked_on = !!*ext_here;
if (pcheck_case != PCHECK_RELAXED && !case_check (path)
|| (opt & PC_SYM_IGNORE))
goto file_not_symlink;
symlink_t sym_check;
res = -1;
sym_check = is_no_symlink;
/* Windows shortcuts are potentially treated as symlinks. Valid Cygwin
& U/WIN shortcuts are R/O, but definitely not directories. */
if ((fileattr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY))
== FILE_ATTRIBUTE_READONLY && suffix.lnk_match ())
{
res = check_shortcut (h);
if (!res)
{
/* If searching for `foo' and then finding a `foo.lnk' which is
no shortcut, return the same as if file not found. */
if (!suffix.lnk_match () || !ext_tacked_on)
goto file_not_symlink;
if ((fileattr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
== FILE_ATTRIBUTE_DIRECTORY)
goto file_not_symlink;
/* Windows shortcuts are potentially treated as symlinks. */
/* Valid Cygwin & U/WIN shortcuts are R/O. */
if ((fileattr & FILE_ATTRIBUTE_READONLY) && suffix.lnk_match ())
sym_check = is_shortcut_symlink;
/* in case we're going to tack *another* .lnk on this filename. */
fileattr = INVALID_FILE_ATTRIBUTES;
continue;
}
if (contents[0] == ':' && contents[1] == '\\'
&& parse_device (contents))
goto file_not_symlink;
}
/* Reparse points are potentially symlinks. This check must be
performed before checking the SYSTEM attribute for sysfile
@ -2342,69 +2494,39 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt)
For instance, Vista starts to create a couple of reparse points
with SYSTEM and HIDDEN flags set. */
else if (fileattr & FILE_ATTRIBUTE_REPARSE_POINT)
sym_check = is_reparse_symlink;
/* This is the old Cygwin method creating symlinks: */
/* A symlink will have the `system' file attribute. */
/* Only files can be symlinks (which can be symlinks to directories). */
else if (fileattr & FILE_ATTRIBUTE_SYSTEM)
sym_check = is_sysfile_symlink;
if (sym_check == is_no_symlink)
goto file_not_symlink;
res = -1;
/* Open the file. Opening reparse points must not use GENERIC_READ.
The reason is that Vista starts to create a couple of reparse
points for backward compatibility, hidden system files, explicitely
denying everyone FILE_READ_DATA access. */
status = NtOpenFile (&h,
sym_check == is_reparse_symlink
? READ_CONTROL : FILE_GENERIC_READ,
&attr, &io, FILE_SHARE_VALID_FLAGS,
FILE_OPEN_FOR_BACKUP_INTENT
| (sym_check == is_reparse_symlink
? FILE_OPEN_REPARSE_POINT
: FILE_SYNCHRONOUS_IO_NONALERT));
if (!NT_SUCCESS (status))
goto file_not_symlink;
switch (sym_check)
{
case is_shortcut_symlink:
res = check_shortcut (h);
NtClose (h);
if (!res)
/* check more below */;
else if (contents[0] == ':' && contents[1] == '\\' && parse_device (contents))
goto file_not_symlink;
else
break;
/* If searching for `foo' and then finding a `foo.lnk' which is
no shortcut, return the same as if file not found. */
if (!suffix.lnk_match () || !ext_tacked_on)
goto file_not_symlink;
fileattr = INVALID_FILE_ATTRIBUTES;
continue; /* in case we're going to tack *another* .lnk on this filename. */
case is_reparse_symlink:
res = check_reparse_point (h);
NtClose (h);
if (!res)
goto file_not_symlink;
break;
case is_sysfile_symlink:
res = check_sysfile (h);
NtClose (h);
if (!res)
goto file_not_symlink;
break;
default: /* Make gcc happy. Won't happen. */
goto file_not_symlink;
}
/* This is the old Cygwin method creating symlinks. A symlink will
have the `system' file attribute. Only files can be symlinks
(which can be symlinks to directories). */
else if ((fileattr & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY))
== FILE_ATTRIBUTE_SYSTEM)
{
res = check_sysfile (h);
if (!res)
goto file_not_symlink;
}
/* If the file could be opened with FILE_READ_EA, and if it's on a
NFS share, check if it's a symlink. */
else if (!no_ea && fs.is_nfs ())
{
res = check_nfs_symlink (h);
if (!res)
goto file_not_symlink;
}
/* Normal file. */
else
goto file_not_symlink;
break;
file_not_symlink:
issymlink = false;
syscall_printf ("%s", isdevice ? "is a device" : "not a symlink");
@ -2412,6 +2534,9 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt)
break;
}
if (h)
NtClose (h);
syscall_printf ("%d = symlink.check (%s, %p) (%p)",
res, suffix.path, contents, pflags);
return res;

View File

@ -127,7 +127,7 @@ struct fs_info
IMPLEMENT_STATUS_FLAG (bool, is_cdrom)
ULONG serial_number () const { return sernum; }
bool update (PUNICODE_STRING, bool) __attribute__ ((regparm (3)));
bool update (PUNICODE_STRING, HANDLE) __attribute__ ((regparm (3)));
};
class path_conv