* mount.cc (GETVOLINFO_VALID_MASK): Drop FILE_SEQUENTIAL_WRITE_ONCE

from mask.  Expand the comment a bit.
	(WIN_FAT_FLAGS): New define.
	(FS_IS_WINDOWS_FAT): New macro.
	(fs_info::update): Handle remote FS faking to be FAT.  Subsume under
	CIFS.  Check for NWFS and has_buggy_basic_info only for remote
	filesystems.  Add check for has_dos_filenames_only.
	* mount.h (class fs_info): Add has_dos_filenames_only status flag.
	Implement accessors.
	* path.cc (symlink_info::check): Rearrange variable definitions to
	clear them up.  Add a restart label to allow a clean restart within
	the method.  Add a check for broken filesystems only allowing DOS
	pathnames in case we encounter a STATUS_OBJECT_NAME_NOT_FOUND status.
	If all checks point to one of that, restart method with tweaked
	incoming path.  Add lengthy comments to explain what we do.
This commit is contained in:
Corinna Vinschen 2010-04-22 17:33:28 +00:00
parent c43e19442c
commit 6ff06a0726
4 changed files with 140 additions and 53 deletions

View File

@ -1,3 +1,21 @@
2010-04-22 Corinna Vinschen <corinna@vinschen.de>
* mount.cc (GETVOLINFO_VALID_MASK): Drop FILE_SEQUENTIAL_WRITE_ONCE
from mask. Expand the comment a bit.
(WIN_FAT_FLAGS): New define.
(FS_IS_WINDOWS_FAT): New macro.
(fs_info::update): Handle remote FS faking to be FAT. Subsume under
CIFS. Check for NWFS and has_buggy_basic_info only for remote
filesystems. Add check for has_dos_filenames_only.
* mount.h (class fs_info): Add has_dos_filenames_only status flag.
Implement accessors.
* path.cc (symlink_info::check): Rearrange variable definitions to
clear them up. Add a restart label to allow a clean restart within
the method. Add a check for broken filesystems only allowing DOS
pathnames in case we encounter a STATUS_OBJECT_NAME_NOT_FOUND status.
If all checks point to one of that, restart method with tweaked
incoming path. Add lengthy comments to explain what we do.
2010-04-22 Corinna Vinschen <corinna@vinschen.de> 2010-04-22 Corinna Vinschen <corinna@vinschen.de>
* path.cc (symlink_info::check): Fix a comment. * path.cc (symlink_info::check): Fix a comment.

View File

