ntdll / kernel32: #42

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


New volume management functions from NT
- implementation of Find{First|Next}Volume[AW], FindVolumeClose,
- partial implementation of DeleteVolumeMountPoint[AW],
   GetVolumeNameForVolumeMountPoint[AW], SetVolumeMountPoint[AW]
   (only works when mount point is at root)
- rewrite of QueryDosDevice, DefineDosDevice for new fs scheme

Note: DefineDosDevice is now broken (until patch 47)
-------------- next part --------------
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/kernel41/kernel32.spec dlls/kernel/kernel32.spec
--- dlls/kernel41/kernel32.spec	2004-02-01 13:25:28.000000000 +0100
+++ dlls/kernel/kernel32.spec	2004-02-01 14:10:48.000000000 +0100
@@ -227,6 +227,8 @@
 @ stdcall DeleteCriticalSection(ptr) ntdll.RtlDeleteCriticalSection
 @ stdcall DeleteFileA(str)
 @ stdcall DeleteFileW(wstr)
+@ stdcall DeleteVolumeMountPointA(str)
+@ stdcall DeleteVolumeMountPointW(wstr)
 @ stdcall DeviceIoControl(long long ptr long ptr long ptr ptr)
 @ stdcall DisableThreadLibraryCalls(long)
 @ stdcall DisconnectNamedPipe(long)
@@ -302,22 +304,22 @@
 @ stdcall FindFirstChangeNotificationW(wstr long long)
 @ stdcall FindFirstFileA(str ptr)
 @ stdcall FindFirstFileW(wstr ptr)
-@ stub FindFirstVolumeA
-@ stub FindFirstVolumeW
+@ stdcall FindFirstVolumeA(ptr long)
+@ stdcall FindFirstVolumeW(ptr long)
 @ stub FindFirstVolumeMountPointA
 @ stub FindFirstVolumeMountPointW
 @ stdcall FindNextChangeNotification(long)
 @ stdcall FindNextFileA(long ptr)
 @ stdcall FindNextFileW(long ptr)
-@ stub FindNextVolumeA
-@ stub FindNextVolumeW
+@ stdcall FindNextVolumeA(long ptr long)
+@ stdcall FindNextVolumeW(long ptr long)
 @ stub FindNextVolumeMountPointA
 @ stub FindNextVolumeMountPointW
 @ stdcall FindResourceA(long str str)
 @ stdcall FindResourceExA(long str str long)
 @ stdcall FindResourceExW(long wstr wstr long)
 @ stdcall FindResourceW(long wstr wstr)
-@ stub FindVolumeClose
+@ stdcall FindVolumeClose(long)
 @ stub FindVolumeMountPointClose
 @ stdcall FlushConsoleInputBuffer(long)
 @ stdcall FlushFileBuffers(long)
@@ -512,8 +514,8 @@
 @ stdcall GetVersionExW(ptr)
 @ stdcall GetVolumeInformationA(str ptr long ptr ptr ptr ptr long)
 @ stdcall GetVolumeInformationW(wstr ptr long ptr ptr ptr ptr long)
-@ stub GetVolumeNameForVolumeMountPointA
-@ stdcall GetVolumeNameForVolumeMountPointW(wstr long long)
+@ stdcall GetVolumeNameForVolumeMountPointA(str ptr long)
+@ stdcall GetVolumeNameForVolumeMountPointW(wstr ptr long)
 @ stub GetVolumePathNameA
 @ stub GetVolumePathNameW
 @ stdcall GetWindowsDirectoryA(ptr long)
@@ -796,8 +798,8 @@
 @ stdcall SetUserGeoID(long)
 @ stdcall SetVolumeLabelA(str str)
 @ stdcall SetVolumeLabelW(wstr wstr)
-@ stub SetVolumeMountPointA
-@ stub SetVolumeMountPointW
+@ stdcall SetVolumeMountPointA(str str)
+@ stdcall SetVolumeMountPointW(wstr wstr)
 @ stdcall SetupComm(long long long)
 @ stdcall SizeofResource(long long)
 @ stdcall Sleep(long)
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/kernel41/kernel_private.h dlls/kernel/kernel_private.h
--- dlls/kernel41/kernel_private.h	2004-01-14 22:29:57.000000000 +0100
+++ dlls/kernel/kernel_private.h	2004-01-18 19:13:01.000000000 +0100
@@ -48,6 +48,7 @@
 #define DOS_TABLE_SIZE 256
 extern HANDLE dos_handles[DOS_TABLE_SIZE];
 void FILE_ConvertOFMode( INT mode, DWORD *access, DWORD *sharing );
+void FILE_SetDosError(void);
 
 extern void PTHREAD_Init(void);
 extern BOOL WOWTHUNK_Init(void);
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/kernel41/Makefile.in dlls/kernel/Makefile.in
--- dlls/kernel41/Makefile.in	2004-02-01 13:25:28.000000000 +0100
+++ dlls/kernel/Makefile.in	2004-02-01 14:10:47.000000000 +0100
@@ -77,6 +77,7 @@
 	version.c \
 	virtual.c \
 	vxd.c \
