Throughout, use FileBothDirectoryInformation info class rather than

FileDirectoryInformation info class to avoid problems with incomplete
	filesystem implementations.  Fix comments accordingly.
	* fhandler_disk_file.cc (fhandler_disk_file::readdir_helper): Set
	fname->Length to 0 in error case to avoid potential crash in debug
	output.
	(fhandler_disk_file::readdir): Try to speed up the working default case.
	Check for STATUS_INVALID_NETWORK_RESPONSE as potential status value
	returned by filesystems not implementing FileIdBothDirectoryInformation.
	* ntdll.h (STATUS_INVALID_NETWORK_RESPONSE): Define.
	(FILE_BOTH_DIRECTORY_INFORMATION): Rename to official name.
	* path.cc (symlink_info::check): Don't request FILE_READ_EA access, it's
	not required for NFS.  Try to speed up the working default case.  Check
	for STATUS_INVALID_NETWORK_RESPONSE as potential status value returned
	by filesystems not supporting non-NULL EA parameters.  Fix the way
	fs.update is called.  Improve debug output.
This commit is contained in:
Corinna Vinschen 2010-01-29 11:20:06 +00:00
parent 3d635c060e
commit 3432d6f1f7
4 changed files with 74 additions and 43 deletions

View File

@ -1,3 +1,22 @@
2010-01-29 Corinna Vinschen <corinna@vinschen.de>
Throughout, use FileBothDirectoryInformation info class rather than
FileDirectoryInformation info class to avoid problems with incomplete
filesystem implementations. Fix comments accordingly.
* fhandler_disk_file.cc (fhandler_disk_file::readdir_helper): Set
fname->Length to 0 in error case to avoid potential crash in debug
output.
(fhandler_disk_file::readdir): Try to speed up the working default case.
Check for STATUS_INVALID_NETWORK_RESPONSE as potential status value
returned by filesystems not implementing FileIdBothDirectoryInformation.
* ntdll.h (STATUS_INVALID_NETWORK_RESPONSE): Define.
(FILE_BOTH_DIRECTORY_INFORMATION): Rename to official name.
* path.cc (symlink_info::check): Don't request FILE_READ_EA access, it's
not required for NFS. Try to speed up the working default case. Check
for STATUS_INVALID_NETWORK_RESPONSE as potential status value returned
by filesystems not supporting non-NULL EA parameters. Fix the way
fs.update is called. Improve debug output.
2010-01-28 Corinna Vinschen <corinna@vinschen.de> 2010-01-28 Corinna Vinschen <corinna@vinschen.de>
* Makefile.in (tags, ctags, CTAGS)): Add rules to create tags file. * Makefile.in (tags, ctags, CTAGS)): Add rules to create tags file.

View File

