From 330a2faed95927d539db8bae15748eb1e0e12a02 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Wed, 26 Apr 2006 16:51:09 +0000 Subject: [PATCH] * fhandler.h (fhandler_base): Change fstat_helper prototype to take file size and inode number as 64 bit values. * fhandler_disk_file.cc (FS_IS_SAMBA): Move to path.cc (FS_IS_SAMBA_WITH_QUOTA): Ditto. (path_conv::hasgood_inode): Delete. (path_conv::is_samba): Delete. (path_conv::isgood_inode): Centralized function to recognize a good inode number. (fhandler_base::fstat_by_handle): Constify fvi_size and fai_size. Accomodate argument change in fstat_helper. (fhandler_base::fstat_by_name): Ditto. (fhandler_base::fstat_helper): Accomodate argument change. Call path_conv::isgood_inode to recognize good inodes. (fhandler_disk_file::opendir): Explain Samba weirdness here. Call path_conv::fs_is_samba instead of path_conv::is_samba. (fhandler_disk_file::readdir): Add STATUS_INVALID_INFO_CLASS as valid return code from NtQueryDirectoryFile to indicate that FileIdBothDirectoryInformation is not supported. Call path_conv::isgood_inode to recognize good inodes. * ntdll.h (STATUS_INVALID_INFO_CLASS): Define. * path.cc (fs_info::update): Rework file system recognition and set appropriate flags. * path.h (struct fs_info): Add is_ntfs, is_samba and is_nfs flags. Constify pure read accessors. --- winsup/cygwin/ChangeLog | 27 +++++++ winsup/cygwin/fhandler.h | 6 +- winsup/cygwin/fhandler_disk_file.cc | 117 +++++++++++----------------- winsup/cygwin/ntdll.h | 1 + winsup/cygwin/path.cc | 25 ++++-- winsup/cygwin/path.h | 43 ++++++---- 6 files changed, 124 insertions(+), 95 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 708c51125..b7a8f46c9 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,30 @@ +2006-04-26 Corinna Vinschen + + * fhandler.h (fhandler_base): Change fstat_helper prototype + to take file size and inode number as 64 bit values. + * fhandler_disk_file.cc (FS_IS_SAMBA): Move to path.cc + (FS_IS_SAMBA_WITH_QUOTA): Ditto. + (path_conv::hasgood_inode): Delete. + (path_conv::is_samba): Delete. + (path_conv::isgood_inode): Centralized function to recognize + a good inode number. + (fhandler_base::fstat_by_handle): Constify fvi_size and fai_size. + Accomodate argument change in fstat_helper. + (fhandler_base::fstat_by_name): Ditto. + (fhandler_base::fstat_helper): Accomodate argument change. Call + path_conv::isgood_inode to recognize good inodes. + (fhandler_disk_file::opendir): Explain Samba weirdness here. + Call path_conv::fs_is_samba instead of path_conv::is_samba. + (fhandler_disk_file::readdir): Add STATUS_INVALID_INFO_CLASS + as valid return code from NtQueryDirectoryFile to indicate that + FileIdBothDirectoryInformation is not supported. + Call path_conv::isgood_inode to recognize good inodes. + * ntdll.h (STATUS_INVALID_INFO_CLASS): Define. + * path.cc (fs_info::update): Rework file system recognition + and set appropriate flags. + * path.h (struct fs_info): Add is_ntfs, is_samba and is_nfs flags. + Constify pure read accessors. + 2006-04-24 Christopher Faylor * environ.cc (getearly): Force correct dereference order when diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index bd068a88d..52e41e27a 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -266,11 +266,9 @@ class fhandler_base FILETIME ftLastAccessTime, FILETIME ftLastWriteTime, DWORD dwVolumeSerialNumber, - DWORD nFileSizeHigh, - DWORD nFileSizeLow, + ULONGLONG nFileSize, LONGLONG nAllocSize, - DWORD nFileIndexHigh, - DWORD nFileIndexLow, + ULONGLONG nFileIndex, DWORD nNumberOfLinks, DWORD dwFileAttributes) __attribute__ ((regparm (3))); diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc index 04f312b8a..7470dcb94 100644 --- a/winsup/cygwin/fhandler_disk_file.cc +++ b/winsup/cygwin/fhandler_disk_file.cc @@ -196,46 +196,15 @@ path_conv::ndisk_links (DWORD nNumberOfLinks) return count + saw_dot; } -#define FS_IS_SAMBA (FILE_CASE_SENSITIVE_SEARCH \ - | FILE_CASE_PRESERVED_NAMES \ - | FILE_PERSISTENT_ACLS) - -#define FS_IS_SAMBA_WITH_QUOTA \ - (FILE_CASE_SENSITIVE_SEARCH \ - | FILE_CASE_PRESERVED_NAMES \ - | FILE_PERSISTENT_ACLS \ - | FILE_VOLUME_QUOTAS) - inline bool -path_conv::hasgood_inode () +path_conv::isgood_inode (__ino64_t ino) const { - /* Assume that if a drive has ACL support it MAY have valid "inodes". - It definitely does not have valid inodes if it does not have ACL - support. Decouple from has_acls() which follows smbntsec setting. */ - return ((fs_flags () & FILE_PERSISTENT_ACLS) - && drive_type () != DRIVE_UNKNOWN); -} - -inline bool -path_conv::is_samba () -{ - /* Something weird happens on Samba up to version 3.0.21c, which is - fixed in 3.0.22. FileIdBothDirectoryInformation seems to work - nicely, but only up to the 128th entry in the directory. After - reaching this entry, the next call to - NtQueryDirectoryFile(FileIdBothDirectoryInformation) returns - STATUS_INVAILD_LEVEL. Why should we care, we can just switch to - FileBothDirectoryInformation, isn't it? Nope! The next call to - NtQueryDirectoryFile(FileBothDirectoryInformation) actually returns - STATUS_NO_MORE_FILES, regardless how many files are left unread in - the directory. This does not happen when using - FileBothDirectoryInformation right from the start. In that case we - can read the whole directory unmolested. So we have to excempt - Samba from the usage of FileIdBothDirectoryInformation entirely, - even though Samba returns valid File IDs. */ - return drive_type () == DRIVE_REMOTE - && (fs_flags () == FS_IS_SAMBA - || fs_flags () == FS_IS_SAMBA_WITH_QUOTA); + /* We can't trust remote inode numbers of only 32 bit. That means, + all remote inode numbers when running under NT4, as well as remote NT4 + NTFS, as well as shares of Samba version < 3.0. + The known exception are SFU NFS shares, which return the valid 32 bit + inode number from the remote file system unchanged. */ + return hasgood_inode () && (ino > UINT32_MAX || !isremote () || fs_is_nfs ()); } int __stdcall @@ -248,8 +217,9 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf) NTSTATUS status; IO_STATUS_BLOCK io; /* The entries potentially contain a name of MAX_PATH wide characters. */ - DWORD fvi_size = 2 * CYG_MAX_PATH + sizeof (FILE_FS_VOLUME_INFORMATION); - DWORD fai_size = 2 * CYG_MAX_PATH + sizeof (FILE_ALL_INFORMATION); + const DWORD fvi_size = 2 * CYG_MAX_PATH + + sizeof (FILE_FS_VOLUME_INFORMATION); + const DWORD fai_size = 2 * CYG_MAX_PATH + sizeof (FILE_ALL_INFORMATION); PFILE_FS_VOLUME_INFORMATION pfvi = (PFILE_FS_VOLUME_INFORMATION) alloca (fvi_size); @@ -278,11 +248,9 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf) *(FILETIME *) &pfai->BasicInformation.LastAccessTime, *(FILETIME *) &pfai->BasicInformation.LastWriteTime, pfvi->VolumeSerialNumber, - pfai->StandardInformation.EndOfFile.HighPart, - pfai->StandardInformation.EndOfFile.LowPart, + pfai->StandardInformation.EndOfFile.QuadPart, pfai->StandardInformation.AllocationSize.QuadPart, - pfai->InternalInformation.FileId.HighPart, - pfai->InternalInformation.FileId.LowPart, + pfai->InternalInformation.FileId.QuadPart, pfai->StandardInformation.NumberOfLinks, pfai->BasicInformation.FileAttributes); } @@ -313,11 +281,11 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf) local.ftLastAccessTime, local.ftLastWriteTime, local.dwVolumeSerialNumber, - local.nFileSizeHigh, - local.nFileSizeLow, + (ULONGLONG) local.nFileSizeHigh << 32 + | local.nFileSizeLow, -1LL, - local.nFileIndexHigh, - local.nFileIndexLow, + (ULONGLONG) local.nFileIndexHigh << 32 + | local.nFileIndexLow, local.nNumberOfLinks, local.dwFileAttributes); } @@ -344,18 +312,17 @@ fhandler_base::fstat_by_name (struct __stat64 *buf) local.ftLastAccessTime, local.ftLastWriteTime, pc.volser (), - local.nFileSizeHigh, - local.nFileSizeLow, + (ULONGLONG) local.nFileSizeHigh << 32 + | local.nFileSizeLow, -1LL, - 0, - 0, + 0ULL, 1, local.dwFileAttributes); } else if (pc.isdir ()) { FILETIME ft = {}; - res = fstat_helper (buf, ft, ft, ft, pc.volser (), 0, 0, -1LL, 0, 0, 1, + res = fstat_helper (buf, ft, ft, ft, pc.volser (), 0ULL, -1LL, 0ULL, 1, FILE_ATTRIBUTE_DIRECTORY); } else @@ -433,11 +400,9 @@ fhandler_base::fstat_helper (struct __stat64 *buf, FILETIME ftLastAccessTime, FILETIME ftLastWriteTime, DWORD dwVolumeSerialNumber, - DWORD nFileSizeHigh, - DWORD nFileSizeLow, + ULONGLONG nFileSize, LONGLONG nAllocSize, - DWORD nFileIndexHigh, - DWORD nFileIndexLow, + ULONGLONG nFileIndex, DWORD nNumberOfLinks, DWORD dwFileAttributes) { @@ -448,7 +413,7 @@ fhandler_base::fstat_helper (struct __stat64 *buf, to_timestruc_t (&ftLastWriteTime, &buf->st_mtim); to_timestruc_t (&ftChangeTime, &buf->st_ctim); buf->st_dev = dwVolumeSerialNumber ?: pc.volser (); - buf->st_size = ((_off64_t) nFileSizeHigh << 32) + nFileSizeLow; + buf->st_size = (_off64_t) nFileSize; /* The number of links to a directory includes the number of subdirectories in the directory, since all those subdirectories point to it. @@ -457,12 +422,9 @@ fhandler_base::fstat_helper (struct __stat64 *buf, let's try it with `1' as link count. */ buf->st_nlink = pc.ndisk_links (nNumberOfLinks); - /* We can't trust remote inode numbers of only 32 bit. That means, - all remote inode numbers when running under NT4, as well as remote NT4 - NTFS, as well as shares of Samba version < 3.0. */ - if (pc.hasgood_inode () && (nFileIndexHigh || !pc.isremote ())) - buf->st_ino = (((__ino64_t) nFileIndexHigh) << 32) - | (__ino64_t) nFileIndexLow; + /* Enforce namehash as inode number on untrusted file systems. */ + if (pc.isgood_inode (nFileIndex)) + buf->st_ino = (__ino64_t) nFileIndex; else buf->st_ino = get_namehash (); @@ -1586,7 +1548,23 @@ fhandler_disk_file::opendir () if (pc.hasgood_inode ()) { dir->__flags |= dirent_set_d_ino; - if (wincap.has_fileid_dirinfo () && !pc.is_samba ()) + /* Something weird happens on Samba up to version 3.0.21c, + which is fixed in 3.0.22. FileIdBothDirectoryInformation + seems to work nicely, but only up to the 128th entry in + the directory. After reaching this entry, the next call + to NtQueryDirectoryFile(FileIdBothDirectoryInformation) + returns STATUS_INVALID_LEVEL. Why should we care, we can + just switch to FileBothDirectoryInformation, isn't it? + Nope! The next call to + NtQueryDirectoryFile(FileBothDirectoryInformation) + actually returns STATUS_NO_MORE_FILES, regardless how + many files are left unread in the directory. This does + not happen when using FileBothDirectoryInformation right + from the start. In that case we can read the whole + directory unmolested. So we have to excempt Samba from + the usage of FileIdBothDirectoryInformation entirely, + even though Samba returns valid File IDs. */ + if (wincap.has_fileid_dirinfo () && !pc.fs_is_samba ()) dir->__flags |= dirent_get_d_ino; } } @@ -1736,7 +1714,8 @@ fhandler_disk_file::readdir (DIR *dir, dirent *de) back to using a standard directory query in this case and note this case using the dirent_get_d_ino flag. */ if (status == STATUS_INVALID_LEVEL - || status == STATUS_INVALID_PARAMETER) + || status == STATUS_INVALID_PARAMETER + || status == STATUS_INVALID_INFO_CLASS) dir->__flags &= ~dirent_get_d_ino; } if (!(dir->__flags & dirent_get_d_ino)) @@ -1788,10 +1767,8 @@ fhandler_disk_file::readdir (DIR *dir, dirent *de) CloseHandle (hdl); } } - /* We can't trust remote inode numbers of only 32 bit. That means, - all remote inode numbers when running under NT4, as well as - remote NT4 NTFS, as well as shares of Samba version < 3.0. */ - if (de->d_ino <= UINT32_MAX && pc.isremote ()) + /* Enforce namehash as inode number on untrusted file systems. */ + if (!pc.isgood_inode (de->d_ino)) { dir->__flags &= ~dirent_set_d_ino; de->d_ino = 0; diff --git a/winsup/cygwin/ntdll.h b/winsup/cygwin/ntdll.h index 6cdc2ce32..265f308f6 100644 --- a/winsup/cygwin/ntdll.h +++ b/winsup/cygwin/ntdll.h @@ -8,6 +8,7 @@ Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ +#define STATUS_INVALID_INFO_CLASS ((NTSTATUS) 0xc0000003) #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS) 0xc0000004) #define STATUS_INVALID_PARAMETER ((NTSTATUS) 0xc000000d) #define STATUS_BUFFER_TOO_SMALL ((NTSTATUS) 0xc0000023) diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index 44ceb6c1c..a95d311e7 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -423,15 +423,26 @@ fs_info::update (const char *win32_path) return false; } - /* FIXME: Samba by default returns "NTFS" in file system name, but - * doesn't support Extended Attributes. If there's some fast way to - * distinguish between samba and real ntfs, it should be implemented - * here. - */ - has_ea (!is_remote_drive () && strcmp (fsname, "NTFS") == 0); +#define FS_IS_SAMBA (FILE_CASE_SENSITIVE_SEARCH \ + | FILE_CASE_PRESERVED_NAMES \ + | FILE_PERSISTENT_ACLS) +#define FS_IS_SAMBA_WITH_QUOTA \ + (FILE_CASE_SENSITIVE_SEARCH \ + | FILE_CASE_PRESERVED_NAMES \ + | FILE_PERSISTENT_ACLS \ + | FILE_VOLUME_QUOTAS) + is_fat (strncasematch (fsname, "FAT", 3)); + is_samba (strcmp (fsname, "NTFS") == 0 && is_remote_drive () + && (flags () == FS_IS_SAMBA || flags () == FS_IS_SAMBA_WITH_QUOTA)); + is_ntfs (strcmp (fsname, "NTFS") == 0 && !is_samba ()); + is_nfs (strcmp (fsname, "NFS") == 0); + + has_ea (is_ntfs ()); has_acls ((flags () & FS_PERSISTENT_ACLS) && (allow_smbntsec || !is_remote_drive ())); - is_fat (strncasematch (fsname, "FAT", 3)); + hasgood_inode (((flags () & FILE_PERSISTENT_ACLS) + && drive_type () != DRIVE_UNKNOWN) + || is_nfs ()); /* Known file systems with buggy open calls. Further explanation in fhandler.cc (fhandler_disk_file::open). */ has_buggy_open (!strcmp (fsname, "SUNWNFS")); diff --git a/winsup/cygwin/path.h b/winsup/cygwin/path.h index 233dd15b8..d432e70d3 100644 --- a/winsup/cygwin/path.h +++ b/winsup/cygwin/path.h @@ -92,8 +92,12 @@ struct fs_info unsigned has_buggy_open : 1; unsigned has_ea : 1; unsigned has_acls : 1; - unsigned is_fat : 1; + unsigned hasgood_inode : 1; unsigned drive_type : 3; + unsigned is_fat : 1; + unsigned is_ntfs : 1; + unsigned is_samba : 1; + unsigned is_nfs : 1; } status; public: void clear () @@ -104,18 +108,26 @@ struct fs_info has_buggy_open (false); has_ea (false); has_acls (false); - is_fat (false); + hasgood_inode (false); drive_type (false); + is_fat (false); + is_ntfs (false); + is_samba (false); + is_nfs (false); } inline DWORD& flags () {return status.flags;}; inline DWORD& serial () {return status.serial;}; IMPLEMENT_STATUS_FLAG (bool, is_remote_drive) IMPLEMENT_STATUS_FLAG (bool, has_buggy_open) - IMPLEMENT_STATUS_FLAG (bool, is_fat) IMPLEMENT_STATUS_FLAG (bool, has_ea) IMPLEMENT_STATUS_FLAG (bool, has_acls) + IMPLEMENT_STATUS_FLAG (bool, hasgood_inode) IMPLEMENT_STATUS_FLAG (DWORD, drive_type) + IMPLEMENT_STATUS_FLAG (bool, is_fat) + IMPLEMENT_STATUS_FLAG (bool, is_ntfs) + IMPLEMENT_STATUS_FLAG (bool, is_samba) + IMPLEMENT_STATUS_FLAG (bool, is_nfs) bool update (const char *); }; @@ -133,13 +145,13 @@ class path_conv device dev; bool case_clash; - bool isremote () {return fs.is_remote_drive ();} - int has_acls () const {return fs.has_acls (); } + bool isremote () const {return fs.is_remote_drive ();} + bool has_acls () const {return fs.has_acls (); } + bool hasgood_inode () const {return fs.hasgood_inode (); } + bool isgood_inode (__ino64_t ino) const; int has_symlinks () const {return path_flags & PATH_HAS_SYMLINKS;} - bool hasgood_inode (); /* Implemented in fhandler_disk_file.cc */ - bool is_samba (); /* Implemented in fhandler_disk_file.cc */ int has_buggy_open () const {return fs.has_buggy_open ();} - bool isencoded () {return path_flags & PATH_ENC;} + bool isencoded () const {return path_flags & PATH_ENC;} int binmode () const { if (path_flags & PATH_BINARY) @@ -222,14 +234,17 @@ class path_conv operator DWORD &() {return fileattr;} operator int () {return fileattr; } char operator [](int i) const {return path[i];} - DWORD get_devn () {return dev.devn;} - short get_unitn () {return dev.minor;} - DWORD file_attributes () {return fileattr;} + DWORD get_devn () const {return dev.devn;} + short get_unitn () const {return dev.minor;} + DWORD file_attributes () const {return fileattr;} void file_attributes (DWORD new_attr) {fileattr = new_attr;} - DWORD drive_type () {return fs.drive_type ();} + DWORD drive_type () const {return fs.drive_type ();} DWORD fs_flags () {return fs.flags ();} - bool fs_has_ea () {return fs.has_ea ();} - bool fs_is_fat () {return fs.is_fat ();} + bool fs_has_ea () const {return fs.has_ea ();} + bool fs_is_fat () const {return fs.is_fat ();} + bool fs_is_ntfs () const {return fs.is_ntfs ();} + bool fs_is_samba () const {return fs.is_samba ();} + bool fs_is_nfs () const {return fs.is_nfs ();} void set_path (const char *p) {strcpy (path, p);} DWORD volser () { return fs.serial (); } void fillin (HANDLE h);