+	volume.c \
 	win87em.c \
 	windebug.c \
 	wowthunk.c
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/kernel41/tests/drive.c dlls/kernel/tests/drive.c
--- dlls/kernel41/tests/drive.c	2004-01-23 22:19:47.000000000 +0100
+++ dlls/kernel/tests/drive.c	2004-01-23 22:21:08.000000000 +0100
@@ -173,11 +173,92 @@
     }
 }
 
+/* those function only appear starting at Win2k, so call them thru pointers */
+static HANDLE   (WINAPI *pFindFirstVolumeA)(LPSTR,DWORD);
+static BOOL     (WINAPI *pFindNextVolumeA)(HANDLE,LPSTR,DWORD);
+static BOOL     (WINAPI *pFindVolumeClose)(HANDLE);
+static BOOL     (WINAPI *pGetVolumeNameForVolumeMountPointA)(LPCSTR,LPSTR,DWORD);
+
+static void test_Volume(void)
+{
+    char        buffer[MAX_PATH];
+    HANDLE      h = pFindFirstVolumeA(buffer, sizeof(buffer));
+    char        drv[] = "A:\\";
+    DWORD       map = GetLogicalDrives();
+    char*       ptr;
+    unsigned    ret;
+
+    ok(h != INVALID_HANDLE_VALUE, "enum of volumes");
+    if (h != INVALID_HANDLE_VALUE)
+    {
+        do
+        {
+            trace("Got volume %s\n", buffer);
+        } while (pFindNextVolumeA(h, buffer, sizeof(buffer)));
+        pFindVolumeClose(h);
+    }
+    for (drv[0] = 'A'; drv[0] <= 'Z'; drv[0]++)
+    {
+        ret = pGetVolumeNameForVolumeMountPointA(drv, buffer, sizeof(buffer));
+        if (map & (1 << (drv[0] - 'A')))
+        {
+            ok(ret, "Couldn't get volume mount point (%lu)\n", GetLastError());
+            trace("%s => %s\n", drv, buffer);
+        }
+        else
+            ok(!ret, "Shouldn't get volume mount point\n");
+    }
+    ret = GetLogicalDriveStringsA(sizeof(buffer) - 1, buffer);
+    ok(ret > 0 && ret < sizeof(buffer), 
+       "Cannot get drive strings (%lu)\n", GetLastError());
+    if (ret > 0 && ret < sizeof(buffer))
+    {
+        for (ptr = buffer; *ptr; ptr += strlen(ptr) + 1)
+        {
+            trace("\t%s\n", ptr);
+            ok(map & (1 << (toupper(*ptr) - 'A')), "Wrong bit set\n");
+        }
+    }
+}
+
+static void test_QueryDosDevice(void)
+{
+    char        buffer[1024];
+    DWORD       len;
+
+    struct {const char* dev; const char* result;} tests[] = {
+        {"com1", "\\device\\serial0"},
+        {"lpt1", "\\device\\parallel0"},
+        {"nul",  "\\device\\null"},
+        {"aux",  "\\dosdevices\\com1"},
+        {NULL, NULL}
+    }, *test;
+    for (test = tests; test->dev; test++)
+    {
+        len = QueryDosDevice(test->dev, buffer, sizeof(buffer));
+        ok(len, "QueryDosDevice failed on %s\n", test->dev);
+        if (len)
+            ok(strcmp(buffer, test->result) == 0, "Mismatch %s instead of %s\n", buffer, test->result);
+    }
+    ok(QueryDosDevice(NULL, buffer, sizeof(buffer)), "QueryDosDevice failed on <null>\n");
+}
+
 START_TEST(drive)
 {
+    HMODULE mod;
+
     test_GetDriveTypeA();
     test_GetDriveTypeW();
 
     test_GetDiskFreeSpaceA();
     test_GetDiskFreeSpaceW();
+
+    mod = GetModuleHandleA("kernel32.dll");
+    pFindFirstVolumeA = (void *)GetProcAddress(mod,"FindFirstVolumeA");
+    pFindNextVolumeA = (void *)GetProcAddress(mod,"FindNextVolumeA");
+    pFindVolumeClose = (void *)GetProcAddress(mod,"FindVolumeClose");
+    pGetVolumeNameForVolumeMountPointA = (void *)GetProcAddress(mod,"GetVolumeNameForVolumeMountPointA");
+
+    if (pFindFirstVolumeA) test_Volume();
+    test_QueryDosDevice();
 }
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/kernel41/volume.c dlls/kernel/volume.c
--- dlls/kernel41/volume.c	1970-01-01 01:00:00.000000000 +0100
+++ dlls/kernel/volume.c	2004-02-01 21:13:30.000000000 +0100
@@ -0,0 +1,593 @@
+/*
+ * Volume file system functions
+ *
+ * Label & serial number read support.
+ * (c) 1999 Petr Tomasek <tomasek at etf.cuni.cz>
+ * (c) 2000 Andreas Mohr (changes)
+ *
+ * Copyright 1993 Erik Bos
+ * Copyright 1996 Alexandre Julliard
+ * Copyright 2003 Eric Pouech
+ *
+ * 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 <dirent.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "winternl.h"
+
+#include "wine/windef16.h"
+#include "kernel_private.h"
+
+#include "wine/unicode.h"
+#include "wine/library.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(file);
+
+/******************************************************************
+ *              get_volume
+ *
+ *
+ */
+static BOOL get_volume(char drive, char* dst, size_t size)
+{
+    char        path[MAX_PATH], tmp[MAX_PATH];
+    LPCSTR      beg, end;
+    struct stat st;
+    size_t      len;
+
+    sprintf(path, "%s/dosdevices/%c:", wine_get_config_dir(), drive);
+    if (lstat(path, &st) == -1 || !S_ISLNK(st.st_mode))
+    {
+        SetLastError(ERROR_PATH_NOT_FOUND);
+        return FALSE;
+    }
+    len = readlink(path, tmp, sizeof(tmp));
+    if (len == -1)
+    {
+        FILE_SetDosError();
+        return FALSE;
+    }
+    if (len >= sizeof(tmp))
+    {
+        SetLastError(ERROR_INSUFFICIENT_BUFFER);
+        return FALSE;
+    }
+    tmp[len] = '\0';
+    /* FIXME: this is a bit harsh for parsing the volume name... */
+    if (!(beg = strstr(tmp, "/volume{")) || !(end = strchr(beg + 7, '}')))
+    {
+        FIXME("Internal error in string parsing (%s)\n", tmp);
+        return FALSE;
+    }
+    len = end - beg;
+    if (len + 1 > size)
+    {
+        SetLastError(ERROR_INSUFFICIENT_BUFFER);
+        return FALSE;
+    }
+    strncpy(dst, beg + 1, len);
+    dst[len] = '\0';
+    return TRUE;
+}
+
+struct volume_enum
+{
+    LPWSTR      index;
+    WCHAR       data[1];
+};
+
+/******************************************************************
+ *		FindFirstVolumeA   (KERNEL32.@)
+ */
+HANDLE WINAPI FindFirstVolumeA(LPSTR buffer, DWORD size)
+{
+    LPWSTR      ptr = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
+    HANDLE      h;
+
+    if (!ptr) return INVALID_HANDLE_VALUE;
+    if ((h = FindFirstVolumeW(ptr, size)) != INVALID_HANDLE_VALUE)
+        WideCharToMultiByte(CP_ACP, 0, ptr, -1, buffer, size, NULL, NULL);
+    HeapFree(GetProcessHeap(), 0, ptr);
+    return h;
+}
+
+/******************************************************************
+ *		FindNextVolumeA   (KERNEL32.@)
+ */
+BOOL WINAPI FindNextVolumeA(HANDLE h, LPSTR buffer, DWORD size)
+{
+    LPWSTR      ptr = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
+    BOOL        ret;
+
+    if (!ptr) return FALSE;
+    if ((ret = FindNextVolumeW(h, ptr, size)))
+        WideCharToMultiByte(CP_ACP, 0, ptr, -1, buffer, size, NULL, NULL);
+    HeapFree(GetProcessHeap(), 0, ptr);
+    return ret;
+}
+
+/******************************************************************
+ *		FindFirstVolumeW   (KERNEL32.@)
+ */
+HANDLE WINAPI FindFirstVolumeW(LPWSTR buffer, DWORD size)
+{
+    unsigned            used = 0, len;
+    struct volume_enum* ve = HeapAlloc(GetProcessHeap(), 0, sizeof(*ve));
+    char                path[MAX_PATH];
+    DIR*                dir;
+    struct dirent*      de;
+
+    if (!ve) return INVALID_HANDLE_VALUE;
+    sprintf(path, "%s/device/", wine_get_config_dir());
+    
+    ve->index = ve->data;
+    dir = opendir(path);
+    if (dir)
+    {
+        while ((de = readdir(dir)))
+        {
+            if (strncmp(de->d_name, "volume{", 7)) continue;
+            len = MultiByteToWideChar(CP_UNIXCP, 0, de->d_name, -1, NULL, 0);
+            ve = HeapReAlloc(GetProcessHeap(), 0, ve, 
+                             sizeof(*ve) + (used + len) * sizeof(WCHAR));
+            if (!ve) return INVALID_HANDLE_VALUE;
+            MultiByteToWideChar(CP_UNIXCP, 0, de->d_name, -1, &ve->data[used], len);
+            used += len;
+        }
+        ve->data[used] = '\0';
+    }
+    closedir(dir);
+    if (!FindNextVolumeW((HANDLE)ve, buffer, size))
+    {
+        FindVolumeClose((HANDLE)ve);
+        return INVALID_HANDLE_VALUE;
+    }
+    return (HANDLE)ve;
+}
+
+/******************************************************************
+ *		FindNextVolumeW   (KERNEL32.@)
+ */
+BOOL WINAPI FindNextVolumeW(HANDLE h, LPWSTR buffer, DWORD size)
+{
+    struct volume_enum* ve = (void*)h;
+    size_t              len;
+
+    if (h == INVALID_HANDLE_VALUE) return FALSE;
+    if (!*ve->index) return FALSE; /* last element, set last error ? */
+    len = strlenW(ve->index) + 1;
+    if (size < len)
+    {
+        SetLastError(ERROR_INSUFFICIENT_BUFFER);
+        return FALSE;
+    }
+    memcpy(buffer, ve->index, len * sizeof(WCHAR));
+    ve->index += len;
+    return TRUE;
+}
+
+/******************************************************************
+ *		FindVolumeClose   (KERNEL32.@)
+ */
+BOOL WINAPI FindVolumeClose(HANDLE h)
+{
+    return HeapFree(GetProcessHeap(), 0, (void*)h);
+}
+
+/***********************************************************************
+ *           GetVolumeNameForVolumeMountPointA   (KERNEL32.@)
+ */
+BOOL WINAPI GetVolumeNameForVolumeMountPointA(LPCSTR mount, LPSTR dst, DWORD size)
+{
+    LPWSTR      mountW, dstW;
+    unsigned    len;
+    BOOL        ret;
+
+    len = MultiByteToWideChar(CP_ACP, 0, mount, -1, NULL, 0);
+    mountW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+    if (!mountW) return FALSE;
+    MultiByteToWideChar(CP_ACP, 0, mount, -1, mountW, len);
+    dstW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
+    if (!dstW)
+    {
+        HeapFree(GetProcessHeap(), 0, mountW);
+        return FALSE;
+    }
+    if ((ret = GetVolumeNameForVolumeMountPointW(mountW, dstW, size)))
+        WideCharToMultiByte(CP_ACP, 0, dstW, -1, dst, size, NULL, NULL);
+    HeapFree(GetProcessHeap(), 0, mountW);
+    HeapFree(GetProcessHeap(), 0, dstW);
+    return ret;
+}
+
+/***********************************************************************
+ *           GetVolumeNameForVolumeMountPointW   (KERNEL32.@)
+ */
+BOOL WINAPI GetVolumeNameForVolumeMountPointW(LPCWSTR mount, LPWSTR dst, DWORD size)
+{
+    char        tmp[MAX_PATH];
+
+    TRACE("(%s, %p, %lx)\n", debugstr_w(mount), dst, size);
+
+    /* we only support setting mount points on root directory */
+    if (strlenW(mount) != 3 || mount[1] != ':' || mount[2] != '\\')
+    {
+        FIXME("Unsupported mount point %s\n", debugstr_w(mount));
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    if (!get_volume(tolowerW(mount[0]), tmp, sizeof(tmp))) return FALSE;
+
+    dst[0] = dst[1] = dst[3] = '\\'; dst[2] = '?';
+    MultiByteToWideChar(CP_UNIXCP, 0, tmp, -1, dst + 4, size - 4);
+    return TRUE;
+}
+
+
+/******************************************************************
+ *		SetVolumeMountPointA   (KERNEL32.@)
+ */
+BOOL WINAPI SetVolumeMountPointA(LPCSTR mount, LPCSTR volume)
+{
+    LPWSTR      mountW, volumeW;
+    unsigned    len;
+    BOOL        ret;
+
+    len = MultiByteToWideChar(CP_ACP, 0, mount, -1, NULL, 0);
+    mountW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+    if (!mountW) return FALSE;
+    MultiByteToWideChar(CP_ACP, 0, mount, -1, mountW, len);
+    len = MultiByteToWideChar(CP_ACP, 0, volume, -1, NULL, 0);
+    volumeW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+    if (!volumeW)
+    {
+        HeapFree(GetProcessHeap(), 0, mountW);
+        return FALSE;
+    }
+    MultiByteToWideChar(CP_ACP, 0, volume, -1, volumeW, len);
+    
+    ret = SetVolumeMountPointW(mountW, volumeW);
+    HeapFree(GetProcessHeap(), 0, mountW);
+    HeapFree(GetProcessHeap(), 0, volumeW);
+    return ret;
+}
+
+/******************************************************************
+ *		SetVolumeMountPointW   (KERNEL32.@)
+ */
+BOOL        WINAPI SetVolumeMountPointW(LPCWSTR mount, LPCWSTR volumeW)
+{
+    char        volume[MAX_PATH], dosmnt1[MAX_PATH], dosmnt2[MAX_PATH];
+    struct stat st;
+
+    if (strlenW(mount) < 3 || mount[1] != ':' || mount[2] != '\\')
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    WideCharToMultiByte(CP_UNIXCP, 0, volumeW, -1, volume, sizeof(volume), NULL, NULL);
+
+    /* MS doc is ambiguous here... in some cases, it says it'll unmount the 
+     * existing mount if any, in some other cases it says it won't delete it 
+     * at root dir... doing the latest
+     */
+    sprintf(dosmnt2, "%s/device/%s/mount", wine_get_config_dir(), volume);
+
+    if (!wine_get_unix_file_name(mount, dosmnt1, sizeof(dosmnt1)))
+    {
+        SetLastError(ERROR_PATH_NOT_FOUND);
+        return FALSE;
+    }
+    if (stat(dosmnt1, &st) == -1 && !S_ISDIR(st.st_mode))
+    {
+        SetLastError(ERROR_FILE_NOT_FOUND);
+        return FALSE;
+    }
+    /* volume should exist and be mounted */
+    if (stat(dosmnt2, &st) == -1)
+    {
+        SetLastError(ERROR_PATH_NOT_FOUND);
+        return FALSE;
+    }
+    /* we fake the mount by:
+     * 1/ remove the dir
+     * 2/ symlink the dir to the volume mount point
+     */
+    if (rmdir(dosmnt1) == -1)
+    {
+        FILE_SetDosError();
+        return FALSE;
+    }
+    if (symlink(dosmnt1, dosmnt2) == -1)
+    {
+        FILE_SetDosError();
+        return FALSE;
+    }
+    return TRUE;
+}
+
+/******************************************************************
+ *		DeleteVolumeMountPointA (KERNEL32.@)
+ */
+BOOL WINAPI DeleteVolumeMountPointA(LPCSTR mount)
+{
+    UNICODE_STRING m;
+    BOOL           ret;
+
+    if (!RtlCreateUnicodeStringFromAsciiz(&m, mount))
+    {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        return FALSE;
+    }
+    ret = DeleteVolumeMountPointW(m.Buffer);
+    RtlFreeUnicodeString(&m);
+    return ret;
+}
+
+/******************************************************************
+ *		DeleteVolumeMountPointW (KERNEL32.@)
+ */
+BOOL WINAPI DeleteVolumeMountPointW(LPCWSTR mount)
+{
+    char        buffer[MAX_PATH];
+    struct stat st;
+
+    if (!wine_get_unix_file_name(mount, buffer, sizeof(buffer)))
+    {
+        SetLastError(ERROR_PATH_NOT_FOUND);
+        return FALSE;
+    }
+    if (lstat(buffer, &st) != -1 && S_ISLNK(st.st_mode) &&
+        unlink(buffer) != -1 && mkdir(buffer, 0777) != -1)
+        return TRUE;
+    FILE_SetDosError();
+    return FALSE;
+}
+
+/***********************************************************************
+ *           DefineDosDeviceA       (KERNEL32.@)
+ */
+BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath)
+{
+    UNICODE_STRING d, t;
+    BOOL           ret;
+
+    if (!RtlCreateUnicodeStringFromAsciiz(&d, devname))
+    {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        return FALSE;
+    }
+    if (!RtlCreateUnicodeStringFromAsciiz(&t, targetpath))
+    {
+        RtlFreeUnicodeString(&d);
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        return FALSE;
+    }
+    ret = DefineDosDeviceW(flags, d.Buffer, t.Buffer);
+    RtlFreeUnicodeString(&d);
+    RtlFreeUnicodeString(&t);
+    return ret;
+}
+
+
+/***********************************************************************
+ *           DefineDosDeviceA       (KERNEL32.@)
+ */
+BOOL WINAPI DefineDosDeviceW(DWORD flags, LPCWSTR devname, LPCWSTR targetpath) 
+{
+    UNICODE_STRING      ustr;
+    char                volume[MAX_PATH], dosmnt1[MAX_PATH], dosmnt2[MAX_PATH], 
+                        device1[MAX_PATH], device2[MAX_PATH];
+    struct stat         st;
+
+    /* FIXME: so far we only support drive as dos devices */
+    if (!(toupperW(devname[0]) >= 'A' && toupperW(devname[0]) <= 'Z') ||
+        devname[1] != ':' || devname[2] != 0)
+    {
+        FIXME("(0x%08lx,%s,%s),stub!\n", flags, debugstr_w(devname), debugstr_w(targetpath));
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    if (flags & DDD_RAW_TARGET_PATH)
+    {
+        RtlInitUnicodeString(&ustr, targetpath);
+    }
+    else if (!RtlDosPathNameToNtPathName_U(targetpath, &ustr, NULL, NULL))
+    {
+        SetLastError(ERROR_PATH_NOT_FOUND);
+        return FALSE;
+    }
+
+    WideCharToMultiByte(CP_UNIXCP, 0, ustr.Buffer, -1, volume, sizeof(volume), NULL, NULL);
+    if (!(flags & DDD_RAW_TARGET_PATH))
+        RtlFreeUnicodeString(&ustr);
+
+    /* FIXME: check it this is correct ? */
+    if (strncmp(volume, "\\\\?\\device\\", 12))
+    {
+        SetLastError(ERROR_PATH_NOT_FOUND);
+        return FALSE;
+    }
+    /* FIXME: we should check that no other drive is mapped to his very volume... */
+    sprintf(dosmnt1, "%s/dosdevices/%c:", wine_get_config_dir(), tolower(devname[0]));
+    sprintf(dosmnt2, "%s/device/%s/mount", wine_get_config_dir(), volume + 12);
+    sprintf(device1, "%s/device/%c:", wine_get_config_dir(), tolower(devname[0]));
+    sprintf(device2, "%s/device/%s", wine_get_config_dir(), volume + 12);
+
+    if (flags & DDD_REMOVE_DEFINITION)
+    {
+        if (unlink(dosmnt1) == -1 || unlink(device1) == -1)
+        {
+            FILE_SetDosError();
+            return FALSE;
+        }
+        return TRUE;
+    }
+
+    /* MS doc is ambiguous here... in some cases, it says it'll unmount the 
+     * existing mount if any, in some other cases it says it won't delete it 
+     * at root dir... doing the latest
+     */
+    /* symlink destination should exist */
+    if (stat(dosmnt2, &st) == -1 || stat(device2, &st) == -1)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    /* symlink source shouldn't exist */
+    if (stat(dosmnt1, &st) != -1 || stat(device1, &st) != -1)
+    {
+        SetLastError(ERROR_ALREADY_ASSIGNED);
+        return FALSE;
+    }
+    if (symlink(dosmnt2, dosmnt1) == -1)
+    {
+        FILE_SetDosError();
+        return FALSE;
+    }
+    if (symlink(device2, device1) == -1)
+    {
+        FILE_SetDosError();
+        remove(dosmnt2);
+        return FALSE;
+    }
+    return TRUE;
+}
+
+/***********************************************************************
+ *           QueryDosDeviceA   (KERNEL32.@)
+ *
+ * returns array of strings terminated by \0, terminated by \0
+ */
+DWORD WINAPI QueryDosDeviceA(LPCSTR devname, LPSTR target, DWORD bufsize)
+{
+    DWORD ret = 0, retW;
+    LPWSTR targetW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
+                                      bufsize * sizeof(WCHAR));
+    UNICODE_STRING devnameW;
+
+    if (devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
+    else devnameW.Buffer = NULL;
+
+    retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
+
+    ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target,
+                              bufsize, NULL, NULL);
+
+    RtlFreeUnicodeString(&devnameW);
+    if (targetW) HeapFree(GetProcessHeap(), 0, targetW);
+    return ret;
+}
+
+
+/***********************************************************************
+ *           QueryDosDeviceW   (KERNEL32.@)
+ *
+ * returns array of strings terminated by \0, terminated by \0
+ *
+ * FIXME
+ *      - Win9x returns for all calls ERROR_INVALID_PARAMETER 
+ */
+DWORD WINAPI QueryDosDeviceW(LPCWSTR devname, LPWSTR target, DWORD bufsize)
+{
+    int         len;
+    DWORD       cnt;
+    char        buffer[MAX_PATH], dst[MAX_PATH];
+    LPSTR       ptr;
+    struct stat st;
+
+    TRACE("(%s,%p,%lu)\n", debugstr_w(devname), target, bufsize);
+    SetLastError(0);
+    if (!devname) 
+    {
+        DWORD           ret = 0;
+        char            path[MAX_PATH];
+        DIR*            dir;
+        struct dirent*  de;
+
+        len = sprintf(path, "%s/dosdevices/", wine_get_config_dir());
+    
+        if ((dir = opendir(path)))
+        {
+            while ((de = readdir(dir)))
+            {
+                strcpy(path + len, de->d_name);
+                if (lstat(path, &st) != -1 && S_ISLNK(st.st_mode))
+                {
+                    cnt = MultiByteToWideChar(CP_UNIXCP, 0, de->d_name, -1, NULL, 0);
+                    if (target)
+                    {
+                        if (ret + cnt + 1 > bufsize)
+                        {
+                            /* in this case WinXP returns 0 */
+                            FIXME("function return is wrong for WinXP!\n");
+                            SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                            break;
+                        }
+                        MultiByteToWideChar(CP_UNIXCP, 0, de->d_name, -1,
+                                            target + ret, bufsize - ret);
+                    }
+                    ret += cnt;
+                }
+
+            }
+            closedir(dir);
+        }
+	if (target && bufsize > 0) target[ret++] = '\0';
+        return ret;
+    }
+    len = sprintf(buffer, "%s/dosdevices/", wine_get_config_dir());
+    WideCharToMultiByte(CP_UNIXCP, 0, devname, -1, buffer + len, sizeof(buffer) - len, NULL, NULL);
+    for (ptr = buffer + len; *ptr; ptr++) *ptr = tolower(*ptr);
+    if (lstat(buffer, &st) != -1 && S_ISLNK(st.st_mode))
+    {
+        len = readlink(buffer, dst, sizeof(dst));
+        if (len > 0 && len < sizeof(dst))
+        {
+            if (len >= 7 && !strncmp(dst + len - 7, "/device", 7)) len -= 7;
+            dst[len] = '\0';
+            for (cnt = 0, ptr = dst + len - 1; ptr >= dst; ptr--)
+            {
+                if (*ptr == '/')
+                {
+                    *ptr = '\\';
+                    if (cnt++ == 1)
+                        return MultiByteToWideChar(CP_UNIXCP, 0, ptr, -1, target, bufsize);
+                }
+            }
+            ERR("Malformated device %s for %s\n", dst, buffer);
+        }
+        else ERR("Cannot read symlink for %s\n", buffer);
+    }
+    else FIXME("(%s) not detected as DOS device!\n", debugstr_w(devname));
+    /* Win9x set the error ERROR_INVALID_PARAMETER */
+    SetLastError(ERROR_FILE_NOT_FOUND);
+    return 0;
+}
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/ntdll41/path.c dlls/ntdll/path.c
--- dlls/ntdll41/path.c	2004-01-23 22:20:11.000000000 +0100
+++ dlls/ntdll/path.c	2004-01-23 22:21:24.000000000 +0100
@@ -168,7 +168,7 @@
  * FIXME:
  *      + fill the cd structure
  */
