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