@ -208,9 +208,12 @@ fs_info::update (PUNICODE_STRING upath, HANDLE in_vol)
/* Should be reevaluated for each new OS. Right now this mask is valid up /* 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 to Vista. The important point here is to test only flags indicating
capabilities and to ignore flags indicating a specific state of this capabilities and to ignore flags indicating a specific state of this
volume. At present these flags to ignore are FILE_VOLUME_IS_COMPRESSED volume. At present these flags to ignore are FILE_VOLUME_IS_COMPRESSED,
and FILE_READ_ONLY_VOLUME. */ FILE_READ_ONLY_VOLUME, and FILE_SEQUENTIAL_WRITE_ONCE. The additional
#define GETVOLINFO_VALID_MASK (0x003701ffUL) filesystem flags supported since Windows 7 are also ignored for now.
They add information, but only on W7 and later, and only for filesystems
also supporting these flags, right now only NTFS. */
#define GETVOLINFO_VALID_MASK (0x002701ffUL)
#define TEST_GVI(f,m) (((f) & GETVOLINFO_VALID_MASK) == (m)) #define TEST_GVI(f,m) (((f) & GETVOLINFO_VALID_MASK) == (m))
/* FIXME: This flag twist is getting awkward. There should really be some /* FIXME: This flag twist is getting awkward. There should really be some
@ -244,6 +247,11 @@ fs_info::update (PUNICODE_STRING upath, HANDLE in_vol)
| FILE_FILE_COMPRESSION) | FILE_FILE_COMPRESSION)
#define FS_IS_WINDOWS_NTFS TEST_GVI(flags () & MINIMAL_WIN_NTFS_FLAGS, \ #define FS_IS_WINDOWS_NTFS TEST_GVI(flags () & MINIMAL_WIN_NTFS_FLAGS, \
MINIMAL_WIN_NTFS_FLAGS) MINIMAL_WIN_NTFS_FLAGS)
/* These are the exact flags of a real Windows FAT/FAT32 filesystem.
Anything else is a filesystem faking to be FAT. */
#define WIN_FAT_FLAGS (FILE_CASE_PRESERVED_NAMES | FILE_UNICODE_ON_DISK)
#define FS_IS_WINDOWS_FAT TEST_GVI(flags (), WIN_FAT_FLAGS)
/* This always fails on NT4. */ /* This always fails on NT4. */
status = NtQueryVolumeInformationFile (vol, &io, &ffoi, sizeof ffoi, status = NtQueryVolumeInformationFile (vol, &io, &ffoi, sizeof ffoi,
FileFsObjectIdInformation); FileFsObjectIdInformation);
@ -266,6 +274,11 @@ fs_info::update (PUNICODE_STRING upath, HANDLE in_vol)
&& !is_netapp (FS_IS_NETAPP_DATAONTAP)) && !is_netapp (FS_IS_NETAPP_DATAONTAP))
/* Any other remote FS faking to be NTFS. */ /* Any other remote FS faking to be NTFS. */
is_cifs (!FS_IS_WINDOWS_NTFS); is_cifs (!FS_IS_WINDOWS_NTFS);
/* Then check the remote filesystems faking to be FAT. Right now all
of them are subsumed under the "CIFS" filesystem type. */
if (!got_fs ()
&& is_fat (RtlEqualUnicodePathPrefix (&fsname, &ro_u_fat, TRUE)))
is_cifs (!FS_IS_WINDOWS_FAT);
/* Then check remote filesystems honest about their name. */ /* Then check remote filesystems honest about their name. */
if (!got_fs () if (!got_fs ()
/* Microsoft NFS needs distinct access methods for metadata. */ /* Microsoft NFS needs distinct access methods for metadata. */
@ -274,6 +287,8 @@ fs_info::update (PUNICODE_STRING upath, HANDLE in_vol)
drawbacks, like not supporting DOS attributes other than R/O drawbacks, like not supporting DOS attributes other than R/O
and stuff like that. */ and stuff like that. */
&& !is_mvfs (RtlEqualUnicodePathPrefix (&fsname, &ro_u_mvfs, FALSE)) && !is_mvfs (RtlEqualUnicodePathPrefix (&fsname, &ro_u_mvfs, FALSE))
/* NWFS == Novell Netware FS. Broken info class, see below. */
&& !is_nwfs (RtlEqualUnicodeString (&fsname, &ro_u_nwfs, FALSE))
/* Known remote file system which can't handle calls to /* Known remote file system which can't handle calls to
NtQueryDirectoryFile(FileIdBothDirectoryInformation) */ NtQueryDirectoryFile(FileIdBothDirectoryInformation) */
&& !is_unixfs (RtlEqualUnicodeString (&fsname, &ro_u_unixfs, FALSE))) && !is_unixfs (RtlEqualUnicodeString (&fsname, &ro_u_unixfs, FALSE)))
@ -283,18 +298,31 @@ fs_info::update (PUNICODE_STRING upath, HANDLE in_vol)
is_sunwnfs (RtlEqualUnicodeString (&fsname, &ro_u_sunwnfs, FALSE)); is_sunwnfs (RtlEqualUnicodeString (&fsname, &ro_u_sunwnfs, FALSE));
has_buggy_open (is_sunwnfs ()); has_buggy_open (is_sunwnfs ());
} }
/* Not only UNIXFS is known to choke on FileIdBothDirectoryInformation.
Some other CIFS servers have problems with this call as well.
Know example: EMC NS-702. We just don't use that info class on
any remote CIFS. */
if (got_fs ()) if (got_fs ())
has_buggy_fileid_dirinfo (is_cifs () || is_unixfs ()); {
/* UNIXFS is known to choke on FileIdBothDirectoryInformation.
Some other CIFS servers have problems with this call as well.
Know example: EMC NS-702. We just don't use that info class on
any remote CIFS. */
has_buggy_fileid_dirinfo (is_cifs () || is_unixfs ());
/* NWFS is known to have a broken FileBasicInformation info class.
It can't be used to fetch information, only to set information.
Therefore, for NWFS we have to fallback to the
FileNetworkOpenInformation info class. Unfortunately we can't
use FileNetworkOpenInformation all the time since that fails on
other filesystems like NFS. */
has_buggy_basic_info (is_nwfs ());
/* Netapp ans NWFS are too dumb to allow non-DOS filesystems
containing trailing dots and spaces when accessed from Windows
clients. We subsume CIFS into this class of filesystems right
away since at least some of them are not capable either. */
has_dos_filenames_only (is_netapp () || is_nwfs () || is_cifs ());
}
} }
if (!got_fs () if (!got_fs ()
&& !is_ntfs (RtlEqualUnicodeString (&fsname, &ro_u_ntfs, FALSE)) && !is_ntfs (RtlEqualUnicodeString (&fsname, &ro_u_ntfs, FALSE))
&& !is_fat (RtlEqualUnicodePathPrefix (&fsname, &ro_u_fat, TRUE)) && !is_fat (RtlEqualUnicodePathPrefix (&fsname, &ro_u_fat, TRUE))
&& !is_csc_cache (RtlEqualUnicodeString (&fsname, &ro_u_csc, FALSE)) && !is_csc_cache (RtlEqualUnicodeString (&fsname, &ro_u_csc, FALSE))
&& !is_nwfs (RtlEqualUnicodeString (&fsname, &ro_u_nwfs, FALSE))
&& is_cdrom (ffdi.DeviceType == FILE_DEVICE_CD_ROM)) && is_cdrom (ffdi.DeviceType == FILE_DEVICE_CD_ROM))
is_udf (RtlEqualUnicodeString (&fsname, &ro_u_udf, FALSE)); is_udf (RtlEqualUnicodeString (&fsname, &ro_u_udf, FALSE));
if (!got_fs ()) if (!got_fs ())
@ -308,12 +336,6 @@ fs_info::update (PUNICODE_STRING upath, HANDLE in_vol)
has_acls (flags () & FS_PERSISTENT_ACLS); has_acls (flags () & FS_PERSISTENT_ACLS);
/* Netapp inode numbers are fly-by-night. */ /* Netapp inode numbers are fly-by-night. */
hasgood_inode ((has_acls () && !is_netapp ()) || is_nfs ()); hasgood_inode ((has_acls () && !is_netapp ()) || is_nfs ());
/* NWFS is known to have a broken FileBasicInformation info class. It
can't be used to fetch information, only to set information. Therefore,
for NWFS we have to fallback to the FileNetworkOpenInformation info
class. Unfortunately we can't use FileNetworkOpenInformation all the
time since that fails on other filesystems like NFS. */
has_buggy_basic_info (is_nwfs ());
/* Case sensitivity is supported if FILE_CASE_SENSITIVE_SEARCH is set, /* Case sensitivity is supported if FILE_CASE_SENSITIVE_SEARCH is set,
except on Samba which handles Windows clients case insensitive. except on Samba which handles Windows clients case insensitive.

View File

@ -51,6 +51,7 @@ class fs_info
unsigned has_buggy_open : 1; unsigned has_buggy_open : 1;
unsigned has_buggy_fileid_dirinfo : 1; unsigned has_buggy_fileid_dirinfo : 1;
unsigned has_buggy_basic_info : 1; unsigned has_buggy_basic_info : 1;
unsigned has_dos_filenames_only : 1;
} status; } status;
ULONG sernum; /* Volume Serial Number */ ULONG sernum; /* Volume Serial Number */
char fsn[80]; /* Windows filesystem name */ char fsn[80]; /* Windows filesystem name */
@ -75,6 +76,7 @@ class fs_info
IMPLEMENT_STATUS_FLAG (bool, has_buggy_open) IMPLEMENT_STATUS_FLAG (bool, has_buggy_open)
IMPLEMENT_STATUS_FLAG (bool, has_buggy_fileid_dirinfo) IMPLEMENT_STATUS_FLAG (bool, has_buggy_fileid_dirinfo)
IMPLEMENT_STATUS_FLAG (bool, has_buggy_basic_info) IMPLEMENT_STATUS_FLAG (bool, has_buggy_basic_info)
IMPLEMENT_STATUS_FLAG (bool, has_dos_filenames_only)
IMPLEMENT_FS_FLAG (is_fat, fat) IMPLEMENT_FS_FLAG (is_fat, fat)
IMPLEMENT_FS_FLAG (is_ntfs, ntfs) IMPLEMENT_FS_FLAG (is_ntfs, ntfs)
IMPLEMENT_FS_FLAG (is_samba, samba) IMPLEMENT_FS_FLAG (is_samba, samba)