-BOOLEAN  WINAPI RtlDosPathNameToNtPathName_U(PWSTR dos_path,
+BOOLEAN  WINAPI RtlDosPathNameToNtPathName_U(PCWSTR dos_path,
                                              PUNICODE_STRING ntpath,
                                              PWSTR* file_part,
                                              CURDIR* cd)
diff -u -N -r -x '*~' -x '.#*' -x CVS files41/dos_fs.c files/dos_fs.c
--- files41/dos_fs.c	2004-01-23 22:19:48.000000000 +0100
+++ files/dos_fs.c	2004-01-23 22:21:09.000000000 +0100
@@ -1682,124 +1682,3 @@
 }
 
 
-/***********************************************************************
- *           QueryDosDeviceA   (KERNEL32.@)
- *
- * returns array of strings terminated by \0, terminated by \0
- */
-DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
-{
-    DWORD ret = 0, retW;
-    LPWSTR targetW = (LPWSTR)HeapAlloc(GetProcessHeap(),0,
-                                     bufsize * sizeof(WCHAR));
-    UNICODE_STRING devnameW;
-
-    if(devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
-    else devnameW.Buffer = NULL;
-
-    retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
-
-    ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target,
-                                        bufsize, NULL, NULL);
-
-    RtlFreeUnicodeString(&devnameW);
-    if (targetW) HeapFree(GetProcessHeap(),0,targetW);
-    return ret;
-}
-
-
-/***********************************************************************
- *           QueryDosDeviceW   (KERNEL32.@)
- *
- * returns array of strings terminated by \0, terminated by \0
- *
- * FIXME
- *      - Win9x returns for all calls ERROR_INVALID_PARAMETER 
- *	- the returned devices for devname == NULL is far from complete
- *      - its not checked that the returned device exist
- */
-DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
-{
-    const WCHAR *pDev, *pName, *pNum = NULL;
-    int    numsiz=0;
-    DWORD  ret;
-
-    TRACE("(%s,...)\n", debugstr_w(devname));
-    if (!devname) {
-	/* return known MSDOS devices */
-        DWORD ret = 0;
-	int i;
-        static const WCHAR devices[][5] = {{'A','U','X',0},
-                                           {'C','O','M','1',0},
-                                           {'C','O','M','2',0},
-                                           {'L','P','T','1',0},
-                                           {'N','U','L',0,}};
-	for(i=0; (i< (sizeof(devices)/sizeof(devices[0]))); i++) {
-	    DWORD len = strlenW(devices[i]);
-	    if(target && (bufsize >= ret + len + 2)) {
-		strcpyW(target+ret, devices[i]);
-                ret += len + 1;
-	    } else {
-                /* in this case WinXP returns 0 */
-                FIXME("function return is wrong for WinXP!\n");
-		SetLastError(ERROR_INSUFFICIENT_BUFFER);
-		break;
-	    }
-	}
-        /* append drives here */
-	if(target && bufsize > 0) target[ret++] = 0;
-	FIXME("Returned list is not complete\n");
-        return ret;
-    }
-    /* In theory all that are possible and have been defined.
-     * Now just those below, since mirc uses it to check for special files.
-     *
-     * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
-     *  but currently we just ignore that.)
-     */
-    if (!strcmpiW(devname, auxW)) {
-        pDev   = dosW;
-        pName  = comW;
-        numsiz = 1;
-        pNum   = oneW;
-    } else if (!strcmpiW(devname, nulW)) {
-        pDev  = devW;
-        pName = nullW;
-    } else if (!strncmpiW(devname, comW, strlenW(comW))) {
-        pDev  = devW;
-        pName = serW;
-        pNum  = devname + strlenW(comW);
-        for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
-        if(*(pNum + numsiz)) {
-	    SetLastError(ERROR_FILE_NOT_FOUND);
-	    return 0;
-	}
-    } else if (!strncmpiW(devname, lptW, strlenW(lptW))) {
-        pDev  = devW;
-        pName = parW;
-        pNum  = devname + strlenW(lptW);
-        for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
-        if(*(pNum + numsiz)) {
-	    SetLastError(ERROR_FILE_NOT_FOUND);
-	return 0;
-    }
-    } else {
-	/* This might be a DOS device we do not handle yet ... */
-	FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname));
-
-        /* Win9x set the error ERROR_INVALID_PARAMETER */
-	SetLastError(ERROR_FILE_NOT_FOUND);
-	return 0;
-}
-    FIXME("device %s may not exist on this computer\n", debugstr_w(devname));
-
-    ret = strlenW(pDev) + strlenW(pName) + numsiz + 2;
-    if (ret > bufsize) ret = 0;
-    if (target && ret) {
-        strcpyW(target,pDev);
-        strcatW(target,pName);
-        if (pNum) strcatW(target,pNum);
-        target[ret-1] = 0;
-    }
-    return ret;
-}
@@ -1317,90 +1317,6 @@
 
 
 /***********************************************************************
- *           DefineDosDeviceA       (KERNEL32.@)
- */
-BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath)
-{
-    UNICODE_STRING d, t;
-    BOOL           ret;
-
-    if (!RtlCreateUnicodeStringFromAsciiz(&d, devname))
-    {
-        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-        return FALSE;
-    }
-    if (!RtlCreateUnicodeStringFromAsciiz(&t, targetpath))
-    {
-        RtlFreeUnicodeString(&d);
-        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-        return FALSE;
-    }
-    ret = DefineDosDeviceW(flags, d.Buffer, t.Buffer);
-    RtlFreeUnicodeString(&d);
-    RtlFreeUnicodeString(&t);
-    return ret;
-}
-
-
-/***********************************************************************
- *           DefineDosDeviceA       (KERNEL32.@)
- */
-BOOL WINAPI DefineDosDeviceW(DWORD flags,LPCWSTR devname,LPCWSTR targetpath) 
-{
-    DOSDRIVE *old, *new;
-
-    /* this is a temporary hack for int21 support. better implementation has to be done */
-    if (flags != DDD_RAW_TARGET_PATH ||
-        !(toupperW(devname[0]) >= 'A' && toupperW(devname[0]) <= 'Z') ||
-        devname[1] != ':' || devname[2] != 0 ||
-        !(toupperW(targetpath[0]) >= 'A' && toupperW(targetpath[0]) <= 'Z') ||
-        targetpath[1] != ':' || targetpath[2] != '\\' || targetpath[3] != 0)
-    {
-        FIXME("(0x%08lx,%s,%s),stub!\n", flags, debugstr_w(devname), debugstr_w(targetpath));
-        SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-        return FALSE;
-    }
-
-    old = DOSDrives + devname[0] - 'A';
-    new = DOSDrives + targetpath[0] - 'A';
-
-    if (!old->root)
-    {
-        SetLastError( ERROR_INVALID_DRIVE );
-        return 0;
-    }
-
-    if ( new->root )
-    {
-        TRACE("Can't map drive %c: to already existing drive %c:\n",
-              devname[0], targetpath[0] );
-	/* it is already mapped there, so return success */
-	if (!strcmp(old->root,new->root))
-	    return 1;
-	return 0;
-    }
-
-    new->root     = heap_strdup( old->root );
-    new->dos_cwd  = HeapAlloc(GetProcessHeap(), 0, (strlenW(old->dos_cwd) + 1) * sizeof(WCHAR));
-    strcpyW(new->dos_cwd, old->dos_cwd);
-    new->unix_cwd = heap_strdup( old->unix_cwd );
-    new->device   = heap_strdup( old->device );
-    memcpy ( new->label_conf, old->label_conf, 12 );
-    memcpy ( new->label_read, old->label_read, 12 );
-    new->serial_conf = old->serial_conf;
-    new->type = old->type;
-    new->flags = old->flags;
-    new->dev = old->dev;
-    new->ino = old->ino;
-
-    TRACE("Drive %c: is now equal to drive %c:\n",
-          targetpath[0], devname[0] );
-
-    return 1;
-}
-
-
-/***********************************************************************
  *           DRIVE_GetFreeSpace
  */
 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
@@ -2178,11 +2094,3 @@
     return ret;
 }
 
-/***********************************************************************
- *           GetVolumeNameForVolumeMountPointW   (KERNEL32.@)
- */
-BOOL WINAPI GetVolumeNameForVolumeMountPointW(LPCWSTR str, LPWSTR dst, DWORD size)
-{
-    FIXME("(%s, %p, %lx): stub\n", debugstr_w(str), dst, size);
-    return 0;
-}
diff -u -N -r -x '*~' -x '.#*' -x CVS include41/winbase.h include/winbase.h
--- include41/winbase.h	2004-01-17 08:10:18.000000000 +0100
+++ include/winbase.h	2004-01-18 19:06:32.000000000 +0100
@@ -1522,7 +1522,7 @@
 BOOL        WINAPI SetThreadToken(PHANDLE,HANDLE);
 BOOL        WINAPI SetTimeZoneInformation(const LPTIME_ZONE_INFORMATION);
 BOOL        WINAPI SetVolumeMountPointA(LPCSTR,LPCSTR);
-BOOL        WINAPI SetVolumeMountPointW(LPCSTR,LPCSTR);
+BOOL        WINAPI SetVolumeMountPointW(LPCWSTR,LPCWSTR);
 #define     SetVolumeMountPoint WINELIB_NAME_AW(SetVolumeMountPoint)
 BOOL        WINAPI SetWaitableTimer(HANDLE,const LARGE_INTEGER*,LONG,PTIMERAPCROUTINE,LPVOID,BOOL);
 BOOL        WINAPI SetupComm(HANDLE,DWORD,DWORD);
