ntdll / kernel32: #45

Eric Pouech pouech-eric at wanadoo.fr
Wed Feb 4 14:55:29 CST 2004


Moved NT => unix path translation to ntdll
- moved path transformation (according to new scheme) into ntdll
- rewrote wine_get_unix_path_name accordingly (and exported it from
   ntdll)
- rewrote RtlDoesFileExists_U to use these new features
-------------- next part --------------
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/kernel44/kernel32.spec dlls/kernel/kernel32.spec
--- dlls/kernel44/kernel32.spec	2004-02-01 15:09:27.000000000 +0100
+++ dlls/kernel/kernel32.spec	2004-02-01 16:02:01.000000000 +0100
@@ -1146,7 +1146,7 @@
 @ varargs __wine_call_from_16_regs()
 
 # Unix files
-@ stdcall wine_get_unix_file_name(wstr ptr long)
+@ stdcall wine_get_unix_file_name(wstr ptr long) ntdll.wine_get_unix_file_name
 
 # Init code
 @ cdecl __wine_kernel_init()
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/ntdll44/dos_fs.c dlls/ntdll/dos_fs.c
--- dlls/ntdll44/dos_fs.c	1970-01-01 01:00:00.000000000 +0100
+++ dlls/ntdll/dos_fs.c	2004-02-01 16:56:18.000000000 +0100
@@ -0,0 +1,843 @@
+/*
+ * DOS file system functions
+ *
+ * Copyright 1993 Erik Bos
+ * Copyright 1996 Alexandre Julliard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+#include "wine/port.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_ERRNO_H
+#include <sys/errno.h>
+#endif
+
+#include <dirent.h>
+#ifdef HAVE_SYS_IOCTL_H 
+#include <sys/ioctl.h>
+#endif
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+#include "wine/unicode.h"
+#include "wine/debug.h"
+#include "wine/server.h"
+#include "wine/library.h"
+#include "async.h"
+#include "ntdll_misc.h"
+
+#include "winternl.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(dos_fs);
+
+/* Chars we don't want to see in DOS file names */
+#define INVALID_DOS_CHARS  "*?<>|\"+=,;[] \345"
+
+#define IS_END_OF_NAME(ch)  (!(ch) || ((ch) == '/') || ((ch) == '\\'))
+
+#define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
+
+/***********************************************************************
+ *           DOSFS_ValidDOSName
+ *
+ * Return 1 if Unix file 'name' is also a valid MS-DOS name
+ * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
+ * File name can be terminated by '\0', '\\' or '/'.
+ */
+static int DOSFS_ValidDOSName(LPCWSTR name)
+{
+    static const char invalid_chars[] = INVALID_DOS_CHARS;
+    const WCHAR *p = name;
+    const char *invalid = invalid_chars;
+    int len = 0;
+
+    if (*p == '.')
+    {
+        /* Check for "." and ".." */
+        p++;
+        if (*p == '.') p++;
+        /* All other names beginning with '.' are invalid */
+        return (IS_END_OF_NAME(*p));
+    }
+    while (!IS_END_OF_NAME(*p))
+    {
+        if (*p < 256 && strchr(invalid, (char)*p)) return 0;  /* Invalid char */
+        if (*p == '.') break;  /* Start of the extension */
+        if (++len > 8) return 0;  /* Name too long */
+        p++;
+    }
+    if (*p != '.') return 1;  /* End of name */
+    p++;
+    if (IS_END_OF_NAME(*p)) return 0;  /* Empty extension not allowed */
+    len = 0;
+    while (!IS_END_OF_NAME(*p))
+    {
+        if (*p < 256 && strchr(invalid, (char)*p)) return 0;  /* Invalid char */
+        if (*p == '.') return 0;  /* Second extension not allowed */
+        if (++len > 3) return 0;  /* Extension too long */
+        p++;
+    }
+    return 1;
+}
+
+/***********************************************************************
+ *           DOSFS_ToDosFCBFormat
+ *
+ * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
+ * expanding wild cards and converting to upper-case in the process.
+ * File name can be terminated by '\0', '\\' or '/'.
+ * Return FALSE if the name is not a valid DOS name.
+ * 'buffer' must be at least 12 characters long.
+ */
+static BOOL DOSFS_ToDosFCBFormat(LPCWSTR name, LPWSTR buffer)
+{
+    static const char invalid_chars[] = INVALID_DOS_CHARS;
+    LPCWSTR p = name;
+    int i;
+
+    /* Check for "." and ".." */
+    if (*p == '.')
+    {
+        p++;
+        buffer[0] = '.';
+        for(i = 1; i < 11; i++) buffer[i] = ' ';
+        buffer[11] = 0;
+        if (*p == '.')
+        {
+            buffer[1] = '.';
+            p++;
+        }
+        return IS_END_OF_NAME(*p);
+    }
+
+    for (i = 0; i < 8; i++)
+    {
+        switch (*p)
+        {
+        case '\0':
+        case '\\':
+        case '/':
+        case '.':
+            buffer[i] = ' ';
+            break;
+        case '?':
+            p++;
+            /* fall through */
+        case '*':
+            buffer[i] = '?';
+            break;
+        default:
+            if (*p < 256 && strchr(invalid_chars, (char)*p)) return FALSE;
+            buffer[i] = toupperW(*p);
+            p++;
+            break;
+        }
+    }
+
+    if (*p == '*')
+    {
+        /* Skip all chars after wildcard up to first dot */
+        while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
+    }
+    else
+    {
+        /* Check if name too long */
+        if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
+    }
+    if (*p == '.') p++;  /* Skip dot */
+
+    for (i = 8; i < 11; i++)
+    {
+        switch(*p)
+        {
+        case '\0':
+        case '\\':
+        case '/':
+            buffer[i] = ' ';
+            break;
+        case '.':
+            return FALSE;  /* Second extension not allowed */
+        case '?':
+            p++;
+            /* fall through */
+        case '*':
+            buffer[i] = '?';
+            break;
+        default:
+            if (*p < 256 && strchr(invalid_chars, (char)*p)) return FALSE;
+            buffer[i] = toupperW(*p);
+            p++;
+            break;
+        }
+    }
+    buffer[11] = '\0';
+
+    /* at most 3 character of the extension are processed
+     * is something behind this ?
+     */
+    while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
+    return IS_END_OF_NAME(*p);
+}
+
+/***********************************************************************
+ *           DOSFS_Hash
+ *
+ * Transform a Unix file name into a hashed DOS name. If the name is a valid
+ * DOS name, it is converted to upper-case; otherwise it is replaced by a
+ * hashed version that fits in 8.3 format.
+ * File name can be terminated by '\0', '\\' or '/'.
+ * 'buffer' must be at least 13 characters long.
+ */
+static void DOSFS_Hash(LPCWSTR name, LPWSTR buffer, BOOL dir_format)
+{
+    static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
+    static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
+
+    LPCWSTR p, ext;
+    LPWSTR dst;
+    unsigned short hash;
+    int i;
+
+    if (dir_format)
+    {
+        for(i = 0; i < 11; i++) buffer[i] = ' ';
+        buffer[11] = 0;
+    }
+
+    if (DOSFS_ValidDOSName(name))
+    {
+        /* Check for '.' and '..' */
+        if (*name == '.')
+        {
+            buffer[0] = '.';
+            if (!dir_format) buffer[1] = buffer[2] = '\0';
+            if (name[1] == '.') buffer[1] = '.';
+            return;
+        }
+
+        /* Simply copy the name, converting to uppercase */
+
+        for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
+            *dst++ = toupperW(*name);
+        if (*name == '.')
+        {
+            if (dir_format) dst = buffer + 8;
+            else *dst++ = '.';
+            for (name++; !IS_END_OF_NAME(*name); name++)
+                *dst++ = toupperW(*name);
+        }
+        if (!dir_format) *dst = '\0';
+        return;
+    }
+
+    /* Compute the hash code of the file name */
+    /* If you know something about hash functions, feel free to */
+    /* insert a better algorithm here... */
+    for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
+        hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
+    hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
+
+    /* Find last dot for start of the extension */
+    for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
+        if (*p == '.') ext = p;
+    if (ext && IS_END_OF_NAME(ext[1]))
+        ext = NULL;  /* Empty extension ignored */
+
+    /* Copy first 4 chars, replacing invalid chars with '_' */
+    for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
+    {
+        if (IS_END_OF_NAME(*p) || (p == ext)) break;
+        *dst++ = (*p < 256 && strchr(invalid_chars, (char)*p)) ? '_' : toupperW(*p);
+    }
+    /* Pad to 5 chars with '~' */
+    while (i-- >= 0) *dst++ = '~';
+
+    /* Insert hash code converted to 3 ASCII chars */
+    *dst++ = hash_chars[(hash >> 10) & 0x1f];
+    *dst++ = hash_chars[(hash >> 5) & 0x1f];
+    *dst++ = hash_chars[hash & 0x1f];
+
+    /* Copy the first 3 chars of the extension (if any) */
+    if (ext)
+    {
+        if (!dir_format) *dst++ = '.';
+        for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
+            *dst++ = (*ext < 256 && strchr(invalid_chars, (char)*ext)) ? '_' : toupperW(*ext);
+    }
+    if (!dir_format) *dst = '\0';
+}
+
+/* Define the VFAT ioctl to get both short and long file names */
+/* FIXME: is it possible to get this to work on other systems? */
+#ifdef linux
+/* We want the real kernel dirent structure, not the libc one */
+typedef struct
+{
+    long d_ino;
+    long d_off;
+    unsigned short d_reclen;
+    char d_name[256];
+} KERNEL_DIRENT;
+
+#define VFAT_IOCTL_READDIR_BOTH  _IOR('r', 1, KERNEL_DIRENT [2])
+
+/* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
+#ifndef O_DIRECTORY
+# define O_DIRECTORY    0200000	/* must be directory */
+#endif
+
+#else   /* linux */
+# undef VFAT_IOCTL_READDIR_BOTH  /* just in case... */
+# ifndef O_DIRECTORY
+#  define O_DIRECTORY   0
+# endif
+#endif  /* linux */
+
+/*
+ * Directory info for DOSFS_ReadDir
+ * contains the names of *all* the files in the directory
+ */
+typedef struct
+{
+    int         used;
+    int         size;
+    WCHAR       names[1];
+} DOS_DIR;
+
+/***********************************************************************
+ *           DOSFS_AddDirEntry
+ *
+ *  Used to construct an array of filenames in DOSFS_OpenDir
+ */
+static NTSTATUS DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
+{
+    int extra1 = strlenW(name) + 1;
+    int extra2 = dosname ? (strlenW(dosname) + 1) : 1;
+
+    /* if we need more, at minimum double the size */
+    if ((extra1 + extra2 + (*dir)->used) > (*dir)->size)
+    {
+        int more = (*dir)->size;
+        DOS_DIR *t;
+
+        if (more < (extra1 + extra2)) more = extra1 + extra2;
+
+        t = RtlReAllocateHeap(ntdll_get_process_heap(), 0, *dir, 
+                              sizeof(**dir) + ((*dir)->size + more) * sizeof(WCHAR));
+        if (!t)
+        {
+            ERR("Out of memory caching directory structure %d %d %d\n",
+                 (*dir)->size, more, (*dir)->used);
+            return STATUS_NO_MEMORY;
+        }
+        (*dir) = t;
+        (*dir)->size += more;
+    }
+
+    /* at this point, the dir structure is big enough to hold these names */
+    strcpyW(&(*dir)->names[(*dir)->used], name);
+    (*dir)->used += extra1;
+    if (dosname)
+        strcpyW(&(*dir)->names[(*dir)->used], dosname);
+    else
+        (*dir)->names[(*dir)->used] = '\0';
+    (*dir)->used += extra2;
+
+    return STATUS_SUCCESS;
+}
+
+
+/***********************************************************************
+ *           DOSFS_OpenDir_VFAT
+ */
+static NTSTATUS DOSFS_OpenDir_VFAT(int fd, DOS_DIR **dir)
+{
+#ifdef VFAT_IOCTL_READDIR_BOTH
+    KERNEL_DIRENT       de[2];
+    NTSTATUS            status = STATUS_SUCCESS;
+
+    /* Check if the VFAT ioctl is supported on this directory */
+
+    do
+    {
+        WCHAR long_name[MAX_PATH];
+        WCHAR short_name[12];
+
+        if (ioctl(fd, VFAT_IOCTL_READDIR_BOTH, (long)de) == -1)
+        {
+            status = STATUS_NOT_SUPPORTED;
+            break;
+        }
+        if (!de[0].d_reclen) break;
+        ntdll_umbstowcs(0, de[0].d_name, strlen(de[0].d_name) + 1,
+                       long_name, MAX_PATH);
+        if (!DOSFS_ToDosFCBFormat(long_name, short_name))
+            short_name[0] = '\0';
+        if (de[1].d_name[0])
+            ntdll_umbstowcs(0, de[1].d_name, strlen(de[1].d_name) + 1,
+                           long_name, MAX_PATH);
+        else
+            ntdll_umbstowcs(0, de[0].d_name, strlen(de[0].d_name) + 1,
+                           long_name, MAX_PATH);
+        status = DOSFS_AddDirEntry(dir, long_name, short_name);
+    } while (status == STATUS_SUCCESS);
+
+    return status;
+#else
+    return STATUS_NOT_SUPPORTED;
+#endif  /* VFAT_IOCTL_READDIR_BOTH */
+}
+
+
+/***********************************************************************
+ *           DOSFS_OpenDir_Normal
+ *
+ * Now use the non standard low level interfaces
+ */
+static NTSTATUS DOSFS_OpenDir_Normal(int fd, DOS_DIR **dir)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    DIR *unixdir;
+    int cd = open(".", O_RDONLY | O_DIRECTORY);
+
+    if (cd == -1 || fchdir(fd) == -1 || !(unixdir = opendir( "." )))
+    {
+        if (cd != -1) fchdir(cd);
+        return FILE_GetNtStatus();
+    }
+
+    while (status == STATUS_SUCCESS)
+    {
+        WCHAR long_name[MAX_PATH];
+        struct dirent *de = readdir(unixdir);
+
+        if (!de) break;
+        ntdll_umbstowcs(0, de->d_name, strlen(de->d_name) + 1,
+                        long_name, MAX_PATH);
+        status = DOSFS_AddDirEntry(dir, long_name, NULL);
+    }
+    closedir(unixdir);
+    fchdir(cd);
+    close(cd);
+    return status;
+}
+
+/***********************************************************************
+ *           DOSFS_OpenDir
+ */
+static NTSTATUS DOSFS_OpenDir(int fd, DOS_DIR** dir)
+{
+    const int   init_size = 0x100;
+    NTSTATUS    status;
+
+    *dir = RtlAllocateHeap(ntdll_get_process_heap(), 0, 
+                           sizeof(**dir) + init_size * sizeof(WCHAR));
+    if (!*dir) status = STATUS_NO_MEMORY;
+    else
+    {
+        (*dir)->used = 0;
+        (*dir)->size = init_size;
+
+        status = DOSFS_OpenDir_VFAT(fd, dir);
+
+        if (status != STATUS_SUCCESS)
+            status = DOSFS_OpenDir_Normal(fd, dir);
+
+        if (status != STATUS_SUCCESS)
+        {
+            RtlFreeHeap(ntdll_get_process_heap(), 0, *dir);
+            *dir = NULL;
+        }
+        else
+        {
+            static const WCHAR empty_strW[] = { 0 };
+            DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
+            (*dir)->used = 0;
+        }
+    }
+
+    return status;
+}
+
+
+/***********************************************************************
+ *           DOSFS_CloseDir
+ */
+static void DOSFS_CloseDir(DOS_DIR *dir)
+{
+    RtlFreeHeap(ntdll_get_process_heap(), 0, dir);
+}
+
+
+/***********************************************************************
+ *           DOSFS_ReadDir
+ */
+static NTSTATUS DOSFS_ReadDir(DOS_DIR *dir, LPCWSTR *long_name, LPCWSTR *short_name)
+{
+    LPCWSTR sn, ln;
+
+    /* the long pathname is first */
+    ln = &dir->names[dir->used];
+    if (!ln[0]) return STATUS_NO_MORE_ENTRIES;
+    *long_name  = ln;
+    dir->used += (strlenW(ln) + 1);
+
+    /* followed by the short path name */
+    sn = &dir->names[dir->used];
+    *short_name = (sn[0]) ? sn : NULL;
+    dir->used += (strlenW(sn) + 1);
+
+    return STATUS_SUCCESS;
+}
+
+
+/******************************************************************
+ *		append
+ *
+ */
+static  NTSTATUS append(struct nt_to_unix_path* paths,
+                        LPCWSTR element, unsigned len, BOOL last)
+{
+    WCHAR       dos_name[12], tmp_buf[13];
+    LPCWSTR     long_name, short_name;
+    DOS_DIR*    dir;
+    NTSTATUS    status = STATUS_SUCCESS;
+    INT 	nlen;
+    int         fd;
+
+    TRACE("(%s + %s@%d last%c)\n", debugstr_an(paths->unix_str.Buffer, paths->unix_str.Length),
+          debugstr_wn(element, len), len, last ? '+':'-');
+
+    fd = open(paths->unix_str.Buffer, O_RDONLY|O_DIRECTORY);
+    if (fd < 0) return STATUS_OBJECT_PATH_NOT_FOUND;
+
+    if (!DOSFS_ToDosFCBFormat(element, dos_name)) dos_name[0] = '\0';
+
+    if ((status = DOSFS_OpenDir(fd, &dir)) != STATUS_SUCCESS)
+    {
+        WARN("(%s,%s): can't open dir: %s\n",
+             paths->unix_str.Buffer, debugstr_w(element), strerror(errno));
+        close(fd);
+        return status;
+    }
+
+    while ((status = DOSFS_ReadDir(dir, &long_name, &short_name)) == STATUS_SUCCESS)
+    {
+        /* Check against Unix name */
+        if (len == strlenW(long_name))
+        {
+            if (!strncmpiW(long_name, element, len)) break;
+        }
+        if (dos_name[0])
+        {
+            /* Check against hashed DOS name */
+            if (!short_name)
+            {
+                DOSFS_Hash(long_name, tmp_buf, TRUE);
+                short_name = tmp_buf;
+            }
+            if (!strcmpW(dos_name, short_name)) break;
+        }
+    }
+    switch (status)
+    {
+    case STATUS_SUCCESS:
+        if (last) paths->last_exists = TRUE;
+        nlen = strlenW(long_name);
+        break;
+    case STATUS_NO_MORE_ENTRIES:
+        if (!last)
+        {
+            WARN("%s not found in '%s'\n", debugstr_wn(element, len), paths->unix_str.Buffer);
+            DOSFS_CloseDir(dir);
+            return STATUS_OBJECT_PATH_NOT_FOUND;
+        }
+        status = STATUS_SUCCESS;
+        paths->last_exists = FALSE;
+        long_name = element;
+        nlen = len;
+        short_name = NULL;
+        break;
+    default:
+        WARN("%s not found in '%s' (%lx)\n", debugstr_wn(element, len), paths->unix_str.Buffer, status);
+        nlen = 0;
+        break;
+    }
+
+    if (status == STATUS_SUCCESS)
+    {
+        nlen = ntdll_wcstoumbs(0, long_name, nlen, 
+                               &paths->unix_str.Buffer[paths->unix_str.Length], 
+                               paths->unix_str.MaximumLength - paths->unix_str.Length,
+                               NULL, NULL);
+        if (nlen > 0) paths->unix_str.Length += nlen;
+        else status = STATUS_BUFFER_TOO_SMALL;
+    }
+
+    if (status == STATUS_SUCCESS)
+    {
+        if (!last)
+            paths->unix_str.Buffer[paths->unix_str.Length++] = '/';
+        paths->unix_str.Buffer[paths->unix_str.Length] = '\0';
+    }
+    TRACE("-> %s (%lx)\n", paths->unix_str.Buffer, status);
+
+    close(fd);
+    DOSFS_CloseDir(dir);
+    return status;
+}
+
+/******************************************************************
+ *              VOLUME_ReadText
+ * FIXME: temporary until volume.c is created
+ */
+static NTSTATUS VOLUME_ReadText(const char* path, char* buffer, unsigned len)
+{
+    int         fl, rlen;
+    NTSTATUS    status;
+
+    TRACE("%s\n", path);
+    fl = open(path, O_RDONLY);
+    if (fl == -1)
+    {
+        TRACE("File %s doesn't exist (%lx)\n", path, FILE_GetNtStatus());
+        return FILE_GetNtStatus();
+    }
+
+    rlen = read(fl, buffer, len - 1);
+    status = (rlen == -1) ? FILE_GetNtStatus() : STATUS_SUCCESS;
+    close(fl);
+    if (status == STATUS_SUCCESS) buffer[rlen] = 0;
+    return status;
+}
+
+/******************************************************************
+ *		FILE_GetUnixName
+ */
+NTSTATUS FILE_GetUnixName(const UNICODE_STRING* win_ustr, struct nt_to_unix_path* paths)
+{
+    static WCHAR        ntW[] = {'\\','?','?','\\'};
+    static WCHAR        sepW[] = {'\\','/',0};
+    static WCHAR        uncW[] = {'u','n','c'};
+    unsigned            i, lasti, sz;
+    NTSTATUS            status;
+    char                src[1024], *ptr;
+    struct stat         st;
+
+    TRACE("(%s)\n",
+          debugstr_wn(win_ustr->Buffer, win_ustr->Length / sizeof(WCHAR)));
+
+    if (memcmp(win_ustr->Buffer, ntW, sizeof(ntW)))
+    {
+        FIXME("Trying to access path (%s) out of NT spacename\n", 
+              debugstr_wn(win_ustr->Buffer, win_ustr->Length / sizeof(WCHAR)));
+        return STATUS_INVALID_PARAMETER;
+    }
+    /* FIXME: security: we should also check that the path isn't \??\..\ which would
+     * defeat all the following code (and the check should be made in ANSI not 
+     * Unicode (because several Unicode chars are mapped to .)
+     */
+    /* do we access a device or a regular file name ? */
+    sz = strcspnW(win_ustr->Buffer + 4, sepW);
+    ptr = src + sprintf(src, "%s/dosdevices/", wine_get_config_dir());
+    i = ntdll_wcstoumbs(0, &win_ustr->Buffer[4], sz,
+                        ptr, sizeof(src) - (ptr - src), NULL, NULL);
+    ptr[i] = '\0';
+    for (;*ptr; ptr++) *ptr = tolower(*ptr);
+    TRACE("@@1 %s\n", src);
+    if (lstat(src, &st) == -1)
+    {
+        if (!memcmp(&win_ustr->Buffer[4], uncW, 3 * sizeof(WCHAR)) &&
+            (win_ustr->Buffer[7] == '/' || win_ustr->Buffer[7] == '\\'))
+        {
+            FIXME("UNC path not supported yet\n");
+        }
+        else WARN("Couldn't lstat %s\n", src);
+        status = FILE_GetNtStatus();
+        goto fail;
+    }
+    paths->is_device = FALSE;
+    status = STATUS_SUCCESS;
+    if (win_ustr->Buffer[4] && win_ustr->Buffer[5] == ':')
+    {
+        char    tmp[MAX_PATH], type[64];
+
+        sprintf(tmp, "%s/device/%c:/type", wine_get_config_dir(), tolowerW(win_ustr->Buffer[4]));
+
+        paths->is_removable = FALSE;
+        if (VOLUME_ReadText(tmp, type, sizeof(type)) == STATUS_SUCCESS)
+        {
+            if (!strcmp(type, "floppy-disk") || !strcmp(type, "removable-disk"))
+                paths->is_removable = TRUE;
+        }
+        if (!win_ustr->Buffer[6])
+        {
+            /* Got a dos drive open access */
+            paths->unix_str.Length = sprintf(paths->unix_str.Buffer, "%s/device/%c:/device", 
+                                             wine_get_config_dir(), tolowerW(win_ustr->Buffer[4]));
+            paths->is_device = TRUE;
+            paths->last_exists = TRUE;
+            TRACE("Got a DOS device %s\n", paths->unix_str.Buffer);
+            goto done;
+        }
+    }
+    if (!S_ISLNK(st.st_mode))
+    {
+        /* should be a VxD or an inaccessible device */
+        strcpy(paths->unix_str.Buffer, src);
+        paths->unix_str.Length = strlen(src);
+        paths->is_device = TRUE;
+        paths->last_exists = TRUE;
+        TRACE("Got a VxD %s\n", paths->unix_str.Buffer);
+        goto done;
+    }
+    strcpy(paths->unix_str.Buffer, src);
+    paths->unix_str.Length = strlen(src);
+    TRACE("@@2 %s\n", paths->unix_str.Buffer);
+
+    paths->last_exists = FALSE;
+
+    if (!win_ustr->Buffer[4 + sz]) /* got a device access */
+    {
+        TRACE("Accessing device: %s (%s)\n", 
+              paths->unix_str.Buffer, paths->is_device ? "dev" : "---");
+        paths->last_exists = TRUE;
+        goto done;
+    }
+
+    TRACE("@@3 %s\n", paths->unix_str.Buffer);
+
+    if (paths->unix_str.Length && paths->unix_str.Buffer[paths->unix_str.Length - 1] != '/')
+        paths->unix_str.Buffer[paths->unix_str.Length++] = '/';
+    paths->unix_str.Buffer[paths->unix_str.Length] = 0;
+
+    for (i = lasti = 4 + sz + 1; i < win_ustr->Length / sizeof(WCHAR); i++)
+    {
+        if (win_ustr->Buffer[i] == '\\' || win_ustr->Buffer[i] == '/')
+        {
+            status = append(paths, &win_ustr->Buffer[lasti], i - lasti, 
+                            win_ustr->Buffer[i + 1] == '\0');
+            if (status != STATUS_SUCCESS) break;
+            lasti = i + 1;
+        }
+    }
+    if (status == STATUS_SUCCESS && i != lasti)
+    {
+        status = append(paths, &win_ustr->Buffer[lasti], i - lasti, TRUE);
+    }
+    if (i == lasti && i == 4 + sz + 1)
+        paths->last_exists = TRUE;
+
+ done:
+    TRACE("\tunix=%s (%lx), last %s)\n", 
+          debugstr_an(paths->unix_str.Buffer, paths->unix_str.Length), 
+          status, paths->last_exists ? "exists" : "misses");
+    return status;
+ fail:
+    TRACE("\t failed %lx\n", status);
+    return status;
+}
+
+/******************************************************************
+ *		FILE_MapUnixToDos
+ *
+ * Tries to map a unix filename to an existing DOS drive.
+ */
+NTSTATUS    FILE_MapUnixToDos(const UNICODE_STRING* unix_ustr, struct nt_to_unix_path* paths)
+{
+    NTSTATUS            status;
+#if 1
+    status = RtlUnicodeStringToAnsiString(&paths->unix_str, unix_ustr, FALSE);
+    paths->is_device = FALSE;
+    paths->last_exists = TRUE;
+    return status;
+#else
+    WCHAR               tmp[] = {'\\','?','?','\\','A',':','\\',0};
+    UNICODE_STRING      ustr;
+    int                 i;
+
+    TRACE("%s\n", debugstr_w(unix_ustr->Buffer));
+
+    ustr.Buffer = tmp;
+    ustr.Length = 7 * sizeof(WCHAR);
+    ustr.MaximumLength = 8 * sizeof(WCHAR);
+    
+    /* FIXME: should simply browse thru $WINEPREFIX/dosdevices/?: 
+       and get correct value */
+
+    for (tmp[4] = 'A'; tmp[4] <= 'Z'; tmp[4]++)
+    {
+        if (!FILE_GetUnixName(&ustr, paths))
+        {
+            if (unix_ustr->Length / sizeof(WCHAR) < paths->unix_str.Length) continue;
+            /* FIXME: use proper conversion here... (codepage) */
+            for (i = 0; i < paths->unix_str.Length; i++)
+                if (unix_ustr->Buffer[i] != paths->unix_str.Buffer[i]) break;
+            if (i == paths->unix_str.Length)
+            {
+                status = RtlUnicodeStringToAnsiString(&paths->unix_str, unix_ustr, FALSE);
+                if (status) return status;
+                TRACE("Got mapping %s => %s (%s)\n", 
+                      debugstr_w(unix_ustr->Buffer), paths->unix_str.Buffer, debugstr_w(tmp));
+                return STATUS_SUCCESS;
+            }
+        }
+    }
+    return STATUS_OBJECT_PATH_NOT_FOUND;
+#endif
+}
+
+/******************************************************************
+ *		wine_get_unix_file_name
+ *
+ */
+BOOL        WINAPI wine_get_unix_file_name(LPCWSTR dospath, LPSTR unixpath, DWORD len)
+{
+    UNICODE_STRING              ustr;
+    struct nt_to_unix_path      paths;
+    NTSTATUS                    status = STATUS_OBJECT_PATH_NOT_FOUND;
+
+    memset(&paths, 0, sizeof(paths));
+    paths.unix_str.Buffer = unixpath;
+    paths.unix_str.MaximumLength = len;
+
+    /* may this is a fully qualified UNIX path... */
+    if (dospath[0] == '/')
+    {
+        RtlInitUnicodeString(&ustr, dospath);
+        status = FILE_MapUnixToDos(&ustr, &paths);
+        if (status != STATUS_SUCCESS && status != STATUS_OBJECT_PATH_NOT_FOUND)
+            return FALSE;
+    }
+    if (status == STATUS_OBJECT_PATH_NOT_FOUND && 
+        RtlDosPathNameToNtPathName_U(dospath, &ustr, NULL, NULL))
+    {
+        status = FILE_GetUnixName(&ustr, &paths);
+/* EPP         if (status == STATUS_SUCCESS && !paths.last_exist) */
+/* EPP             status = STATUS_OBJECT_PATH_NOT_FOUND; */
+        RtlFreeUnicodeString(&ustr);
+    }
+    return status == STATUS_SUCCESS;
+}
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/ntdll44/file.c dlls/ntdll/file.c
--- dlls/ntdll44/file.c	2004-01-23 22:25:46.000000000 +0100
+++ dlls/ntdll/file.c	2004-01-23 22:32:12.000000000 +0100
@@ -206,7 +206,7 @@
  * Retrieve the Nt Status code from errno.
  * Try to be consistent with FILE_SetDosError().
  */
