* 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:
parent
c43e19442c
commit
6ff06a0726
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue