Fix a handle leak.
[reactos.git] / reactos / lib / kernel32 / file / volume.c
index 036057d..a7e07ae 100644 (file)
-/*
+/* $Id$
+ *
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS system libraries
  * FILE:            lib/kernel32/file/volume.c
  * PURPOSE:         File volume functions
  * PROGRAMMER:      Ariadne ( ariadne@xs4all.nl)
  *                  Erik Bos, Alexandre Julliard :
- *                      DRIVE_IsValid, GetLogicalDriveStringsA,
+ *                      GetLogicalDriveStringsA,
  *                      GetLogicalDriveStringsW, GetLogicalDrives
  * UPDATE HISTORY:
  *                  Created 01/11/98
  */
 //WINE copyright notice:
 /*
- * DOS drives handling functions 
+ * DOS drives handling functions
  *
  * Copyright 1993 Erik Bos
  * Copyright 1996 Alexandre Julliard
  */
 
-#include <windows.h>
-#include <ddk/ntddk.h>
-#include <wchar.h>
-#include <string.h>
+#include <k32.h>
 
 #define NDEBUG
-#include <kernel32/kernel32.h>
+#include "../include/debug.h"
 
 
 #define MAX_DOS_DRIVES 26
 
 
-int DRIVE_IsValid( int drive )
+static HANDLE
+InternalOpenDirW(LPCWSTR DirName,
+                BOOLEAN Write)
 {
-    char Drives[4];
-    Drives[0] = 'A';
-    Drives[1] = ':';
-    Drives[2] = '\\';
-    Drives[3] = 0;
-
-    Drives[0] = 'A' + drive -1;
-    if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
-    if ( CreateFileA(Drives,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS|FILE_ATTRIBUTE_DIRECTORY,NULL) == INVALID_HANDLE_VALUE ) {
-       return 0;
+  UNICODE_STRING NtPathU;
+  OBJECT_ATTRIBUTES ObjectAttributes;
+  NTSTATUS errCode;
+  IO_STATUS_BLOCK IoStatusBlock;
+  HANDLE hFile;
+
+  if (!RtlDosPathNameToNtPathName_U((LPWSTR)DirName,
+                                   &NtPathU,
+                                   NULL,
+                                   NULL))
+    {
+       DPRINT("Invalid path\n");
+       SetLastError(ERROR_BAD_PATHNAME);
+       return INVALID_HANDLE_VALUE;
     }
-    return drive;
-    
-}
 
+    InitializeObjectAttributes(&ObjectAttributes,
+                              &NtPathU,
+                              Write ? FILE_WRITE_ATTRIBUTES : FILE_READ_ATTRIBUTES,
+                              NULL,
+                              NULL);
+
+    errCode = NtCreateFile (&hFile,
+                           Write ? FILE_GENERIC_WRITE : FILE_GENERIC_READ,
+                           &ObjectAttributes,
+                           &IoStatusBlock,
+                           NULL,
+                           0,
+                           FILE_SHARE_READ|FILE_SHARE_WRITE,
+                           FILE_OPEN,
+                           0,
+                           NULL,
+                           0);
+
+    RtlFreeUnicodeString(&NtPathU);
 
-DWORD
-STDCALL
-GetLogicalDriveStringsA(
-                       DWORD nBufferLength,
-                       LPSTR lpBuffer
-                       )
-{
-    int drive, count;
-
-    for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
-        if (DRIVE_IsValid(drive)) count++;
-    if (count * 4 * sizeof(char) <= nBufferLength)
+    if (!NT_SUCCESS(errCode))
     {
-        LPSTR p = lpBuffer;
-        for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
-            if (DRIVE_IsValid(drive))
-            {
-                *p++ = 'A' + drive;
-                *p++ = ':';
-                *p++ = '\\';
-                *p++ = '\0';
-            }
-        *p = '\0';
+       SetLastErrorByStatus (errCode);
+       return INVALID_HANDLE_VALUE;
     }
-    return count * 4 * sizeof(char);
+    return hFile;
 }
 
 
-DWORD
-STDCALL
-GetLogicalDriveStringsW(
-    DWORD nBufferLength,
-    LPWSTR lpBuffer
-    )
+/*
+ * @implemented
+ */
+DWORD STDCALL
+GetLogicalDriveStringsA(DWORD nBufferLength,
+                       LPSTR lpBuffer)
 {
-    int drive, count;
+   DWORD drive, count;
+   DWORD dwDriveMap;
+
+   dwDriveMap = GetLogicalDrives();
+
+   for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
+     {
+       if (dwDriveMap & (1<<drive))
+          count++;
+     }
+
+
+   if (count * 4 * sizeof(char) <= nBufferLength)
+     {
+       LPSTR p = lpBuffer;
+
+       for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
+         if (dwDriveMap & (1<<drive))
+         {
+            *p++ = 'A' + drive;
+            *p++ = ':';
+            *p++ = '\\';
+            *p++ = '\0';
+         }
+       *p = '\0';
+     }
+    return (count * 4 * sizeof(char));
+}
+
+
+/*
+ * @implemented
+ */
+DWORD STDCALL
+GetLogicalDriveStringsW(DWORD nBufferLength,
+                       LPWSTR lpBuffer)
+{
+   DWORD drive, count;
+   DWORD dwDriveMap;
+
+   dwDriveMap = GetLogicalDrives();
+
+   for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
+     {
+       if (dwDriveMap & (1<<drive))
+          count++;
+     }
 
-    for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
-        if (DRIVE_IsValid(drive)) count++;
     if (count * 4 * sizeof(WCHAR) <=  nBufferLength)
     {
         LPWSTR p = lpBuffer;
         for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
-            if (DRIVE_IsValid(drive))
+            if (dwDriveMap & (1<<drive))
             {
                 *p++ = (WCHAR)('A' + drive);
                 *p++ = (WCHAR)':';
@@ -100,49 +145,69 @@ GetLogicalDriveStringsW(
             }
         *p = (WCHAR)'\0';
     }
-    return count * 4 * sizeof(WCHAR);
+    return (count * 4 * sizeof(WCHAR));
 }
 
 
-DWORD
-STDCALL
+/*
+ * @implemented
+ */
+DWORD STDCALL
 GetLogicalDrives(VOID)
 {
-    DWORD ret = 0;
-    int drive;
+       NTSTATUS Status;
+       PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo;
+
+       /* Get the Device Map for this Process */
+       Status = NtQueryInformationProcess(NtCurrentProcess(),
+                                          ProcessDeviceMap,
+                                          &ProcessDeviceMapInfo,
+                                          sizeof(ProcessDeviceMapInfo),
+                                          NULL);
+
+       /* Return the Drive Map */
+       if (!NT_SUCCESS(Status))
+       {
+               SetLastErrorByStatus(Status);
+               return 0;
+       }
 
-    for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
-        if (DRIVE_IsValid(drive)) ret |= (1 << drive);
-    return ret;
+        return ProcessDeviceMapInfo.Query.DriveMap;
 }
 
 
-
-WINBOOL
-STDCALL
-GetDiskFreeSpaceA(
-    LPCSTR lpRootPathName,
-    LPDWORD lpSectorsPerCluster,
-    LPDWORD lpBytesPerSector,
-    LPDWORD lpNumberOfFreeClusters,
-    LPDWORD lpTotalNumberOfClusters
-    )
+/*
+ * @implemented
+ */
+BOOL STDCALL
+GetDiskFreeSpaceA (
+       LPCSTR  lpRootPathName,
+       LPDWORD lpSectorsPerCluster,
+       LPDWORD lpBytesPerSector,
+       LPDWORD lpNumberOfFreeClusters,
+       LPDWORD lpTotalNumberOfClusters
+       )
 {
-       ULONG i;
-       WCHAR RootPathNameW[MAX_PATH];
-       i = 0;
-       while ((*lpRootPathName)!=0 && i < MAX_PATH)
-       {
-               RootPathNameW[i] = *lpRootPathName;
-               lpRootPathName++;
-               i++;
-       }
-       RootPathNameW[i] = 0;
-       return GetDiskFreeSpaceW(RootPathNameW,lpSectorsPerCluster, lpBytesPerSector, lpNumberOfFreeClusters, lpTotalNumberOfClusters );
+   PWCHAR RootPathNameW=NULL;
+
+   if (lpRootPathName)
+   {
+      if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE)))
+         return FALSE;
+   }
+
+       return GetDiskFreeSpaceW (RootPathNameW,
+                                   lpSectorsPerCluster,
+                                   lpBytesPerSector,
+                                   lpNumberOfFreeClusters,
+                                   lpTotalNumberOfClusters);
 }
 
-WINBOOL
-STDCALL
+
+/*
+ * @implemented
+ */
+BOOL STDCALL
 GetDiskFreeSpaceW(
     LPCWSTR lpRootPathName,
     LPDWORD lpSectorsPerCluster,
@@ -153,16 +218,26 @@ GetDiskFreeSpaceW(
 {
     FILE_FS_SIZE_INFORMATION FileFsSize;
     IO_STATUS_BLOCK IoStatusBlock;
+    WCHAR RootPathName[MAX_PATH];
     HANDLE hFile;
     NTSTATUS errCode;
-       
-    hFile = CreateFileW(lpRootPathName,
-                        FILE_READ_ATTRIBUTES,
-                        FILE_SHARE_READ,
-                        NULL,
-                        OPEN_EXISTING,
-                        FILE_ATTRIBUTE_NORMAL,
-                        NULL);
+
+    if (lpRootPathName)
+    {
+        wcsncpy (RootPathName, lpRootPathName, 3);
+    }
+    else
+    {
+        GetCurrentDirectoryW (MAX_PATH, RootPathName);
+    }
+    RootPathName[3] = 0;
+
+  hFile = InternalOpenDirW(RootPathName, FALSE);
+  if (INVALID_HANDLE_VALUE == hFile)
+    {
+      SetLastError(ERROR_PATH_NOT_FOUND);
+      return FALSE;
+    }
 
     errCode = NtQueryVolumeInformationFile(hFile,
                                            &IoStatusBlock,
@@ -172,49 +247,52 @@ GetDiskFreeSpaceW(
     if (!NT_SUCCESS(errCode))
     {
         CloseHandle(hFile);
-        SetLastError(RtlNtStatusToDosError(errCode));
+        SetLastErrorByStatus (errCode);
         return FALSE;
     }
 
     *lpBytesPerSector = FileFsSize.BytesPerSector;
     *lpSectorsPerCluster = FileFsSize.SectorsPerAllocationUnit;
-    *lpNumberOfFreeClusters = FileFsSize.AvailableAllocationUnits.LowPart;
-    *lpTotalNumberOfClusters = FileFsSize.TotalAllocationUnits.LowPart;
+    *lpNumberOfFreeClusters = FileFsSize.AvailableAllocationUnits.u.LowPart;
+    *lpTotalNumberOfClusters = FileFsSize.TotalAllocationUnits.u.LowPart;
     CloseHandle(hFile);
+
     return TRUE;
 }
 
-WINBOOL
-STDCALL
-GetDiskFreeSpaceExA(
-    LPCSTR lpDirectoryName,
-    PULARGE_INTEGER lpFreeBytesAvailableToCaller,
-    PULARGE_INTEGER lpTotalNumberOfBytes,
-    PULARGE_INTEGER lpTotalNumberOfFreeBytes
-    )
+
+/*
+ * @implemented
+ */
+BOOL STDCALL
+GetDiskFreeSpaceExA (
+       LPCSTR lpDirectoryName   OPTIONAL,
+       PULARGE_INTEGER lpFreeBytesAvailableToCaller,
+       PULARGE_INTEGER lpTotalNumberOfBytes,
+       PULARGE_INTEGER lpTotalNumberOfFreeBytes
+       )
 {
-    WCHAR DirectoryNameW[MAX_PATH];
-    ULONG i;
+   PWCHAR DirectoryNameW=NULL;
 
-    i = 0;
-    while ((*lpDirectoryName)!=0 && i < MAX_PATH)
-    {
-        DirectoryNameW[i] = *lpDirectoryName;
-        lpDirectoryName++;
-        i++;
-    }
-    DirectoryNameW[i] = 0;
-    return GetDiskFreeSpaceExW(DirectoryNameW,
-                               lpFreeBytesAvailableToCaller,
-                               lpTotalNumberOfBytes,
-                               lpTotalNumberOfFreeBytes);
+       if (lpDirectoryName)
+       {
+      if (!(DirectoryNameW = FilenameA2W(lpDirectoryName, FALSE)))
+         return FALSE;
+       }
+
+   return GetDiskFreeSpaceExW (DirectoryNameW ,
+                                     lpFreeBytesAvailableToCaller,
+                                     lpTotalNumberOfBytes,
+                                     lpTotalNumberOfFreeBytes);
 }
 
 
-WINBOOL
-STDCALL
+/*
+ * @implemented
+ */
+BOOL STDCALL
 GetDiskFreeSpaceExW(
-    LPCWSTR lpDirectoryName,
+    LPCWSTR lpDirectoryName OPTIONAL,
     PULARGE_INTEGER lpFreeBytesAvailableToCaller,
     PULARGE_INTEGER lpTotalNumberOfBytes,
     PULARGE_INTEGER lpTotalNumberOfFreeBytes
@@ -222,20 +300,30 @@ GetDiskFreeSpaceExW(
 {
     FILE_FS_SIZE_INFORMATION FileFsSize;
     IO_STATUS_BLOCK IoStatusBlock;
+    ULARGE_INTEGER BytesPerCluster;
+    WCHAR RootPathName[MAX_PATH];
     HANDLE hFile;
     NTSTATUS errCode;
-    WCHAR RootPath[4];
-    ULARGE_INTEGER BytesPerCluster;
 
-    wcsncpy (RootPath, lpDirectoryName, 3);
-       
-    hFile = CreateFileW(RootPath,
-                        FILE_READ_ATTRIBUTES,
-                        FILE_SHARE_READ,
-                        NULL,
-                        OPEN_EXISTING,
-                        FILE_ATTRIBUTE_NORMAL,
-                        NULL);
+    /*
+    FIXME: this is obviously wrong for UNC paths, symbolic directories etc.
+    -Gunnar
+    */
+    if (lpDirectoryName)
+    {
+        wcsncpy (RootPathName, lpDirectoryName, 3);
+    }
+    else
+    {
+        GetCurrentDirectoryW (MAX_PATH, RootPathName);
+    }
+    RootPathName[3] = 0;
+
+    hFile = InternalOpenDirW(RootPathName, FALSE);
+    if (INVALID_HANDLE_VALUE == hFile)
+    {
+        return FALSE;
+    }
 
     errCode = NtQueryVolumeInformationFile(hFile,
                                            &IoStatusBlock,
@@ -245,7 +333,7 @@ GetDiskFreeSpaceExW(
     if (!NT_SUCCESS(errCode))
     {
         CloseHandle(hFile);
-        SetLastError(RtlNtStatusToDosError(errCode));
+        SetLastErrorByStatus (errCode);
         return FALSE;
     }
 
@@ -253,43 +341,43 @@ GetDiskFreeSpaceExW(
         FileFsSize.BytesPerSector * FileFsSize.SectorsPerAllocationUnit;
 
     // FIXME: Use quota information
-    lpFreeBytesAvailableToCaller->QuadPart =
-        BytesPerCluster.QuadPart * FileFsSize.AvailableAllocationUnits.QuadPart;
+       if (lpFreeBytesAvailableToCaller)
+        lpFreeBytesAvailableToCaller->QuadPart =
+            BytesPerCluster.QuadPart * FileFsSize.AvailableAllocationUnits.QuadPart;
 
-    lpTotalNumberOfBytes->QuadPart =
-        BytesPerCluster.QuadPart * FileFsSize.TotalAllocationUnits.LowPart;
-    lpTotalNumberOfFreeBytes->QuadPart =
-        BytesPerCluster.QuadPart * FileFsSize.AvailableAllocationUnits.QuadPart;
+       if (lpTotalNumberOfBytes)
+        lpTotalNumberOfBytes->QuadPart =
+            BytesPerCluster.QuadPart * FileFsSize.TotalAllocationUnits.QuadPart;
+       if (lpTotalNumberOfFreeBytes)
+        lpTotalNumberOfFreeBytes->QuadPart =
+            BytesPerCluster.QuadPart * FileFsSize.AvailableAllocationUnits.QuadPart;
 
     CloseHandle(hFile);
+
     return TRUE;
 }
 
 
-UINT
-STDCALL
-GetDriveTypeA(
-    LPCSTR lpRootPathName
-    )
+/*
+ * @implemented
+ */
+UINT STDCALL
+GetDriveTypeA(LPCSTR lpRootPathName)
 {
-       ULONG i;
-       WCHAR RootPathNameW[MAX_PATH];
-       i = 0;
-       while ((*lpRootPathName)!=0 && i < MAX_PATH)
-       {
-               RootPathNameW[i] = *lpRootPathName;
-               lpRootPathName++;
-               i++;
-       }
-       RootPathNameW[i] = 0;
-       return GetDriveTypeW(RootPathNameW);
+   PWCHAR RootPathNameW;
+
+   if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE)))
+      return DRIVE_UNKNOWN;
+
+   return GetDriveTypeW(RootPathNameW);
 }
 
-UINT
-STDCALL
-GetDriveTypeW(
-    LPCWSTR lpRootPathName
-    )
+
+/*
+ * @implemented
+ */
+UINT STDCALL
+GetDriveTypeW(LPCWSTR lpRootPathName)
 {
        FILE_FS_DEVICE_INFORMATION FileFsDevice;
        IO_STATUS_BLOCK IoStatusBlock;
@@ -297,87 +385,192 @@ GetDriveTypeW(
        HANDLE hFile;
        NTSTATUS errCode;
 
-       hFile = CreateFileW(
-               lpRootPathName, 
-               GENERIC_ALL,    
-               FILE_SHARE_READ|FILE_SHARE_WRITE,       
-               NULL,   
-               OPEN_EXISTING,  
-               FILE_ATTRIBUTE_NORMAL,  
-               NULL 
-       );
-
-       errCode = NtQueryVolumeInformationFile(hFile,&IoStatusBlock,&FileFsDevice, sizeof(FILE_FS_DEVICE_INFORMATION),FileFsDeviceInformation);
-       if ( !NT_SUCCESS(errCode) ) {
+       hFile = InternalOpenDirW(lpRootPathName, FALSE);
+       if (hFile == INVALID_HANDLE_VALUE)
+       {
+           return DRIVE_NO_ROOT_DIR;   /* According to WINE regression tests */
+       }
+
+       errCode = NtQueryVolumeInformationFile (hFile,
+                                               &IoStatusBlock,
+                                               &FileFsDevice,
+                                               sizeof(FILE_FS_DEVICE_INFORMATION),
+                                               FileFsDeviceInformation);
+       if (!NT_SUCCESS(errCode))
+       {
                CloseHandle(hFile);
-               SetLastError(RtlNtStatusToDosError(errCode));
+               SetLastErrorByStatus (errCode);
                return 0;
        }
        CloseHandle(hFile);
-       return (UINT)FileFsDevice.DeviceType;
-
-       
 
+        switch (FileFsDevice.DeviceType)
+        {
+               case FILE_DEVICE_CD_ROM:
+               case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
+                       return DRIVE_CDROM;
+               case FILE_DEVICE_VIRTUAL_DISK:
+                       return DRIVE_RAMDISK;
+               case FILE_DEVICE_NETWORK_FILE_SYSTEM:
+                       return DRIVE_REMOTE;
+               case FILE_DEVICE_DISK:
+               case FILE_DEVICE_DISK_FILE_SYSTEM:
+                       if (FileFsDevice.Characteristics & FILE_REMOTE_DEVICE)
+                               return DRIVE_REMOTE;
+                       if (FileFsDevice.Characteristics & FILE_REMOVABLE_MEDIA)
+                               return DRIVE_REMOVABLE;
+                       return DRIVE_FIXED;
+        }
+
+        DPRINT1("Returning DRIVE_UNKNOWN for device type %d\n", FileFsDevice.DeviceType);
+
+       return DRIVE_UNKNOWN;
 }
 
-WINBOOL
-STDCALL
+
+/*
+ * @implemented
+ */
+BOOL STDCALL
 GetVolumeInformationA(
-    LPCSTR lpRootPathName,
-    LPSTR lpVolumeNameBuffer,
-    DWORD nVolumeNameSize,
-    LPDWORD lpVolumeSerialNumber,
-    LPDWORD lpMaximumComponentLength,
-    LPDWORD lpFileSystemFlags,
-    LPSTR lpFileSystemNameBuffer,
-    DWORD nFileSystemNameSize
-    )
+       LPCSTR  lpRootPathName,
+       LPSTR   lpVolumeNameBuffer,
+       DWORD   nVolumeNameSize,
+       LPDWORD lpVolumeSerialNumber,
+       LPDWORD lpMaximumComponentLength,
+       LPDWORD lpFileSystemFlags,
+       LPSTR   lpFileSystemNameBuffer,
+       DWORD   nFileSystemNameSize
+       )
 {
-       ULONG i;
-       WCHAR RootPathNameW[MAX_PATH];
-       WCHAR VolumeNameBufferW[MAX_PATH];
-       WCHAR FileSystemNameBufferW[MAX_PATH];
-       
-
-       i = 0;
-       while ((*lpRootPathName)!=0 && i < MAX_PATH)
-       {
-               RootPathNameW[i] = *lpRootPathName;
-               lpRootPathName++;
-               i++;
-       }
-       RootPathNameW[i] = 0;
-
-       if ( GetVolumeInformationW(RootPathNameW,
-               VolumeNameBufferW,
-               nVolumeNameSize,
-               lpVolumeSerialNumber,
-               lpMaximumComponentLength,
-               lpFileSystemFlags,
-               FileSystemNameBufferW,
-               nFileSystemNameSize ) ) {
-               for(i=0;i<nVolumeNameSize;i++)
-                       lpVolumeNameBuffer[i] = (CHAR)VolumeNameBufferW[i];
-
-               for(i=0;i<nFileSystemNameSize;i++)
-                       lpFileSystemNameBuffer[i] = (CHAR)FileSystemNameBufferW[i];
-               
-               return TRUE;
-       }
-       return FALSE;
+  UNICODE_STRING FileSystemNameU;
+  UNICODE_STRING VolumeNameU = {0};
+  ANSI_STRING VolumeName;
+  ANSI_STRING FileSystemName;
+  PWCHAR RootPathNameW;
+  BOOL Result;
 
-}
+  if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE)))
+     return FALSE;
+
+  if (lpVolumeNameBuffer)
+    {
+      VolumeNameU.MaximumLength = nVolumeNameSize * sizeof(WCHAR);
+      VolumeNameU.Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
+                                           0,
+                                           VolumeNameU.MaximumLength);
+      if (VolumeNameU.Buffer == NULL)
+      {
+          goto FailNoMem;
+      }
+    }
+
+  if (lpFileSystemNameBuffer)
+    {
+      FileSystemNameU.Length = 0;
+      FileSystemNameU.MaximumLength = nFileSystemNameSize * sizeof(WCHAR);
+      FileSystemNameU.Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
+                                               0,
+                                               FileSystemNameU.MaximumLength);
+      if (FileSystemNameU.Buffer == NULL)
+      {
+          if (VolumeNameU.Buffer != NULL)
+          {
+              RtlFreeHeap(RtlGetProcessHeap(),
+                          0,
+                          VolumeNameU.Buffer);
+          }
+
+FailNoMem:
+          SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+          return FALSE;
+      }
+    }
+
+  Result = GetVolumeInformationW (RootPathNameW,
+                                 lpVolumeNameBuffer ? VolumeNameU.Buffer : NULL,
+                                 nVolumeNameSize,
+                                 lpVolumeSerialNumber,
+                                 lpMaximumComponentLength,
+                                 lpFileSystemFlags,
+                                 lpFileSystemNameBuffer ? FileSystemNameU.Buffer : NULL,
+                                 nFileSystemNameSize);
 
+  if (Result)
+    {
+      if (lpVolumeNameBuffer)
+        {
+          VolumeNameU.Length = wcslen(VolumeNameU.Buffer) * sizeof(WCHAR);
+         VolumeName.Length = 0;
+         VolumeName.MaximumLength = nVolumeNameSize;
+         VolumeName.Buffer = lpVolumeNameBuffer;
+       }
 
+      if (lpFileSystemNameBuffer)
+       {
+         FileSystemNameU.Length = wcslen(FileSystemNameU.Buffer) * sizeof(WCHAR);
+         FileSystemName.Length = 0;
+         FileSystemName.MaximumLength = nFileSystemNameSize;
+         FileSystemName.Buffer = lpFileSystemNameBuffer;
+       }
 
+      /* convert unicode strings to ansi (or oem) */
+      if (bIsFileApiAnsi)
+        {
+         if (lpVolumeNameBuffer)
+           {
+             RtlUnicodeStringToAnsiString (&VolumeName,
+                                           &VolumeNameU,
+                                           FALSE);
+           }
+         if (lpFileSystemNameBuffer)
+           {
+             RtlUnicodeStringToAnsiString (&FileSystemName,
+                                           &FileSystemNameU,
+                                           FALSE);
+           }
+       }
+      else
+        {
+         if (lpVolumeNameBuffer)
+           {
+             RtlUnicodeStringToOemString (&VolumeName,
+                                          &VolumeNameU,
+                                          FALSE);
+           }
+          if (lpFileSystemNameBuffer)
+           {
+             RtlUnicodeStringToOemString (&FileSystemName,
+                                          &FileSystemNameU,
+                                          FALSE);
+           }
+       }
+    }
+
+  if (lpVolumeNameBuffer)
+    {
+      RtlFreeHeap (RtlGetProcessHeap (),
+                  0,
+                  VolumeNameU.Buffer);
+    }
+  if (lpFileSystemNameBuffer)
+    {
+      RtlFreeHeap (RtlGetProcessHeap (),
+                  0,
+                  FileSystemNameU.Buffer);
+    }
 
-#define FS_VOLUME_BUFFER_SIZE (MAX_PATH + sizeof(FILE_FS_VOLUME_INFORMATION))
+  return Result;
+}
 
-#define FS_ATTRIBUTE_BUFFER_SIZE (MAX_PATH + sizeof(FILE_FS_ATTRIBUTE_INFORMATION))
+#define FS_VOLUME_BUFFER_SIZE (MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_VOLUME_INFORMATION))
 
+#define FS_ATTRIBUTE_BUFFER_SIZE (MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_ATTRIBUTE_INFORMATION))
 
-WINBOOL
-STDCALL
+/*
+ * @implemented
+ */
+BOOL STDCALL
 GetVolumeInformationW(
     LPCWSTR lpRootPathName,
     LPWSTR lpVolumeNameBuffer,
@@ -389,104 +582,431 @@ GetVolumeInformationW(
     DWORD nFileSystemNameSize
     )
 {
-        PFILE_FS_VOLUME_INFORMATION FileFsVolume;
-        PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute;
-       IO_STATUS_BLOCK IoStatusBlock;
-       USHORT Buffer[FS_VOLUME_BUFFER_SIZE];
-       USHORT Buffer2[FS_ATTRIBUTE_BUFFER_SIZE];
-
-       HANDLE hFile;
-       NTSTATUS errCode;
-
-        FileFsVolume = (PFILE_FS_VOLUME_INFORMATION)Buffer;
-        FileFsAttribute = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer2;
-
-        DPRINT("FileFsVolume %p\n", FileFsVolume);
-        DPRINT("FileFsAttribute %p\n", FileFsAttribute);
-
-        hFile = CreateFileW(lpRootPathName,
-                            FILE_READ_ATTRIBUTES,
-                            FILE_SHARE_READ|FILE_SHARE_WRITE,
-                            NULL,
-                            OPEN_EXISTING,
-                            FILE_ATTRIBUTE_NORMAL,
-                            NULL);
-
-        DPRINT("hFile: %x\n", hFile);
-        errCode = NtQueryVolumeInformationFile(hFile,
-                                               &IoStatusBlock,
-                                               FileFsVolume,
-                                               FS_VOLUME_BUFFER_SIZE,
-                                               FileFsVolumeInformation);
-       if ( !NT_SUCCESS(errCode) ) {
-                DPRINT("Status: %x\n", errCode);
-                CloseHandle(hFile);
-               SetLastError(RtlNtStatusToDosError(errCode));
-               return FALSE;
-       }
+  PFILE_FS_VOLUME_INFORMATION FileFsVolume;
+  PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute;
+  IO_STATUS_BLOCK IoStatusBlock;
+  WCHAR RootPathName[MAX_PATH];
+  UCHAR Buffer[max(FS_VOLUME_BUFFER_SIZE, FS_ATTRIBUTE_BUFFER_SIZE)];
+
+  HANDLE hFile;
+  NTSTATUS errCode;
+
+  FileFsVolume = (PFILE_FS_VOLUME_INFORMATION)Buffer;
+  FileFsAttribute = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer;
+
+  DPRINT("FileFsVolume %p\n", FileFsVolume);
+  DPRINT("FileFsAttribute %p\n", FileFsAttribute);
+
+  if (!lpRootPathName || !wcscmp(lpRootPathName, L""))
+  {
+      GetCurrentDirectoryW (MAX_PATH, RootPathName);
+  }
+  else
+  {
+      wcsncpy (RootPathName, lpRootPathName, 3);
+  }
+  RootPathName[3] = 0;
+
+  hFile = InternalOpenDirW(RootPathName, FALSE);
+  if (hFile == INVALID_HANDLE_VALUE)
+    {
+      return FALSE;
+    }
 
-        if (lpVolumeSerialNumber)
-                *lpVolumeSerialNumber = FileFsVolume->VolumeSerialNumber;
+  DPRINT("hFile: %x\n", hFile);
+  errCode = NtQueryVolumeInformationFile(hFile,
+                                         &IoStatusBlock,
+                                         FileFsVolume,
+                                         FS_VOLUME_BUFFER_SIZE,
+                                         FileFsVolumeInformation);
+  if ( !NT_SUCCESS(errCode) )
+    {
+      DPRINT("Status: %x\n", errCode);
+      CloseHandle(hFile);
+      SetLastErrorByStatus (errCode);
+      return FALSE;
+    }
 
-        if (lpVolumeNameBuffer)
-                wcsncpy(lpVolumeNameBuffer, FileFsVolume->VolumeLabel,min(nVolumeNameSize,MAX_PATH));
+  if (lpVolumeSerialNumber)
+    *lpVolumeSerialNumber = FileFsVolume->VolumeSerialNumber;
 
-       errCode = NtQueryVolumeInformationFile(hFile,&IoStatusBlock,FileFsAttribute, FS_ATTRIBUTE_BUFFER_SIZE,FileFsAttributeInformation);
-       if ( !NT_SUCCESS(errCode) ) {
-                DPRINT("Status: %x\n", errCode);
-               CloseHandle(hFile);
-               SetLastError(RtlNtStatusToDosError(errCode));
-               return FALSE;
+  if (lpVolumeNameBuffer)
+    {
+      if (nVolumeNameSize * sizeof(WCHAR) >= FileFsVolume->VolumeLabelLength + sizeof(WCHAR))
+        {
+         memcpy(lpVolumeNameBuffer,
+                FileFsVolume->VolumeLabel,
+                FileFsVolume->VolumeLabelLength);
+         lpVolumeNameBuffer[FileFsVolume->VolumeLabelLength / sizeof(WCHAR)] = 0;
+       }
+      else
+        {
+         CloseHandle(hFile);
+         SetLastError(ERROR_MORE_DATA);
+         return FALSE;
        }
+    }
 
-        if (lpFileSystemFlags)
-                *lpFileSystemFlags = FileFsAttribute->FileSystemAttributes;
-        if (lpMaximumComponentLength)
-                *lpMaximumComponentLength = FileFsAttribute->MaximumComponentNameLength;
-        if (lpFileSystemNameBuffer)
-                wcsncpy(lpFileSystemNameBuffer, FileFsAttribute->FileSystemName,min(nFileSystemNameSize,MAX_PATH));
+  errCode = NtQueryVolumeInformationFile (hFile,
+                                         &IoStatusBlock,
+                                         FileFsAttribute,
+                                         FS_ATTRIBUTE_BUFFER_SIZE,
+                                         FileFsAttributeInformation);
+  CloseHandle(hFile);
+  if (!NT_SUCCESS(errCode))
+    {
+      DPRINT("Status: %x\n", errCode);
+      SetLastErrorByStatus (errCode);
+      return FALSE;
+    }
 
-       CloseHandle(hFile);
-       return TRUE;
+  if (lpFileSystemFlags)
+    *lpFileSystemFlags = FileFsAttribute->FileSystemAttributes;
+  if (lpMaximumComponentLength)
+    *lpMaximumComponentLength = FileFsAttribute->MaximumComponentNameLength;
+  if (lpFileSystemNameBuffer)
+    {
+      if (nFileSystemNameSize * sizeof(WCHAR) >= FileFsAttribute->FileSystemNameLength + sizeof(WCHAR))
+        {
+         memcpy(lpFileSystemNameBuffer,
+                FileFsAttribute->FileSystemName,
+                FileFsAttribute->FileSystemNameLength);
+         lpFileSystemNameBuffer[FileFsAttribute->FileSystemNameLength / sizeof(WCHAR)] = 0;
+       }
+      else
+        {
+         SetLastError(ERROR_MORE_DATA);
+         return FALSE;
+       }
+    }
+  return TRUE;
 }
 
-WINBOOL
+
+/*
+ * @implemented
+ */
+BOOL
 STDCALL
-SetVolumeLabelA(
-    LPCSTR lpRootPathName,
-    LPCSTR lpVolumeName
-    )
+SetVolumeLabelA (
+       LPCSTR  lpRootPathName,
+       LPCSTR  lpVolumeName /* NULL if deleting label */
+       )
 {
-       WCHAR RootPathNameW[MAX_PATH];
-       WCHAR VolumeNameW[MAX_PATH];
-       UINT i;
-
-       i = 0;
-       while ((*lpRootPathName)!=0 && i < MAX_PATH)
-       {
-               RootPathNameW[i] = *lpRootPathName;
-               lpRootPathName++;
-               i++;
-       }
-       RootPathNameW[i] = 0;
-
-       i = 0;
-       while ((*lpVolumeName)!=0 && i < MAX_PATH)
-       {
-               VolumeNameW[i] = *lpVolumeName;
-               lpVolumeName++;
-               i++;
-       }
-       VolumeNameW[i] = 0;
-       return SetVolumeLabelW(RootPathNameW,VolumeNameW);
+       PWCHAR RootPathNameW;
+   PWCHAR VolumeNameW = NULL;
+       BOOL Result;
+
+   if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE)))
+      return FALSE;
+
+   if (lpVolumeName)
+   {
+      if (!(VolumeNameW = FilenameA2W(lpVolumeName, TRUE)))
+         return FALSE;
+   }
+
+   Result = SetVolumeLabelW (RootPathNameW,
+                             VolumeNameW);
+
+   if (VolumeNameW)
+   {
+          RtlFreeHeap (RtlGetProcessHeap (),
+                       0,
+                   VolumeNameW );
+   }
+
+       return Result;
 }
 