View File

@ -2170,29 +2170,39 @@ 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) fs_info &fs)
{ {
HANDLE h = NULL; int res;
int res = 0; HANDLE h;
NTSTATUS status;
UNICODE_STRING upath;
OBJECT_ATTRIBUTES attr;
IO_STATUS_BLOCK io;
FILE_BASIC_INFORMATION fbi;
suffix_scan suffix; suffix_scan suffix;
contents[0] = '\0';
ULONG ci_flag = cygwin_shared->obcaseinsensitive || (pflags & PATH_NOPOSIX)
? OBJ_CASE_INSENSITIVE : 0;
/* TODO: Temporarily do all char->UNICODE conversion here. This should
already be slightly faster than using Ascii functions. */
tmp_pathbuf tp;
tp.u_get (&upath);
InitializeObjectAttributes (&attr, &upath, ci_flag, NULL, NULL);
/* This label is used in case we encounter a FS which only handles
DOS paths. See below. */
restart:
h = NULL;
res = 0;
contents[0] = '\0';
issymlink = true; issymlink = true;
isdevice = false; isdevice = false;
ext_here = suffix.has (path, suffixes);
extn = ext_here - path;
major = 0; major = 0;
minor = 0; minor = 0;
mode = 0; mode = 0;
pflags &= ~(PATH_SYMLINK | PATH_LNK | PATH_REP); pflags &= ~(PATH_SYMLINK | PATH_LNK | PATH_REP);
ULONG ci_flag = cygwin_shared->obcaseinsensitive || (pflags & PATH_NOPOSIX)
? OBJ_CASE_INSENSITIVE : 0;
/* TODO: Temporarily do all char->UNICODE conversion here. This should ext_here = suffix.has (path, suffixes);
already be slightly faster than using Ascii functions. */ extn = ext_here - path;
tmp_pathbuf tp;
UNICODE_STRING upath;
OBJECT_ATTRIBUTES attr;
tp.u_get (&upath);
InitializeObjectAttributes (&attr, &upath, ci_flag, NULL, NULL);
PVOID eabuf = &nfs_aol_ffei; PVOID eabuf = &nfs_aol_ffei;
ULONG easize = sizeof nfs_aol_ffei; ULONG easize = sizeof nfs_aol_ffei;
@ -2200,9 +2210,6 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt,
bool had_ext = !!*ext_here; bool had_ext = !!*ext_here;
while (suffix.next ()) while (suffix.next ())
{ {
FILE_BASIC_INFORMATION fbi;
NTSTATUS status;
IO_STATUS_BLOCK io;
bool no_ea = false; bool no_ea = false;
bool fs_update_called = false; bool fs_update_called = false;
@ -2251,28 +2258,66 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt,
| FILE_OPEN_FOR_BACKUP_INTENT); | FILE_OPEN_FOR_BACKUP_INTENT);
debug_printf ("%p = NtOpenFile (no-EA, %S)", status, &upath); debug_printf ("%p = NtOpenFile (no-EA, %S)", status, &upath);
} }
if (status == STATUS_OBJECT_NAME_NOT_FOUND && ci_flag == 0 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
&& wincap.has_broken_udf ()) {
{ if (ci_flag == 0 && wincap.has_broken_udf ())
/* On NT 5.x UDF is broken (at least) in terms of case sensitivity.
When trying to open a file case sensitive, the file appears to be
non-existant. Another bug is described in fs_info::update. */
attr.Attributes = OBJ_CASE_INSENSITIVE;
status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
&attr, &io, FILE_SHARE_VALID_FLAGS,
FILE_OPEN_REPARSE_POINT
| FILE_OPEN_FOR_BACKUP_INTENT);
debug_printf ("%p = NtOpenFile (broken-UDF, %S)", status, &upath);
attr.Attributes = 0;
if (NT_SUCCESS (status))
{ {
fs.update (&upath, h); /* On NT 5.x UDF is broken (at least) in terms of case
if (fs.is_udf ()) sensitivity. When trying to open a file case sensitive,
fs_update_called = true; the file appears to be non-existant. Another bug is
else described in fs_info::update. */
{ attr.Attributes = OBJ_CASE_INSENSITIVE;
NtClose (h); status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
status = STATUS_OBJECT_NAME_NOT_FOUND; &attr, &io, FILE_SHARE_VALID_FLAGS,
FILE_OPEN_REPARSE_POINT
| FILE_OPEN_FOR_BACKUP_INTENT);
debug_printf ("%p = NtOpenFile (broken-UDF, %S)", status, &upath);
attr.Attributes = 0;
if (NT_SUCCESS (status))
{
fs.update (&upath, h);
if (fs.is_udf ())
fs_update_called = true;
else
{
NtClose (h);
status = STATUS_OBJECT_NAME_NOT_FOUND;
}
}
}
/* There are filesystems out in the wild (Netapp, NWFS, and others)
which are uncapable of generating pathnames outside the Win32
rules. That means, filenames on these FSes must not have a
leading space or trailing dots and spaces. This code snippet
manages them. I really hope it's streamlined enough not to
slow down normal operation. This extra check only kicks in if
we encountered a STATUS_OBJECT_NAME_NOT_FOUND *and* we didn't
already attach a suffix *and* the above special case for UDF
on XP didn't succeeed. */
if (!*ext_here && !fs_update_called)
{
/* Check for leading space or trailing dot or space in
last component. */
char *pend = ext_here;
while (pend[-1] == '.' || pend[-1] == ' ')
--pend;
char *pbeg = pend;
while (pbeg[-1] != '\\')
--pbeg;
/* If so, call fs.update to check if the filesystem is one of
the broken ones. */
if ((*pbeg == ' ' || *pend != '\0')
&& fs.update (&upath, NULL)
&& fs.has_dos_filenames_only ())
{
/* If so, strip leading spaces and trailing dots and spaces
from filename and... */
if (pbeg)
while (*pbeg == ' ')
memmove (pbeg, pbeg + 1, --pend - pbeg);
*pend = '\0';
/* ...try again. */
goto restart;
} }
} }
} }