-static DWORD FILE_GetNtStatus(void)
+DWORD FILE_GetNtStatus(void)
 {
     int err = errno;
     DWORD nt;
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/ntdll44/Makefile.in dlls/ntdll/Makefile.in
--- dlls/ntdll44/Makefile.in	2004-01-01 09:53:17.000000000 +0100
+++ dlls/ntdll/Makefile.in	2004-01-19 21:48:52.000000000 +0100
@@ -10,6 +10,7 @@
 	cdrom.c \
 	critsection.c \
 	debugtools.c \
+	dos_fs.c \
 	env.c \
 	error.c \
 	exception.c \
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/ntdll44/ntdll_misc.h dlls/ntdll/ntdll_misc.h
--- dlls/ntdll44/ntdll_misc.h	2004-01-23 22:25:46.000000000 +0100
+++ dlls/ntdll/ntdll_misc.h	2004-02-01 17:27:34.000000000 +0100
@@ -87,6 +87,23 @@
                                       LPSECURITY_ATTRIBUTES sa, DWORD creation,
                                       DWORD attributes, HANDLE template );
 
+/* File IO */
+
+#define MAX_PATHNAME_LEN   1024
+
+struct nt_to_unix_path
+{
+    ANSI_STRING         unix_str;
+    unsigned            is_device : 1,          /* whether we're dealing with a device or a regular file */
+                        is_removable : 1,       /* whether the file is on a removable device */
+                        last_exists : 1;        /* whether last part of the path exists or not */
+};
+
+extern NTSTATUS FILE_GetUnixName(const UNICODE_STRING* win_ustr,
+                                 struct nt_to_unix_path* n2up);
+extern NTSTATUS FILE_MapUnixToDos(const UNICODE_STRING* unix_ustr, struct nt_to_unix_path* paths);
+extern DWORD    FILE_GetNtStatus(void);
+
 /* Device IO */
 /* ntdll/cdrom.c.c */
 extern NTSTATUS CDROM_DeviceIoControl(HANDLE hDevice, 
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/ntdll44/ntdll.spec dlls/ntdll/ntdll.spec
--- dlls/ntdll44/ntdll.spec	2004-01-23 22:25:46.000000000 +0100
+++ dlls/ntdll/ntdll.spec	2004-01-23 22:32:12.000000000 +0100
@@ -1081,6 +1081,9 @@
 # signal handling
 @ cdecl __wine_set_signal_handler(long ptr)
 
+# files
+@ stdcall wine_get_unix_file_name(wstr ptr long)
+
 ################################################################
 # Wine dll separation hacks, these will go away, don't use them
 #
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/ntdll44/path.c dlls/ntdll/path.c
--- dlls/ntdll44/path.c	2004-01-23 22:26:08.000000000 +0100
+++ dlls/ntdll/path.c	2004-01-23 22:32:54.000000000 +0100
@@ -22,6 +22,7 @@
 #include "config.h"
 
 #include <stdarg.h>
+#include <sys/stat.h>
 
 #include "windef.h"
 #include "winbase.h"
@@ -70,13 +71,36 @@
  *
  * FIXME: should not use CreateFileW
  */
-BOOLEAN WINAPI RtlDoesFileExists_U(LPCWSTR file_name)
+BOOLEAN WINAPI RtlDoesFileExists_U(LPCWSTR filename)
 {
-    HANDLE handle = pCreateFileW( file_name, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
-                                  NULL, OPEN_EXISTING, 0, 0 );
-    if (handle == INVALID_HANDLE_VALUE) return FALSE;
-    NtClose( handle );
-    return TRUE;
+    char                buffer[2048]; /* FIXME */
+    UNICODE_STRING      ntstr;
+    struct nt_to_unix_path      paths;
+    struct stat         st;
+    NTSTATUS            status = STATUS_OBJECT_PATH_NOT_FOUND;
+
+    memset(&paths, 0, sizeof(paths));
+    paths.unix_str.Buffer = buffer;
+    paths.unix_str.MaximumLength = sizeof(buffer);
+
+    /* may this is a fully qualified UNIX path... */
+    if (filename[0] == '/')
+    {
+        RtlInitUnicodeString(&ntstr, filename);
+        status = FILE_MapUnixToDos(&ntstr, &paths);
+        if (status != STATUS_SUCCESS && status != STATUS_OBJECT_PATH_NOT_FOUND)
+            return FALSE;
+    }
+    if (status == STATUS_OBJECT_PATH_NOT_FOUND)
+    {
+        if (RtlDosPathNameToNtPathName_U(filename, &ntstr, NULL, NULL))
+        {
+            BOOL ret = FILE_GetUnixName(&ntstr, &paths) || !paths.last_exists;
+            RtlFreeUnicodeString(&ntstr);
+            if (ret) return FALSE;
+        } else return FALSE;
+    }
+    return stat(paths.unix_str.Buffer, &st) != -1;
 }
 
 /***********************************************************************
@@ -445,7 +469,6 @@
         reqsize += deplen + sizeof(WCHAR);
         goto done;
     }
-
     memmove(buffer + reqsize / sizeof(WCHAR), name + dep, deplen + sizeof(WCHAR));
     if (reqsize) memcpy(buffer, ins_str, reqsize);
     reqsize += deplen;
diff -u -N -r -x '*~' -x '.#*' -x CVS files44/dos_fs.c files/dos_fs.c
--- files44/dos_fs.c	2004-01-23 22:25:48.000000000 +0100
+++ files/dos_fs.c	2004-01-23 22:33:49.000000000 +0100
@@ -957,26 +957,6 @@
 
 
 /***********************************************************************
- *           wine_get_unix_file_name (KERNEL32.@) Not a Windows API
- *
- * Return the full Unix file name for a given path.
- */
-BOOL WINAPI wine_get_unix_file_name( LPCWSTR dosW, LPSTR buffer, DWORD len )
-{
-    BOOL ret;
-    DOS_FULL_NAME path;
-
-    ret = DOSFS_GetFullName( dosW, FALSE, &path );
-    if (ret && len)
-    {
-        strncpy( buffer, path.long_name, len );
-        buffer[len - 1] = 0; /* ensure 0 termination */
-    }
-    return ret;
-}
-
-
-/***********************************************************************
  *           get_show_dir_symlinks_option
  */
 static BOOL get_show_dir_symlinks_option(void)


More information about the wine-patches mailing list