[PATCH 2/2] ntdll: Check for case-insensitive volumes. (try 7)

Charles Davis cdavis at mymail.mines.edu
Wed Dec 22 19:12:44 CST 2010


Changes since try 6:
o getattrlist() code path pulled into its own function.
o getattrlist() path restructured in case getattrlist() fails.
o Call statfs() again on the Linux path.
o Gratuitous whitespace changes gone.
---
 dlls/ntdll/directory.c |  249 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 248 insertions(+), 1 deletions(-)

diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c
index 865c2fa..28580cf 100644
--- a/dlls/ntdll/directory.c
+++ b/dlls/ntdll/directory.c
@@ -41,6 +41,9 @@
 #ifdef HAVE_SYS_STAT_H
 # include <sys/stat.h>
 #endif
+#ifdef HAVE_SYS_ATTR_H
+#include <sys/attr.h>
+#endif
 #ifdef HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
 #endif
@@ -56,6 +59,9 @@
 #ifdef HAVE_SYS_MOUNT_H
 #include <sys/mount.h>
 #endif
+#ifdef HAVE_SYS_STATFS_H
+#include <sys/statfs.h>
+#endif
 #include <time.h>
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
@@ -130,6 +136,35 @@ static inline int getdents64( int fd, char *de, unsigned int size )
 
 #endif  /* linux */
 
+#if defined(HAVE_GETATTRLIST)
+
+struct get_fsid
+{
+    ULONG size;
+    dev_t dev;
+    fsid_t fsid;
+};
+
+struct fs_cache
+{
+    dev_t dev;
+    fsid_t fsid;
+    BOOLEAN case_sensitive;
+} fs_cache[64];
+
+#if defined(ATTR_VOL_CAPABILITIES) && defined(VOL_CAPABILITIES_FORMAT) && \
+    defined(VOL_CAP_FMT_CASE_SENSITIVE)
+
+struct vol_caps
+{
+    ULONG size;
+    vol_capabilities_attr_t caps;
+};
+
+#endif /* ATTR_VOL_CAPABILITIES */
+
+#endif /* HAVE_GETATTRLIST */
+
 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
 #define IS_SEPARATOR(ch)   ((ch) == '\\' || (ch) == '/')
 
@@ -783,6 +818,209 @@ static char *get_device_mount_point( dev_t dev )
 }
 
 