diff -u -N -r -x '*~' -x '.#*' -x CVS include41/winternl.h include/winternl.h
--- include41/winternl.h	2004-01-01 09:54:11.000000000 +0100
+++ include/winternl.h	2004-01-18 19:07:41.000000000 +0100
@@ -1108,7 +1108,7 @@
 void      WINAPI RtlDestroyProcessParameters(RTL_USER_PROCESS_PARAMETERS*);
 DOS_PATHNAME_TYPE WINAPI RtlDetermineDosPathNameType_U(PCWSTR);
 BOOLEAN   WINAPI RtlDoesFileExists_U(LPCWSTR);
-BOOLEAN   WINAPI RtlDosPathNameToNtPathName_U(LPWSTR,PUNICODE_STRING,PWSTR*,CURDIR*);
+BOOLEAN   WINAPI RtlDosPathNameToNtPathName_U(LPCWSTR,PUNICODE_STRING,PWSTR*,CURDIR*);
 ULONG     WINAPI RtlDosSearchPath_U(LPCWSTR, LPCWSTR, LPCWSTR, ULONG, LPWSTR, LPWSTR*);
 WCHAR     WINAPI RtlDowncaseUnicodeChar(WCHAR);
 NTSTATUS  WINAPI RtlDowncaseUnicodeString(UNICODE_STRING*,const UNICODE_STRING*,BOOLEAN);


More information about the wine-patches mailing list