Alexandre Julliard : ntdll:
More workarounds for more kernel bugs in VFAT ioctl mapping on x86-64.
Alexandre Julliard
julliard at wine.codeweavers.com
Sat Oct 14 14:13:40 CDT 2006
Module: wine
Branch: master
Commit: 9719b45bb27a0599d3f8533646becdcb89d72952
URL: http://source.winehq.org/git/wine.git/?a=commit;h=9719b45bb27a0599d3f8533646becdcb89d72952
Author: Alexandre Julliard <julliard at winehq.org>
Date: Sat Oct 14 20:24:07 2006 +0200
ntdll: More workarounds for more kernel bugs in VFAT ioctl mapping on x86-64.
---
dlls/ntdll/directory.c | 105 +++++++++++++++++++++++++++++++++---------------
1 files changed, 73 insertions(+), 32 deletions(-)
diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c
index 145fc00..29541af 100644
--- a/dlls/ntdll/directory.c
+++ b/dlls/ntdll/directory.c
@@ -915,19 +915,61 @@ static FILE_BOTH_DIR_INFORMATION *append
}
+#ifdef VFAT_IOCTL_READDIR_BOTH
+
+/***********************************************************************
+ * start_vfat_ioctl
+ *
+ * Wrapper for the VFAT ioctl to work around various kernel bugs.
+ * dir_section must be held by caller.
+ */
+static KERNEL_DIRENT *start_vfat_ioctl( int fd )
+{
+ static KERNEL_DIRENT *de;
+ int res;
+
+ if (!de)
+ {
+ const size_t page_size = getpagesize();
+ SIZE_T size = 2 * sizeof(*de) + page_size;
+ void *addr = NULL;
+
+ if (NtAllocateVirtualMemory( GetCurrentProcess(), &addr, 1, &size, MEM_RESERVE, PAGE_READWRITE ))
+ return NULL;
+ /* commit only the size needed for the dir entries */
+ /* this leaves an extra unaccessible page, which should make the kernel */
+ /* fail with -EFAULT before it stomps all over our memory */
+ de = addr;
+ size = 2 * sizeof(*de);
+ NtAllocateVirtualMemory( GetCurrentProcess(), &addr, 1, &size, MEM_COMMIT, PAGE_READWRITE );
+ }
+
+ /* set d_reclen to 65535 to work around an AFS kernel bug */
+ de[0].d_reclen = 65535;
+ res = ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de );
+ if (res == -1)
+ {
+ if (errno != ENOENT) return NULL; /* VFAT ioctl probably not supported */
+ de[0].d_reclen = 0; /* eof */
+ }
+ else if (!res && de[0].d_reclen == 65535) return NULL; /* AFS bug */
+
+ return de;
+}
+
+
/***********************************************************************
* read_directory_vfat
*
* Read a directory using the VFAT ioctl; helper for NtQueryDirectoryFile.
*/
-#ifdef VFAT_IOCTL_READDIR_BOTH
static int read_directory_vfat( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
BOOLEAN single_entry, const UNICODE_STRING *mask,
BOOLEAN restart_scan )
{
- int res;
- KERNEL_DIRENT de[2];
+ size_t len;
+ KERNEL_DIRENT *de;
FILE_BOTH_DIR_INFORMATION *info, *last_info = NULL;
static const unsigned int max_dir_info_size = sizeof(*info) + (MAX_DIR_ENTRY_LEN-1) * sizeof(WCHAR);
@@ -939,18 +981,16 @@ static int read_directory_vfat( int fd,
{
off_t old_pos = lseek( fd, 0, SEEK_CUR );
- /* Set d_reclen to 65535 to work around an AFS kernel bug */
- de[0].d_reclen = 65535;
- res = ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de );
- if (res == -1 && errno != ENOENT) return -1; /* VFAT ioctl probably not supported */
- if (!res && de[0].d_reclen == 65535) return -1; /* AFS bug */
+ if (!(de = start_vfat_ioctl( fd ))) return -1; /* not supported */
- while (res != -1)
+ while (de[0].d_reclen)
{
- if (!de[0].d_reclen) break;
/* make sure names are null-terminated to work around an x86-64 kernel bug */
- if (de[0].d_reclen < sizeof(de[0].d_name)) de[0].d_name[de[0].d_reclen] = 0;
- if (de[1].d_reclen < sizeof(de[1].d_name)) de[1].d_name[de[1].d_reclen] = 0;
+ len = min(de[0].d_reclen, sizeof(de[0].d_name) - 1 );
+ de[0].d_name[len] = 0;
+ len = min(de[1].d_reclen, sizeof(de[1].d_name) - 1 );
+ de[1].d_name[len] = 0;
+
if (de[1].d_name[0])
info = append_entry( buffer, &io->Information, length,
de[1].d_name, de[0].d_name, mask );
@@ -968,23 +1008,21 @@ static int read_directory_vfat( int fd,
break;
}
old_pos = lseek( fd, 0, SEEK_CUR );
- res = ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de );
+ if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1) break;
}
}
else /* we'll only return full entries, no need to worry about overflow */
{
- /* Set d_reclen to 65535 to work around an AFS kernel bug */
- de[0].d_reclen = 65535;
- res = ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de );
- if (res == -1 && errno != ENOENT) return -1; /* VFAT ioctl probably not supported */
- if (!res && de[0].d_reclen == 65535) return -1; /* AFS bug */
+ if (!(de = start_vfat_ioctl( fd ))) return -1; /* not supported */
- while (res != -1)
+ while (de[0].d_reclen)
{
- if (!de[0].d_reclen) break;
/* make sure names are null-terminated to work around an x86-64 kernel bug */
- if (de[0].d_reclen < sizeof(de[0].d_name)) de[0].d_name[de[0].d_reclen] = 0;
- if (de[1].d_reclen < sizeof(de[1].d_name)) de[1].d_name[de[1].d_reclen] = 0;
+ len = min(de[0].d_reclen, sizeof(de[0].d_name) - 1 );
+ de[0].d_name[len] = 0;
+ len = min(de[1].d_reclen, sizeof(de[1].d_name) - 1 );
+ de[1].d_name[len] = 0;
+
if (de[1].d_name[0])
info = append_entry( buffer, &io->Information, length,
de[1].d_name, de[0].d_name, mask );
@@ -998,7 +1036,7 @@ static int read_directory_vfat( int fd,
/* check if we still have enough space for the largest possible entry */
if (io->Information + max_dir_info_size > length) break;
}
- res = ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de );
+ if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1) break;
}
}
@@ -1355,20 +1393,19 @@ #ifdef VFAT_IOCTL_READDIR_BOTH
int fd = open( unix_name, O_RDONLY | O_DIRECTORY );
if (fd != -1)
{
- KERNEL_DIRENT de[2];
+ KERNEL_DIRENT *de;
- /* Set d_reclen to 65535 to work around an AFS kernel bug */
- de[0].d_reclen = 65535;
- if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1 &&
- de[0].d_reclen != 65535)
+ RtlEnterCriticalSection( &dir_section );
+ if ((de = start_vfat_ioctl( fd )))
{
unix_name[pos - 1] = '/';
- for (;;)
+ while (de[0].d_reclen)
{
- if (!de[0].d_reclen) break;
/* make sure names are null-terminated to work around an x86-64 kernel bug */
- if (de[0].d_reclen < sizeof(de[0].d_name)) de[0].d_name[de[0].d_reclen] = 0;
- if (de[1].d_reclen < sizeof(de[1].d_name)) de[1].d_name[de[1].d_reclen] = 0;
+ size_t len = min(de[0].d_reclen, sizeof(de[0].d_name) - 1 );
+ de[0].d_name[len] = 0;
+ len = min(de[1].d_reclen, sizeof(de[1].d_name) - 1 );
+ de[1].d_name[len] = 0;
if (de[1].d_name[0])
{
@@ -1377,6 +1414,7 @@ #ifdef VFAT_IOCTL_READDIR_BOTH
if (ret == length && !memicmpW( buffer, name, length))
{
strcpy( unix_name + pos, de[1].d_name );
+ RtlLeaveCriticalSection( &dir_section );
close( fd );
return STATUS_SUCCESS;
}
@@ -1387,16 +1425,19 @@ #ifdef VFAT_IOCTL_READDIR_BOTH
{
strcpy( unix_name + pos,
de[1].d_name[0] ? de[1].d_name : de[0].d_name );
+ RtlLeaveCriticalSection( &dir_section );
close( fd );
return STATUS_SUCCESS;
}
if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1)
{
+ RtlLeaveCriticalSection( &dir_section );
close( fd );
goto not_found;
}
}
}
+ RtlLeaveCriticalSection( &dir_section );
close( fd );
}
/* fall through to normal handling */
More information about the wine-cvs
mailing list