-WINBOOL
-STDCALL
+
+/*
+ * @implemented
+ */
+BOOL STDCALL
 SetVolumeLabelW(
-    LPCWSTR lpRootPathName,
-    LPCWSTR lpVolumeName
-    )
+   LPCWSTR lpRootPathName,
+   LPCWSTR lpVolumeName /* NULL if deleting label */
+   )
 {
+   PFILE_FS_LABEL_INFORMATION LabelInfo;
+   IO_STATUS_BLOCK IoStatusBlock;
+   ULONG LabelLength;
+   HANDLE hFile;
+   NTSTATUS Status;
+
+   LabelLength = wcslen(lpVolumeName) * sizeof(WCHAR);
+   LabelInfo = RtlAllocateHeap(RtlGetProcessHeap(),
+                              0,
+                              sizeof(FILE_FS_LABEL_INFORMATION) +
+                              LabelLength);
+   if (LabelInfo == NULL)
+   {
+       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+       return FALSE;
+   }
+   LabelInfo->VolumeLabelLength = LabelLength;
+   memcpy(LabelInfo->VolumeLabel,
+         lpVolumeName,
+         LabelLength);
+
+   hFile = InternalOpenDirW(lpRootPathName, TRUE);
+   if (INVALID_HANDLE_VALUE == hFile)
+   {
+        RtlFreeHeap(RtlGetProcessHeap(),
+                   0,
+                   LabelInfo);
+        return FALSE;
+   }
+
+   Status = NtSetVolumeInformationFile(hFile,
+                                      &IoStatusBlock,
+                                      LabelInfo,
+                                      sizeof(FILE_FS_LABEL_INFORMATION) +
+                                      LabelLength,
+                                      FileFsLabelInformation);
+
+   RtlFreeHeap(RtlGetProcessHeap(),
+              0,
+              LabelInfo);
+
+   if (!NT_SUCCESS(Status))
+     {
+       DPRINT("Status: %x\n", Status);
+       CloseHandle(hFile);
+       SetLastErrorByStatus(Status);
        return FALSE;
+     }
+
+   CloseHandle(hFile);
+   return TRUE;
+}
+
+/**
+ * @name GetVolumeNameForVolumeMountPointW
+ *
+ * Return an unique volume name for a drive root or mount point.
+ *
+ * @param VolumeMountPoint
+ *        Pointer to string that contains either root drive name or
+ *        mount point name.
+ * @param VolumeName
+ *        Pointer to buffer that is filled with resulting unique
+ *        volume name on success.
+ * @param VolumeNameLength
+ *        Size of VolumeName buffer in TCHARs.
+ *
+ * @return
+ *     TRUE when the function succeeds and the VolumeName buffer is filled,
+ *     FALSE otherwise.
+ */
+
+BOOL WINAPI
+GetVolumeNameForVolumeMountPointW(
+   IN LPCWSTR VolumeMountPoint,
+   OUT LPWSTR VolumeName,
+   IN DWORD VolumeNameLength)
+{
+   UNICODE_STRING NtFileName;
+   OBJECT_ATTRIBUTES ObjectAttributes;
+   HANDLE FileHandle;
+   IO_STATUS_BLOCK Iosb;
+   ULONG BufferLength;
+   PMOUNTDEV_NAME MountDevName;
+   PMOUNTMGR_MOUNT_POINT MountPoint;
+   ULONG MountPointSize;
+   PMOUNTMGR_MOUNT_POINTS MountPoints;
+   ULONG Index;
+   PUCHAR SymbolicLinkName;
+   BOOL Result;
+   NTSTATUS Status;
+
+   /*
+    * First step is to convert the passed volume mount point name to
+    * an NT acceptable name.
+    */
+
+   if (!RtlDosPathNameToNtPathName_U(VolumeName, &NtFileName, NULL, NULL))
+   {
+      SetLastError(ERROR_PATH_NOT_FOUND);
+      return FALSE;
+   }
+
+   if (NtFileName.Length > sizeof(WCHAR) &&
+       NtFileName.Buffer[(NtFileName.Length / sizeof(WCHAR)) - 1] == '\\')
+   {
+      NtFileName.Length -= sizeof(WCHAR);
+   }
+
+   /*
+    * Query mount point device name which we will later use for determining
+    * the volume name.
+    */
+
+   InitializeObjectAttributes(&ObjectAttributes, &NtFileName, 0, NULL, NULL);
+   Status = NtOpenFile(&FileHandle, FILE_READ_ATTRIBUTES | SYNCHRONIZE,
+                       &ObjectAttributes, &Iosb,
+                       FILE_SHARE_READ | FILE_SHARE_WRITE,
+                       FILE_SYNCHRONOUS_IO_NONALERT);
+   RtlFreeUnicodeString(&NtFileName);
+   if (!NT_SUCCESS(Status))
+   {
+      SetLastErrorByStatus(Status);
+      return FALSE;
+   }
+
+   BufferLength = sizeof(MOUNTDEV_NAME) + 50 * sizeof(WCHAR);
+   do
+   {
+      MountDevName = RtlAllocateHeap(GetProcessHeap(), 0, BufferLength);
+      if (MountDevName == NULL)
+      {
+         NtClose(FileHandle);
+         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+         return FALSE;
+      }
+
+      Status = NtDeviceIoControlFile(FileHandle, NULL, NULL, NULL, &Iosb,
+                                     IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
+                                     NULL, 0, MountDevName, BufferLength);
+      if (!NT_SUCCESS(Status))
+      {
+         RtlFreeHeap(GetProcessHeap(), 0, MountDevName);
+         if (Status == STATUS_BUFFER_OVERFLOW)
+         {
+            BufferLength = sizeof(MOUNTDEV_NAME) + MountDevName->NameLength;
+            continue;
+         }
+         else 
+         {
+            NtClose(FileHandle);
+            SetLastErrorByStatus(Status);
+            return FALSE;
+         }
+      }
+   }
+   while (!NT_SUCCESS(Status));
+
+   NtClose(FileHandle);
+
+   /*
+    * Get the mount point information from mount manager.
+    */
+
+   MountPointSize = MountDevName->NameLength + sizeof(MOUNTMGR_MOUNT_POINT);
+   MountPoint = RtlAllocateHeap(GetProcessHeap(), 0, MountPointSize);
+   if (MountPoint == NULL)
+   {
+      RtlFreeHeap(GetProcessHeap(), 0, MountDevName);
+      SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+      return FALSE;
+   }
+   RtlZeroMemory(MountPoint, sizeof(MOUNTMGR_MOUNT_POINT));
+   MountPoint->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
+   MountPoint->DeviceNameLength = MountDevName->NameLength;
+   RtlCopyMemory(MountPoint + 1, MountDevName->Name, MountDevName->NameLength);
+   RtlFreeHeap(GetProcessHeap(), 0, MountDevName);
+
+   RtlInitUnicodeString(&NtFileName, L"\\??\\MountPointManager");
+   InitializeObjectAttributes(&ObjectAttributes, &NtFileName, 0, NULL, NULL);
+   Status = NtOpenFile(&FileHandle, FILE_GENERIC_READ, &ObjectAttributes,
+                       &Iosb, FILE_SHARE_READ | FILE_SHARE_WRITE,
+                       FILE_SYNCHRONOUS_IO_NONALERT);
+   if (!NT_SUCCESS(Status))
+   {
+      SetLastErrorByStatus(Status);
+      RtlFreeHeap(GetProcessHeap(), 0, MountPoint);
+      return FALSE;
+   }
+
+   BufferLength = sizeof(MOUNTMGR_MOUNT_POINTS);
+   do
+   {
+      MountPoints = RtlAllocateHeap(GetProcessHeap(), 0, BufferLength);
+      if (MountPoints == NULL)
+      {
+         RtlFreeHeap(GetProcessHeap(), 0, MountPoint);
+         NtClose(FileHandle);
+         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+         return FALSE;
+      }
+
+      Status = NtDeviceIoControlFile(FileHandle, NULL, NULL, NULL, &Iosb,
+                                     IOCTL_MOUNTMGR_QUERY_POINTS,
+                                     MountPoint, MountPointSize,
+                                     MountPoints, BufferLength);
+      if (!NT_SUCCESS(Status))
+      {
+         RtlFreeHeap(GetProcessHeap(), 0, MountPoints);
+         if (Status == STATUS_BUFFER_OVERFLOW)
+         {
+            BufferLength = MountPoints->Size;
+            continue;
+         }
+         else if (!NT_SUCCESS(Status))
+         {
+            RtlFreeHeap(GetProcessHeap(), 0, MountPoint);
+            NtClose(FileHandle);
+            SetLastErrorByStatus(Status);
+            return FALSE;
+         }
+      }
+   }
+   while (!NT_SUCCESS(Status));
+
+   RtlFreeHeap(GetProcessHeap(), 0, MountPoint);
+   NtClose(FileHandle);
+
+   /*
+    * Now we've gathered info about all mount points mapped to our device, so
+    * select the correct one and copy it into the output buffer.
+    */
+
+   for (Index = 0; Index < MountPoints->NumberOfMountPoints; Index++)
+   {
+      MountPoint = MountPoints->MountPoints + Index;
+      SymbolicLinkName = (PUCHAR)MountPoints + MountPoint->SymbolicLinkNameOffset;
+      
+      /*
+       * Check for "\\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\"
+       * (with the last slash being optional) style symbolic links.
+       */
+
+      if (MountPoint->SymbolicLinkNameLength == 48 * sizeof(WCHAR) ||
+          (MountPoint->SymbolicLinkNameLength == 49 * sizeof(WCHAR) &&
+           SymbolicLinkName[48] == L'\\'))
+      {
+         if (RtlCompareMemory(SymbolicLinkName, L"\\??\\Volume{",
+                              11 * sizeof(WCHAR)) == 11 * sizeof(WCHAR) &&
+             SymbolicLinkName[19] == L'-' && SymbolicLinkName[24] == L'-' &&
+             SymbolicLinkName[29] == L'-' && SymbolicLinkName[34] == L'-' &&
+             SymbolicLinkName[47] == L'}')
+         {
+            if (VolumeNameLength >= MountPoint->SymbolicLinkNameLength / sizeof(WCHAR))
+            {
+               RtlCopyMemory(VolumeName,
+                             (PUCHAR)MountPoints + MountPoint->SymbolicLinkNameOffset,
+                             MountPoint->SymbolicLinkNameLength);
+               VolumeName[1] = L'\\';
+               Result = TRUE;
+            }
+            else
+            {
+               SetLastError(ERROR_FILENAME_EXCED_RANGE);
+               Result = FALSE;
+            }
+
+            RtlFreeHeap(GetProcessHeap(), 0, MountPoints);
+
+            return Result;
+         }
+      }
+   }
+
+   RtlFreeHeap(GetProcessHeap(), 0, MountPoints);
+   SetLastError(ERROR_INVALID_PARAMETER);
+
+   return FALSE;
 }
+
+/* EOF */