@ -211,11 +211,11 @@ path_conv::ndisk_links (DWORD nNumberOfLinks)
unsigned count = 0; unsigned count = 0;
bool first = true; bool first = true;
PFILE_DIRECTORY_INFORMATION fdibuf = (PFILE_DIRECTORY_INFORMATION) PFILE_BOTH_DIRECTORY_INFORMATION fdibuf = (PFILE_BOTH_DIRECTORY_INFORMATION)
alloca (65536); alloca (65536);
__DIR_mounts *dir = new __DIR_mounts (normalized_path); __DIR_mounts *dir = new __DIR_mounts (normalized_path);
while (NT_SUCCESS (NtQueryDirectoryFile (fh, NULL, NULL, NULL, &io, fdibuf, while (NT_SUCCESS (NtQueryDirectoryFile (fh, NULL, NULL, NULL, &io, fdibuf,
65536, FileDirectoryInformation, 65536, FileBothDirectoryInformation,
FALSE, NULL, first))) FALSE, NULL, first)))
{ {
if (first) if (first)
@ -227,9 +227,9 @@ path_conv::ndisk_links (DWORD nNumberOfLinks)
if (fdibuf->FileNameLength != 2 || fdibuf->FileName[0] != L'.') if (fdibuf->FileNameLength != 2 || fdibuf->FileName[0] != L'.')
count = 2; count = 2;
} }
for (PFILE_DIRECTORY_INFORMATION pfdi = fdibuf; for (PFILE_BOTH_DIRECTORY_INFORMATION pfdi = fdibuf;
pfdi; pfdi;
pfdi = (PFILE_DIRECTORY_INFORMATION) pfdi = (PFILE_BOTH_DIRECTORY_INFORMATION)
(pfdi->NextEntryOffset ? (PBYTE) pfdi + pfdi->NextEntryOffset (pfdi->NextEntryOffset ? (PBYTE) pfdi + pfdi->NextEntryOffset
: NULL)) : NULL))
{ {
@ -434,7 +434,7 @@ fhandler_base::fstat_by_name (struct __stat64 *buf)
else if (NT_SUCCESS (status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, else if (NT_SUCCESS (status = NtQueryDirectoryFile (dir, NULL, NULL, NULL,
&io, &fdi_buf.fdi, &io, &fdi_buf.fdi,
sizeof fdi_buf, sizeof fdi_buf,
FileDirectoryInformation, FileBothDirectoryInformation,
TRUE, &basename, TRUE))) TRUE, &basename, TRUE)))
FileId.QuadPart = 0; /* get_ino is called in fstat_helper. */ FileId.QuadPart = 0; /* get_ino is called in fstat_helper. */
if (!NT_SUCCESS (status)) if (!NT_SUCCESS (status))
@ -1665,7 +1665,7 @@ fhandler_disk_file::opendir (int fd)
OS/FS combinations (say, Win2K/CDFS or so). Instead of OS/FS combinations (say, Win2K/CDFS or so). Instead of
testing in readdir for yet another error code, let's use testing in readdir for yet another error code, let's use
FileIdBothDirectoryInformation only on filesystems supporting FileIdBothDirectoryInformation only on filesystems supporting
persistent ACLs, FileDirectoryInformation otherwise. persistent ACLs, FileBothDirectoryInformation otherwise.
NFS clients hide dangling symlinks from directory queries, NFS clients hide dangling symlinks from directory queries,
unless you use the FileNamesInformation info class. unless you use the FileNamesInformation info class.
@ -1770,7 +1770,10 @@ fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
if ((de->d_ino = d_mounts (dir)->check_missing_mount (fname))) if ((de->d_ino = d_mounts (dir)->check_missing_mount (fname)))
added = true; added = true;
if (!added) if (!added)
{
fname->Length = 0;
return geterrno_from_win_error (w32_err); return geterrno_from_win_error (w32_err);
}
attr = 0; attr = 0;
dir->__flags &= ~dirent_set_d_ino; dir->__flags &= ~dirent_set_d_ino;
@ -1891,30 +1894,32 @@ fhandler_disk_file::readdir (DIR *dir, dirent *de)
which return STATUS_NOT_SUPPORTED rather than handling this info which return STATUS_NOT_SUPPORTED rather than handling this info
class. We just fall back to using a standard directory query in class. We just fall back to using a standard directory query in
this case and note this case using the dirent_get_d_ino flag. */ this case and note this case using the dirent_get_d_ino flag. */
if (status == STATUS_INVALID_LEVEL if (!NT_SUCCESS (status) && status != STATUS_NO_MORE_FILES
&& (status == STATUS_INVALID_LEVEL
|| status == STATUS_NOT_SUPPORTED || status == STATUS_NOT_SUPPORTED
|| status == STATUS_INVALID_PARAMETER || status == STATUS_INVALID_PARAMETER
|| status == STATUS_INVALID_INFO_CLASS) || status == STATUS_INVALID_NETWORK_RESPONSE
|| status == STATUS_INVALID_INFO_CLASS))
dir->__flags &= ~dirent_get_d_ino; dir->__flags &= ~dirent_get_d_ino;
/* Something weird happens on Samba up to version 3.0.21c, which is /* Something weird happens on Samba up to version 3.0.21c, which is
fixed in 3.0.22. FileIdBothDirectoryInformation seems to work fixed in 3.0.22. FileIdBothDirectoryInformation seems to work
nicely, but only up to the 128th entry in the directory. After nicely, but only up to the 128th entry in the directory. After
reaching this entry, the next call to NtQueryDirectoryFile reaching this entry, the next call to NtQueryDirectoryFile
(FileIdBothDirectoryInformation) returns STATUS_INVALID_LEVEL. (FileIdBothDirectoryInformation) returns STATUS_INVALID_LEVEL.
Why should we care, we can just switch to FileDirectoryInformation, Why should we care, we can just switch to
isn't it? Nope! The next call to FileBothDirectoryInformation, isn't it? Nope! The next call to
NtQueryDirectoryFile(FileDirectoryInformation) NtQueryDirectoryFile(FileBothDirectoryInformation) actually
actually returns STATUS_NO_MORE_FILES, regardless how many files returns STATUS_NO_MORE_FILES, regardless how many files are left
are left unread in the directory. This does not happen when using unread in the directory. This does not happen when using
FileDirectoryInformation right from the start, but since FileBothDirectoryInformation right from the start, but since
we can't decide whether the server we're talking with has this we can't decide whether the server we're talking with has this
bug or not, we end up serving Samba shares always in the slow bug or not, we end up serving Samba shares always in the slow
mode using FileDirectoryInformation. So, what we do here is mode using FileBothDirectoryInformation. So, what we do here is
to implement the solution suggested by Andrew Tridgell, we just to implement the solution suggested by Andrew Tridgell, we just
reread all entries up to dir->d_position using reread all entries up to dir->d_position using
FileDirectoryInformation. FileBothDirectoryInformation.
However, We do *not* mark this server as broken and fall back to However, We do *not* mark this server as broken and fall back to
using FileDirectoryInformation further on. This would slow using FileBothDirectoryInformation further on. This would slow
down every access to such a server, even for directories under down every access to such a server, even for directories under
128 entries. Also, bigger dirs only suffer from one additional 128 entries. Also, bigger dirs only suffer from one additional
call per full directory scan, which shouldn't be too big a hit. call per full directory scan, which shouldn't be too big a hit.
@ -1929,7 +1934,7 @@ fhandler_disk_file::readdir (DIR *dir, dirent *de)
status = NtQueryDirectoryFile (get_handle (), NULL, NULL, status = NtQueryDirectoryFile (get_handle (), NULL, NULL,
NULL, &io, d_cache (dir), NULL, &io, d_cache (dir),
DIR_BUF_SIZE, DIR_BUF_SIZE,
FileDirectoryInformation, FileBothDirectoryInformation,
FALSE, NULL, cnt == 0); FALSE, NULL, cnt == 0);
if (!NT_SUCCESS (status)) if (!NT_SUCCESS (status))
goto go_ahead; goto go_ahead;
@ -1949,7 +1954,7 @@ fhandler_disk_file::readdir (DIR *dir, dirent *de)
d_cache (dir), DIR_BUF_SIZE, d_cache (dir), DIR_BUF_SIZE,
(dir->__flags & dirent_nfs_d_ino) (dir->__flags & dirent_nfs_d_ino)
? FileNamesInformation ? FileNamesInformation
: FileDirectoryInformation, : FileBothDirectoryInformation,
FALSE, NULL, dir->__d_position == 0); FALSE, NULL, dir->__d_position == 0);
} }
@ -1982,9 +1987,11 @@ go_ahead:
} }
else else
{ {
FileName = ((PFILE_DIRECTORY_INFORMATION) buf)->FileName; FileName = ((PFILE_BOTH_DIRECTORY_INFORMATION) buf)->FileName;
FileNameLength = ((PFILE_DIRECTORY_INFORMATION) buf)->FileNameLength; FileNameLength =
FileAttributes = ((PFILE_DIRECTORY_INFORMATION) buf)->FileAttributes; ((PFILE_BOTH_DIRECTORY_INFORMATION) buf)->FileNameLength;
FileAttributes =
((PFILE_BOTH_DIRECTORY_INFORMATION) buf)->FileAttributes;
} }
RtlInitCountedUnicodeString (&fname, FileName, FileNameLength); RtlInitCountedUnicodeString (&fname, FileName, FileNameLength);
de->d_ino = d_mounts (dir)->check_mount (&fname, de->d_ino); de->d_ino = d_mounts (dir)->check_mount (&fname, de->d_ino);

View File

@ -1,7 +1,7 @@
/* ntdll.h. Contains ntdll specific stuff not defined elsewhere. /* ntdll.h. Contains ntdll specific stuff not defined elsewhere.
Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
2009 Red Hat, Inc. 2009, 2010 Red Hat, Inc.
This file is part of Cygwin. This file is part of Cygwin.
@ -39,6 +39,7 @@
#define STATUS_DISK_FULL ((NTSTATUS) 0xc000007f) #define STATUS_DISK_FULL ((NTSTATUS) 0xc000007f)
#define STATUS_WORKING_SET_QUOTA ((NTSTATUS) 0xc00000a1) #define STATUS_WORKING_SET_QUOTA ((NTSTATUS) 0xc00000a1)
#define STATUS_NOT_SUPPORTED ((NTSTATUS) 0xc00000bb) #define STATUS_NOT_SUPPORTED ((NTSTATUS) 0xc00000bb)
#define STATUS_INVALID_NETWORK_RESPONSE ((NTSTATUS) 0xc00000c3)
#define STATUS_DIRECTORY_NOT_EMPTY ((NTSTATUS) 0xc0000101) #define STATUS_DIRECTORY_NOT_EMPTY ((NTSTATUS) 0xc0000101)
#define STATUS_CANNOT_DELETE ((NTSTATUS) 0xc0000121) #define STATUS_CANNOT_DELETE ((NTSTATUS) 0xc0000121)
#define STATUS_INVALID_LEVEL ((NTSTATUS) 0xc0000148) #define STATUS_INVALID_LEVEL ((NTSTATUS) 0xc0000148)
@ -153,7 +154,7 @@ typedef struct _FILE_DIRECTORY_INFORMATION {
WCHAR FileName[1]; WCHAR FileName[1];
} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION; } FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION;
typedef struct _FILE_BOTH_DIR_INFORMATION typedef struct _FILE_BOTH_DIRECTORY_INFORMATION
{ {
ULONG NextEntryOffset; ULONG NextEntryOffset;
ULONG FileIndex; ULONG FileIndex;
@ -169,7 +170,7 @@ typedef struct _FILE_BOTH_DIR_INFORMATION
CCHAR ShortNameLength; CCHAR ShortNameLength;
WCHAR ShortName[12]; WCHAR ShortName[12];
WCHAR FileName[1]; WCHAR FileName[1];
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION; } FILE_BOTH_DIRECTORY_INFORMATION, *PFILE_BOTH_DIRECTORY_INFORMATION;
typedef struct _FILE_ID_BOTH_DIR_INFORMATION typedef struct _FILE_ID_BOTH_DIR_INFORMATION
{ {

View File

@ -514,7 +514,7 @@ getfileattr (const char *path, bool caseinsensitive) /* path has to be always ab
directory query. */ directory query. */
UNICODE_STRING dirname, basename; UNICODE_STRING dirname, basename;
HANDLE dir; HANDLE dir;
FILE_DIRECTORY_INFORMATION fdi; FILE_BOTH_DIRECTORY_INFORMATION fdi;
RtlSplitUnicodePath (&upath, &dirname, &basename); RtlSplitUnicodePath (&upath, &dirname, &basename);
InitializeObjectAttributes (&attr, &dirname, InitializeObjectAttributes (&attr, &dirname,
@ -529,7 +529,7 @@ getfileattr (const char *path, bool caseinsensitive) /* path has to be always ab
{ {
status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io, status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io,
&fdi, sizeof fdi, &fdi, sizeof fdi,
FileDirectoryInformation, FileBothDirectoryInformation,
TRUE, &basename, TRUE); TRUE, &basename, TRUE);
NtClose (dir); NtClose (dir);
if (NT_SUCCESS (status) || status == STATUS_BUFFER_OVERFLOW) if (NT_SUCCESS (status) || status == STATUS_BUFFER_OVERFLOW)
@ -2209,18 +2209,22 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt,
Fortunately it's ignored on most other file systems so we don't have Fortunately it's ignored on most other file systems so we don't have
to special case NFS too much. */ to special case NFS too much. */
status = NtCreateFile (&h, status = NtCreateFile (&h,
READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA, READ_CONTROL | FILE_READ_ATTRIBUTES,
&attr, &io, NULL, 0, FILE_SHARE_VALID_FLAGS, &attr, &io, NULL, 0, FILE_SHARE_VALID_FLAGS,
FILE_OPEN, FILE_OPEN,
FILE_OPEN_REPARSE_POINT FILE_OPEN_REPARSE_POINT
| FILE_OPEN_FOR_BACKUP_INTENT, | FILE_OPEN_FOR_BACKUP_INTENT,
eabuf, easize); eabuf, easize);
debug_printf ("%p = NtCreateFile (%S)", status, &upath);
/* No right to access EAs or EAs not supported? */ /* No right to access EAs or EAs not supported? */
if (status == STATUS_ACCESS_DENIED || status == STATUS_EAS_NOT_SUPPORTED if (!NT_SUCCESS (status)
&& (status == STATUS_ACCESS_DENIED
|| status == STATUS_EAS_NOT_SUPPORTED
|| status == STATUS_NOT_SUPPORTED || status == STATUS_NOT_SUPPORTED
/* Or a bug in Samba 3.2.x (x <= 7) when accessing a share's root dir || status == STATUS_INVALID_NETWORK_RESPONSE
which has EAs enabled? */ /* Or a bug in Samba 3.2.x (x <= 7) when accessing a share's
|| status == STATUS_INVALID_PARAMETER) root dir which has EAs enabled? */
|| status == STATUS_INVALID_PARAMETER))
{ {
no_ea = true; no_ea = true;
/* If EAs are not supported, there's no sense to check them again /* If EAs are not supported, there's no sense to check them again
@ -2235,6 +2239,7 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt,
&attr, &io, FILE_SHARE_VALID_FLAGS, &attr, &io, FILE_SHARE_VALID_FLAGS,
FILE_OPEN_REPARSE_POINT FILE_OPEN_REPARSE_POINT
| FILE_OPEN_FOR_BACKUP_INTENT); | FILE_OPEN_FOR_BACKUP_INTENT);
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 && ci_flag == 0
&& wincap.has_broken_udf ()) && wincap.has_broken_udf ())
@ -2247,6 +2252,7 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt,
&attr, &io, FILE_SHARE_VALID_FLAGS, &attr, &io, FILE_SHARE_VALID_FLAGS,
FILE_OPEN_REPARSE_POINT FILE_OPEN_REPARSE_POINT
| FILE_OPEN_FOR_BACKUP_INTENT); | FILE_OPEN_FOR_BACKUP_INTENT);
debug_printf ("%p = NtOpenFile (broken-UDF, %S)", status, &upath);
attr.Attributes = 0; attr.Attributes = 0;
if (NT_SUCCESS (status)) if (NT_SUCCESS (status))
{ {
@ -2261,12 +2267,10 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt,
} }
} }
if (NT_SUCCESS (status)
/* Check file system while we're having the file open anyway. /* Check file system while we're having the file open anyway.
This speeds up path_conv noticably (~10%). */ This speeds up path_conv noticably (~10%). */
if (!fs_update_called) && (fs_update_called || fs.update (&upath, h))
fs.update (&upath, h);
if (NT_SUCCESS (status)
&& NT_SUCCESS (status = fs.has_buggy_basic_info () && NT_SUCCESS (status = fs.has_buggy_basic_info ()
? NtQueryAttributesFile (&attr, &fbi) ? NtQueryAttributesFile (&attr, &fbi)
: NtQueryInformationFile (h, &io, &fbi, sizeof fbi, : NtQueryInformationFile (h, &io, &fbi, sizeof fbi,
@ -2306,7 +2310,7 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt,
OBJECT_ATTRIBUTES dattr; OBJECT_ATTRIBUTES dattr;
HANDLE dir; HANDLE dir;
struct { struct {
FILE_DIRECTORY_INFORMATION fdi; FILE_BOTH_DIRECTORY_INFORMATION fdi;
WCHAR dummy_buf[NAME_MAX + 1]; WCHAR dummy_buf[NAME_MAX + 1];
} fdi_buf; } fdi_buf;
@ -2332,7 +2336,7 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt,
{ {
status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io, status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
&fdi_buf, sizeof fdi_buf, &fdi_buf, sizeof fdi_buf,
FileDirectoryInformation, FileBothDirectoryInformation,
TRUE, &basename, TRUE); TRUE, &basename, TRUE);
/* Take the opportunity to check file system while we're /* Take the opportunity to check file system while we're
having the handle to the parent dir. */ having the handle to the parent dir. */