+
+#ifdef HAVE_GETATTRLIST
+/***********************************************************************
+ *           look_up_fs_cache
+ *
+ * Checks if the specified file system is in the cache.
+ */
+static struct fs_cache *look_up_fs_cache( dev_t dev )
+{
+    int i;
+    for (i = 0; i < sizeof(fs_cache)/sizeof(fs_cache[0]); i++)
+        if (fs_cache[i].dev == dev)
+            return fs_cache+i;
+    return NULL;
+}
+
+/***********************************************************************
+ *           add_fs_cache
+ *
+ * Adds the specified file system to the cache.
+ */
+static void add_fs_cache( dev_t dev, fsid_t fsid, BOOLEAN case_sensitive )
+{
+    int i;
+    struct fs_cache *entry = look_up_fs_cache( dev );
+    static int once = 0;
+    if (entry)
+    {
+        /* Update the cache */
+        entry->fsid = fsid;
+        entry->case_sensitive = case_sensitive;
+        return;
+    }
+
+    /* Add a new entry */
+    for (i = 0; i < sizeof(fs_cache)/sizeof(fs_cache[0]); i++)
+        if (fs_cache[i].dev == 0)
+        {
+            /* This entry is empty, use it */
+            fs_cache[i].dev = dev;
+            fs_cache[i].fsid = fsid;
+            fs_cache[i].case_sensitive = case_sensitive;
+            return;
+        }
+
+    /* Cache is out of space, warn */
+    if (once++)
+        WARN( "FS cache is out of space, expect performance problems\n" );
+}
+#endif
+
+#if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
+    defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
+/***********************************************************************
+ *           get_dir_case_sensitivity_attr
+ *
+ * Checks if the volume containing the specified directory is case
+ * sensitive or not. Uses getattrlist(2).
+ */
+static int get_dir_case_sensitivity_attr( const char *dir )
+{
+    char *mntpoint = NULL;
+    struct attrlist attr;
+    struct vol_caps caps;
+    struct get_fsid get_fsid;
+    struct fs_cache *entry;
+
+    /* First get the FS ID of the volume */
+    attr.bitmapcount = ATTR_BIT_MAP_COUNT;
+    attr.reserved = 0;
+    attr.commonattr = ATTR_CMN_DEVID|ATTR_CMN_FSID;
+    attr.volattr = attr.dirattr = attr.fileattr = attr.forkattr = 0;
+    get_fsid.size = 0;
+    if (getattrlist( dir, &attr, &get_fsid, sizeof(get_fsid), 0 ) != 0 ||
+        get_fsid.size != sizeof(get_fsid))
+        return -1;
+    /* Try to look it up in the cache */
+    entry = look_up_fs_cache( get_fsid.dev );
+    if (entry && !memcmp( &entry->fsid, &get_fsid.fsid, sizeof(fsid_t) ))
+        /* Cache lookup succeeded */
+        return entry->case_sensitive;
+    /* Cache is stale at this point, we have to update it */
+
+    mntpoint = get_device_mount_point( get_fsid.dev );
+    /* Now look up the case-sensitivity */
+    attr.commonattr = 0;
+    attr.volattr = ATTR_VOL_INFO|ATTR_VOL_CAPABILITIES;
+    if (getattrlist( mntpoint, &attr, &caps, sizeof(caps), 0 ) < 0)
+    {
+        RtlFreeHeap( GetProcessHeap(), 0, mntpoint );
+        add_fs_cache( get_fsid.dev, get_fsid.fsid, TRUE );
+        return TRUE;
+    }
+    RtlFreeHeap( GetProcessHeap(), 0, mntpoint );
+    if (caps.size == sizeof(caps) &&
+        (caps.caps.valid[VOL_CAPABILITIES_FORMAT] &
+         (VOL_CAP_FMT_CASE_SENSITIVE | VOL_CAP_FMT_CASE_PRESERVING)) ==
+        (VOL_CAP_FMT_CASE_SENSITIVE | VOL_CAP_FMT_CASE_PRESERVING))
+    {
+        BOOLEAN ret;
+
+        if ((caps.caps.capabilities[VOL_CAPABILITIES_FORMAT] &
+            VOL_CAP_FMT_CASE_SENSITIVE) != VOL_CAP_FMT_CASE_SENSITIVE)
+            ret = FALSE;
+        else
+            ret = TRUE;
+        /* Update the cache */
+        add_fs_cache( get_fsid.dev, get_fsid.fsid, ret );
+        return ret;
+    }
+    return FALSE;
+}
+#endif
+
+/***********************************************************************
+ *           get_dir_case_sensitivity_stat
+ *
+ * Checks if the volume containing the specified directory is case
+ * sensitive or not. Uses statfs(2) or statvfs(2).
+ */
+static BOOLEAN get_dir_case_sensitivity_stat( const char *dir )
+{
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+    struct statfs stfs;
+#elif defined(__NetBSD__)
+    struct statvfs stfs;
+#elif defined(__linux__)
+    struct statfs stfs;
+    struct stat st;
+    char *cifile;
+#endif
+
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+    statfs( dir, &stfs );
+    /* Assume these file systems are always case insensitive on Mac OS.
+     * For FreeBSD, only assume CIOPFS is case insensitive (AFAIK, Mac OS
+     * is the only UNIX that supports case-insensitive lookup).
+     */
+    if (!strcmp( stfs.f_fstypename, "fusefs" ) &&
+        !strncmp( stfs.f_mntfromname, "ciopfs", 5 ))
+        return FALSE;
+#ifdef __APPLE__
+    if (!strcmp( stfs.f_fstypename, "msdos" ) ||
+        !strcmp( stfs.f_fstypename, "cd9660" ) ||
+        !strcmp( stfs.f_fstypename, "udf" ) ||
+        !strcmp( stfs.f_fstypename, "ntfs" ) ||
+        !strcmp( stfs.f_fstypename, "smbfs" ))
+        return FALSE;
+#ifdef _DARWIN_FEATURE_64_BIT_INODE
+     if (!strcmp( stfs.f_fstypename, "hfs" ) && (stfs.f_fssubtype == 0 ||
+                                                 stfs.f_fssubtype == 1 ||
+                                                 stfs.f_fssubtype == 128))
+        return FALSE;
+#else
+     /* The field says "reserved", but a quick look at the kernel source
+      * tells us that this "reserved" field is really the same as the
+      * "fssubtype" field from the inode64 structure (see munge_statfs()
+      * in <xnu-source>/bsd/vfs/vfs_syscalls.c).
+      */
+     if (!strcmp( stfs.f_fstypename, "hfs" ) && (stfs.f_reserved1 == 0 ||
+                                                 stfs.f_reserved1 == 1 ||
+                                                 stfs.f_reserved1 == 128))
+        return FALSE;
+#endif
+#endif
+    return TRUE;
+#elif defined(__NetBSD__)
+    statvfs( dir, &stfs );
+    /* Only assume CIOPFS is case insensitive. */
+    if (strcmp( stfs.f_fstypename, "fusefs" ) ||
+        strncmp( stfs.f_mntfromname, "ciopfs", 5 ))
+        return TRUE;
+    return FALSE;
+#elif defined(__linux__)
+    /* Only assume CIOPFS is case insensitive. */
+    statfs( dir, &stfs );
+    if (stfs.f_type != 0x65735546 /* FUSE_SUPER_MAGIC */)
+        return TRUE;
+    /* Normally, we'd have to parse the mtab to find out exactly what
+     * kind of FUSE FS this is. But, someone on wine-devel suggested
+     * a shortcut. We'll stat a special file in the directory. If it's
+     * there, we'll assume it's a CIOPFS, else not.
+     * This will break if somebody puts a file named ".ciopfs" in a non-
+     * CIOPFS directory.
+     */
+    cifile = RtlAllocateHeap( GetProcessHeap(), 0, strlen( dir )+sizeof("/.ciopfs") );
+    if (!cifile) return TRUE;
+    strcpy( cifile, dir );
+    strcat( cifile, "/.ciopfs" );
+    if (stat( cifile, &st ) == 0)
+    {
+        RtlFreeHeap( GetProcessHeap(), 0, cifile );
+        return FALSE;
+    }
+    RtlFreeHeap( GetProcessHeap(), 0, cifile );
+    return TRUE;
+#else
+    FIXME("is not implemented for this platform!\n");
+    return TRUE;
+#endif
+}
+
+
 /***********************************************************************
  *           init_options
  *
@@ -1816,7 +2054,7 @@ static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, i
     DIR *dir;
     struct dirent *de;
     struct stat st;
-    int ret, used_default, is_name_8_dot_3;
+    int ret, used_default, is_name_8_dot_3, case_sensitive;
 
     /* try a shortcut for this directory */
 
@@ -1839,6 +2077,13 @@ static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, i
     if (pos > 1) unix_name[pos - 1] = 0;
     else unix_name[1] = 0;  /* keep the initial slash */
 
+#if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
+    defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
+    case_sensitive = get_dir_case_sensitivity_attr( unix_name );
+    if (case_sensitive == -1)
+#endif
+        case_sensitive = get_dir_case_sensitivity_stat( unix_name );
+
     /* check if it fits in 8.3 so that we don't look for short names if we won't need them */
 
     str.Buffer = (WCHAR *)name;
@@ -1846,6 +2091,8 @@ static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, i
     str.MaximumLength = str.Length;
     is_name_8_dot_3 = RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) && !spaces;
 
+    if (!case_sensitive && !is_name_8_dot_3) goto not_found;
+
     /* now look for it through the directory */
 
 #ifdef VFAT_IOCTL_READDIR_BOTH
-- 
1.7.3.4




More information about the wine-patches mailing list