reactos/dll/win32/kernel32/client/file/volume.c

1427 lines
43 KiB
C
Raw Normal View History

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS system libraries
* FILE: dll/win32/kernel32/client/file/volume.c
* PURPOSE: File volume functions
* PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
* Erik Bos, Alexandre Julliard :
* GetLogicalDriveStringsA,
* GetLogicalDriveStringsW, GetLogicalDrives
* Pierre Schweitzer (pierre@reactos.org)
* UPDATE HISTORY:
* Created 01/11/98
*/
//WINE copyright notice:
/*
* DOS drives handling functions
*
* Copyright 1993 Erik Bos
* Copyright 1996 Alexandre Julliard
*/
2003-01-15 Casper S. Hornstrup <chorns@users.sourceforge.net> * lib/kernel32/k32.h: New file. * lib/kernel32/makefile (TARGET_CFLAGS): Add -I./. (TARGET_PCH): Set to k32.h. * lib/kernel32/except/except.c: Use <k32.h>. * lib/kernel32/file/backup.c: Ditto. * lib/kernel32/file/cnotify.c: Ditto. * lib/kernel32/file/copy.c: Ditto. * lib/kernel32/file/create.c: Ditto. * lib/kernel32/file/curdir.c: Ditto. * lib/kernel32/file/delete.c: Ditto. * lib/kernel32/file/deviceio.c: Ditto. * lib/kernel32/file/dir.c: Ditto. * lib/kernel32/file/dosdev.c: Ditto. * lib/kernel32/file/file.c: Ditto. * lib/kernel32/file/find.c: Ditto. * lib/kernel32/file/iocompl.c: Ditto. * lib/kernel32/file/lfile.c: Ditto. * lib/kernel32/file/lock.c: Ditto. * lib/kernel32/file/mailslot.c: Ditto. * lib/kernel32/file/move.c: Ditto. * lib/kernel32/file/npipe.c: Ditto. * lib/kernel32/file/pipe.c: Ditto. * lib/kernel32/file/rw.c: Ditto. * lib/kernel32/file/tape.c: Ditto. * lib/kernel32/file/volume.c: Ditto. * lib/kernel32/mem/global.c: Ditto. * lib/kernel32/mem/heap.c: Ditto. * lib/kernel32/mem/isbad.c: Ditto. * lib/kernel32/mem/local.c: Ditto. * lib/kernel32/mem/procmem.c: Ditto. * lib/kernel32/mem/section.c: Ditto. * lib/kernel32/mem/virtual.c: Ditto. * lib/kernel32/misc/atom.c: Ditto. * lib/kernel32/misc/comm.c: Ditto. * lib/kernel32/misc/console.c: Ditto. * lib/kernel32/misc/debug.c: Ditto. * lib/kernel32/misc/dllmain.c: Ditto. * lib/kernel32/misc/env.c: Ditto. * lib/kernel32/misc/error.c: Ditto. * lib/kernel32/misc/handle.c: Ditto. * lib/kernel32/misc/ldr.c: Ditto. * lib/kernel32/misc/profile.c: Ditto. * lib/kernel32/misc/res.c: Ditto. * lib/kernel32/misc/stubs.c: Ditto. * lib/kernel32/misc/sysinfo.c: Ditto. * lib/kernel32/misc/time.c: Ditto. * lib/kernel32/process/cmdline.c: Ditto. * lib/kernel32/process/create.c: Ditto. * lib/kernel32/process/proc.c: Ditto. * lib/kernel32/process/session.c: Ditto. * lib/kernel32/string/lstring.c: Ditto. * lib/kernel32/synch/critical.c: Ditto. * lib/kernel32/synch/event.c: Ditto. * lib/kernel32/synch/intrlck.c: Ditto. * lib/kernel32/synch/mutex.c: Ditto. * lib/kernel32/synch/sem.c: Ditto. * lib/kernel32/synch/timer.c: Ditto. * lib/kernel32/synch/wait.c: Ditto. * lib/kernel32/thread/fiber.c: Ditto. * lib/kernel32/thread/thread.c: Ditto. * lib/kernel32/thread/tls.c: Ditto. svn path=/trunk/; revision=4009
2003-01-15 21:24:36 +00:00
#include <k32.h>
[KERNEL32]: While working on the CMAKE branch, Amine and myself discovered a rather serious issue in kernel32 (and perhaps other libraries as well). Unlike rbuild, CMake does not allow you to export non-existant DLL functions (try it: add "poopyhead" in kernel32's exports under RBuild, and will it export "poopyhead", God knowing what that will actually link to). As an additional feature on top of the "allow non-existing functions to be exported" "feature", because rbuild generates and links STDCALL function names without the proper decoration (vs. enforcing decoration at linking, but only removing it at export-time), this allows the definition (as an export) of a STDCALL function that is completely different from the actual function itself. For example, the 5-parameter Foo function is normally Foo@20, while the 3-parameter Foo function woudl be Foo@12. Linking one against the other would fail (say, 2 parameters were added to Foo in a newer version). However, under RBUILD, both of these would appear as "Foo", and the linker/compiler would happilly connect the caller of Foo@3 (that has pushed 3 parameters) to the receiving side of Foo@5 (that is about to pop 5 parameters). Even -if- decorations WERE to be applied, Foo@12 would STILL succeed, because of the first feature, which would enable the export of Foo@12 even though no such function exist. In a further, bizare, twist of fate, the behavior of this RBUILD "feature", when the target function is not found, is to link the exported DLL TO ITSELF. Therefore, one can see how, previously to this patch, kernel32.dll would import a dozen functions from itself (all the non-existing functions). To really seal the deal, the behavior of exported functions used by kernel32, but that are actually forwarded to another DLL deserves a special mention. GetLastError, for example, merely forwards to RtlGetLastWin32Error, so it is normal behavior to use a #define in the C code so that all internal calls to the function are routed correctly. This did not happen, so instead, kernel32 tried importing/linking/exporting GetLastError, but this symbol is not found in the binary, because it is only a forwarder. This caused kernel32 to import from itself (the behavior when an exported symbol is not found). When importing from itself, the loader would now find the _forwarded_ for GetLastError, and correctly link with ntdll. What should be a one-liner of assembly (inline TEB access) thus became a triple-call indirection (GetLastError@0->StubLastError@0->__impGetLastError@0->__impRtlGetLastWin32Error->RtlGetLastWin32Error. While analyzing these issues, we also realized a strange macro SetLastErrorByStatus that manually tried to perform what there already exists a function for: RtlSetLastNtStatusFromWin32Error. And, in an exciting coda, we also realized that our Server 2003 Kernel32 exports more than a dozen Windows 95 APIs, through an auto-stub generation mechanism within winebuild, that gets linked as an object behind the scenes. [KERNEL32]: Removed all Win95 exports, cleaned up exports. [KERNEL32]: Fixed up set/get error macros by making them inline and/or calling the correct ntdll function. [KERNEL32]: Removed bizare calls to Wine-internal/specific APIs from our core Win32 DLL. [KERNEL32]: Wrote stubs for all functions which should be exported, and set the correct number of parameters for them. [KERNEL32]: Kernel32 is smaller, loads faster, does not export Windows 95 functions, does not export non-existing functions, and does not import from itself anymore. Note: This is one of the many failings of RBUILD the CMAKE system has helped us discover. I believe these issues are serious enough to warrant an immediate sync with trunk, but rest assured, there are many more completely broken, infinitely-regressing things that we discovered while switching to CMAKE. svn path=/trunk/; revision=48475
2010-08-07 05:02:58 +00:00
#define NDEBUG
#include <debug.h>
2003-07-10 Casper S. Hornstrup <chorns@users.sourceforge.net> * lib/kernel32/debug/break.c: Add @implemented and @unimplemented to APIs. * lib/kernel32/debug/debugger.c: Ditto. * lib/kernel32/debug/output.c: Ditto. * lib/kernel32/except/except.c: Ditto. * lib/kernel32/file/backup.c: Ditto. * lib/kernel32/file/cnotify.c: Ditto. * lib/kernel32/file/copy.c: Ditto. * lib/kernel32/file/create.c: Ditto. * lib/kernel32/file/curdir.c: Ditto. * lib/kernel32/file/delete.c: Ditto. * lib/kernel32/file/deviceio.c: Ditto. * lib/kernel32/file/dir.c: Ditto. * lib/kernel32/file/dosdev.c: Ditto. * lib/kernel32/file/file.c: Ditto. * lib/kernel32/file/find.c: Ditto. * lib/kernel32/file/iocompl.c: Ditto. * lib/kernel32/file/lfile.c: Ditto. * lib/kernel32/file/lock.c: Ditto. * lib/kernel32/file/mailslot.c: Ditto. * lib/kernel32/file/move.c: Ditto. * lib/kernel32/file/npipe.c: Ditto. * lib/kernel32/file/pipe.c: Ditto. * lib/kernel32/file/rw.c: Ditto. * lib/kernel32/file/tape.c: Ditto. * lib/kernel32/file/volume.c: Ditto. * lib/kernel32/mem/global.c: Ditto. * lib/kernel32/mem/heap.c: Ditto. * lib/kernel32/mem/isbad.c: Ditto. * lib/kernel32/mem/local.c: Ditto. * lib/kernel32/mem/procmem.c: Ditto. * lib/kernel32/mem/section.c: Ditto. * lib/kernel32/mem/virtual.c: Ditto. * lib/kernel32/misc/atom.c: Ditto. * lib/kernel32/misc/comm.c: Ditto. * lib/kernel32/misc/computername.c: Ditto. * lib/kernel32/misc/console.c: Ditto. * lib/kernel32/misc/env.c: Ditto. * lib/kernel32/misc/error.c: Ditto. * lib/kernel32/misc/errormsg.c: Ditto. * lib/kernel32/misc/handle.c: Ditto. * lib/kernel32/misc/ldr.c: Ditto. * lib/kernel32/misc/mbchars.c: Ditto. * lib/kernel32/misc/muldiv.c: Ditto. * lib/kernel32/misc/perfcnt.c: Ditto. * lib/kernel32/misc/profile.c: Ditto. * lib/kernel32/misc/res.c: Ditto. * lib/kernel32/misc/stubs.c: Ditto. * lib/kernel32/misc/sysinfo.c: Ditto. * lib/kernel32/misc/time.c: Ditto. * lib/kernel32/misc/toolhelp.c: Ditto. * lib/kernel32/process/cmdline.c: Ditto. * lib/kernel32/process/create.c: Ditto. * lib/kernel32/process/proc.c: Ditto. * lib/kernel32/process/session.c: Ditto. * lib/kernel32/string/lstring.c: Ditto. * lib/kernel32/synch/critical.c: Ditto. * lib/kernel32/synch/event.c: Ditto. * lib/kernel32/synch/intrlck.c: Ditto. * lib/kernel32/synch/mutex.c: Ditto. * lib/kernel32/synch/sem.c: Ditto. * lib/kernel32/synch/timer.c: Ditto. * lib/kernel32/synch/wait.c: Ditto. * lib/kernel32/thread/fiber.c: Ditto. * lib/kernel32/thread/fls.c: Ditto. * lib/kernel32/thread/thread.c: Ditto. * lib/kernel32/thread/tls.c: Ditto. svn path=/trunk/; revision=5045
2003-07-10 18:50:51 +00:00
/*
* @implemented
*/
BOOL
WINAPI
GetVolumeInformationA(IN LPCSTR lpRootPathName,
IN LPSTR lpVolumeNameBuffer,
IN DWORD nVolumeNameSize,
OUT LPDWORD lpVolumeSerialNumber OPTIONAL,
OUT LPDWORD lpMaximumComponentLength OPTIONAL,
OUT LPDWORD lpFileSystemFlags OPTIONAL,
OUT LPSTR lpFileSystemNameBuffer OPTIONAL,
IN DWORD nFileSystemNameSize)
{
BOOL Ret;
NTSTATUS Status;
PUNICODE_STRING RootPathNameU;
ANSI_STRING VolumeName, FileSystemName;
UNICODE_STRING VolumeNameU, FileSystemNameU;
/* If no root path provided, default to \ */
if (lpRootPathName == NULL)
{
lpRootPathName = "\\";
}
/* Convert root path to unicode */
RootPathNameU = Basep8BitStringToStaticUnicodeString(lpRootPathName);
if (RootPathNameU == NULL)
{
return FALSE;
}
/* Init all our STRINGS (U/A) */
VolumeNameU.Buffer = NULL;
VolumeNameU.MaximumLength = 0;
FileSystemNameU.Buffer = NULL;
FileSystemNameU.MaximumLength = 0;
VolumeName.Buffer = lpVolumeNameBuffer;
VolumeName.MaximumLength = nVolumeNameSize + 1;
FileSystemName.Buffer = lpFileSystemNameBuffer;
FileSystemName.MaximumLength = nFileSystemNameSize + 1;
/* Assume failure for now */
Ret = FALSE;
/* If caller wants volume name, allocate a buffer to receive it */
if (lpVolumeNameBuffer != NULL)
{
VolumeNameU.MaximumLength = sizeof(WCHAR) * (nVolumeNameSize + 1);
VolumeNameU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0,
VolumeNameU.MaximumLength);
if (VolumeNameU.Buffer == NULL)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto CleanAndQuit;
}
}
/* If caller wants file system name, allocate a buffer to receive it */
if (lpFileSystemNameBuffer != NULL)
{
FileSystemNameU.MaximumLength = sizeof(WCHAR) * (nFileSystemNameSize + 1);
FileSystemNameU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0,
FileSystemNameU.MaximumLength);
if (FileSystemNameU.Buffer == NULL)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto CleanAndQuit;
}
}
/* Call W */
Ret = GetVolumeInformationW(RootPathNameU->Buffer, VolumeNameU.Buffer,
nVolumeNameSize, lpVolumeSerialNumber,
lpMaximumComponentLength, lpFileSystemFlags,
FileSystemNameU.Buffer, nFileSystemNameSize);
/* If it succeed, convert back to ANSI */
if (Ret)
{
if (lpVolumeNameBuffer != NULL)
{
RtlInitUnicodeString(&VolumeNameU, VolumeNameU.Buffer);
Status = RtlUnicodeStringToAnsiString(&VolumeName, &VolumeNameU, FALSE);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
Ret = FALSE;
goto CleanAndQuit;
}
}
if (lpFileSystemNameBuffer != NULL)
{
RtlInitUnicodeString(&FileSystemNameU, FileSystemNameU.Buffer);
Status = RtlUnicodeStringToAnsiString(&FileSystemName, &FileSystemNameU, FALSE);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
Ret = FALSE;
goto CleanAndQuit;
}
}
}
/* Clean and quit */
CleanAndQuit:
if (VolumeNameU.Buffer != NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameU.Buffer);
}
if (FileSystemNameU.Buffer != NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, FileSystemNameU.Buffer);
}
return Ret;
}
/*
* @implemented
*/
BOOL
IsThisARootDirectory(IN HANDLE VolumeHandle,
IN PUNICODE_STRING NtPathName)
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
struct
{
FILE_NAME_INFORMATION;
WCHAR Buffer[MAX_PATH];
} FileNameInfo;
/* If we have a handle, query the name */
if (VolumeHandle)
{
Status = NtQueryInformationFile(VolumeHandle, &IoStatusBlock, &FileNameInfo, sizeof(FileNameInfo), FileNameInformation);
if (!NT_SUCCESS(Status))
{
return FALSE;
}
/* Check we properly end with a \ */
if (FileNameInfo.FileName[FileNameInfo.FileNameLength / sizeof(WCHAR) - 1] != L'\\')
{
return FALSE;
}
}
/* If we have a path */
if (NtPathName != NULL)
{
HANDLE LinkHandle;
WCHAR Buffer[512];
ULONG ReturnedLength;
UNICODE_STRING LinkTarget;
OBJECT_ATTRIBUTES ObjectAttributes;
NtPathName->Length -= sizeof(WCHAR);
InitializeObjectAttributes(&ObjectAttributes, NtPathName,
OBJ_CASE_INSENSITIVE,
NULL, NULL);
/* Try to see whether that's a symbolic name */
Status = NtOpenSymbolicLinkObject(&LinkHandle, SYMBOLIC_LINK_QUERY, &ObjectAttributes);
NtPathName->Length += sizeof(WCHAR);
if (!NT_SUCCESS(Status))
{
return FALSE;
}
/* If so, query the target */
LinkTarget.Buffer = Buffer;
LinkTarget.Length = 0;
LinkTarget.MaximumLength = sizeof(Buffer);
Status = NtQuerySymbolicLinkObject(LinkHandle, &LinkTarget, &ReturnedLength);
NtClose(LinkHandle);
/* A root directory (NtName) is a symbolic link */
if (!NT_SUCCESS(Status))
{
return FALSE;
}
}
return TRUE;
}
2003-07-10 Casper S. Hornstrup <chorns@users.sourceforge.net> * lib/kernel32/debug/break.c: Add @implemented and @unimplemented to APIs. * lib/kernel32/debug/debugger.c: Ditto. * lib/kernel32/debug/output.c: Ditto. * lib/kernel32/except/except.c: Ditto. * lib/kernel32/file/backup.c: Ditto. * lib/kernel32/file/cnotify.c: Ditto. * lib/kernel32/file/copy.c: Ditto. * lib/kernel32/file/create.c: Ditto. * lib/kernel32/file/curdir.c: Ditto. * lib/kernel32/file/delete.c: Ditto. * lib/kernel32/file/deviceio.c: Ditto. * lib/kernel32/file/dir.c: Ditto. * lib/kernel32/file/dosdev.c: Ditto. * lib/kernel32/file/file.c: Ditto. * lib/kernel32/file/find.c: Ditto. * lib/kernel32/file/iocompl.c: Ditto. * lib/kernel32/file/lfile.c: Ditto. * lib/kernel32/file/lock.c: Ditto. * lib/kernel32/file/mailslot.c: Ditto. * lib/kernel32/file/move.c: Ditto. * lib/kernel32/file/npipe.c: Ditto. * lib/kernel32/file/pipe.c: Ditto. * lib/kernel32/file/rw.c: Ditto. * lib/kernel32/file/tape.c: Ditto. * lib/kernel32/file/volume.c: Ditto. * lib/kernel32/mem/global.c: Ditto. * lib/kernel32/mem/heap.c: Ditto. * lib/kernel32/mem/isbad.c: Ditto. * lib/kernel32/mem/local.c: Ditto. * lib/kernel32/mem/procmem.c: Ditto. * lib/kernel32/mem/section.c: Ditto. * lib/kernel32/mem/virtual.c: Ditto. * lib/kernel32/misc/atom.c: Ditto. * lib/kernel32/misc/comm.c: Ditto. * lib/kernel32/misc/computername.c: Ditto. * lib/kernel32/misc/console.c: Ditto. * lib/kernel32/misc/env.c: Ditto. * lib/kernel32/misc/error.c: Ditto. * lib/kernel32/misc/errormsg.c: Ditto. * lib/kernel32/misc/handle.c: Ditto. * lib/kernel32/misc/ldr.c: Ditto. * lib/kernel32/misc/mbchars.c: Ditto. * lib/kernel32/misc/muldiv.c: Ditto. * lib/kernel32/misc/perfcnt.c: Ditto. * lib/kernel32/misc/profile.c: Ditto. * lib/kernel32/misc/res.c: Ditto. * lib/kernel32/misc/stubs.c: Ditto. * lib/kernel32/misc/sysinfo.c: Ditto. * lib/kernel32/misc/time.c: Ditto. * lib/kernel32/misc/toolhelp.c: Ditto. * lib/kernel32/process/cmdline.c: Ditto. * lib/kernel32/process/create.c: Ditto. * lib/kernel32/process/proc.c: Ditto. * lib/kernel32/process/session.c: Ditto. * lib/kernel32/string/lstring.c: Ditto. * lib/kernel32/synch/critical.c: Ditto. * lib/kernel32/synch/event.c: Ditto. * lib/kernel32/synch/intrlck.c: Ditto. * lib/kernel32/synch/mutex.c: Ditto. * lib/kernel32/synch/sem.c: Ditto. * lib/kernel32/synch/timer.c: Ditto. * lib/kernel32/synch/wait.c: Ditto. * lib/kernel32/thread/fiber.c: Ditto. * lib/kernel32/thread/fls.c: Ditto. * lib/kernel32/thread/thread.c: Ditto. * lib/kernel32/thread/tls.c: Ditto. svn path=/trunk/; revision=5045
2003-07-10 18:50:51 +00:00
/*
* @implemented
*/
BOOL
WINAPI
GetVolumeInformationW(IN LPCWSTR lpRootPathName,
IN LPWSTR lpVolumeNameBuffer,
IN DWORD nVolumeNameSize,
OUT LPDWORD lpVolumeSerialNumber OPTIONAL,
OUT LPDWORD lpMaximumComponentLength OPTIONAL,
OUT LPDWORD lpFileSystemFlags OPTIONAL,
OUT LPWSTR lpFileSystemNameBuffer OPTIONAL,
IN DWORD nFileSystemNameSize)
{
BOOL Ret;
NTSTATUS Status;
HANDLE VolumeHandle;
LPCWSTR RootPathName;
UNICODE_STRING NtPathName;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
PFILE_FS_VOLUME_INFORMATION VolumeInfo;
PFILE_FS_ATTRIBUTE_INFORMATION VolumeAttr;
ULONG OldMode, VolumeInfoSize, VolumeAttrSize;
/* If no root path provided, default to \ */
if (lpRootPathName == NULL)
{
RootPathName = L"\\";
}
else
{
RootPathName = lpRootPathName;
}
/* Convert length to bytes */
nVolumeNameSize *= sizeof(WCHAR);
nFileSystemNameSize *= sizeof(WCHAR);
/* Convert to NT name */
if (!RtlDosPathNameToNtPathName_U(RootPathName, &NtPathName, NULL, NULL))
{
SetLastError(ERROR_PATH_NOT_FOUND);
return FALSE;
}
/* Check we really end with a backslash */
if (NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 1] != L'\\')
{
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
return FALSE;
}
/* Try to open the received path */
InitializeObjectAttributes(&ObjectAttributes, &NtPathName,
OBJ_CASE_INSENSITIVE,
NULL, NULL);
/* No errors to the user */
RtlSetThreadErrorMode(RTL_SEM_FAILCRITICALERRORS, &OldMode);
Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, 0, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT);
RtlSetThreadErrorMode(OldMode, NULL);
if (!NT_SUCCESS(Status))
{
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
BaseSetLastNTError(Status);
return FALSE;
}
/* Check whether that's a root directory */
if (!IsThisARootDirectory(VolumeHandle, &NtPathName))
{
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
NtClose(VolumeHandle);
SetLastError(ERROR_DIR_NOT_ROOT);
return FALSE;
}
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
/* Assume we don't need to query FileFsVolumeInformation */
VolumeInfo = NULL;
/* If user wants volume name, allocate a buffer to query it */
if (lpVolumeNameBuffer != NULL)
{
VolumeInfoSize = nVolumeNameSize + sizeof(FILE_FS_VOLUME_INFORMATION);
}
/* If user just wants the serial number, allocate a dummy buffer */
else if (lpVolumeSerialNumber != NULL)
{
VolumeInfoSize = MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_VOLUME_INFORMATION);
}
/* Otherwise, nothing to query */
else
{
VolumeInfoSize = 0;
}
/* If we're to query, allocate a big enough buffer */
if (VolumeInfoSize != 0)
{
VolumeInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumeInfoSize);
if (VolumeInfo == NULL)
{
NtClose(VolumeHandle);
BaseSetLastNTError(STATUS_NO_MEMORY);
return FALSE;
}
}
/* Assume we don't need to query FileFsAttributeInformation */
VolumeAttr = NULL;
/* If user wants filesystem name, allocate a buffer to query it */
if (lpFileSystemNameBuffer != NULL)
{
VolumeAttrSize = nFileSystemNameSize + sizeof(FILE_FS_ATTRIBUTE_INFORMATION);
}
/* If user just wants max compo len or flags, allocate a dummy buffer */
else if (lpMaximumComponentLength != NULL || lpFileSystemFlags != NULL)
{
VolumeAttrSize = MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_ATTRIBUTE_INFORMATION);
}
else
{
VolumeAttrSize = 0;
}
/* If we're to query, allocate a big enough buffer */
if (VolumeAttrSize != 0)
{
VolumeAttr = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumeAttrSize);
if (VolumeAttr == NULL)
{
if (VolumeInfo != NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeInfo);
}
NtClose(VolumeHandle);
BaseSetLastNTError(STATUS_NO_MEMORY);
return FALSE;
}
}
/* Assume we'll fail */
Ret = FALSE;
/* If we're to query FileFsVolumeInformation, do it now! */
if (VolumeInfo != NULL)
{
Status = NtQueryVolumeInformationFile(VolumeHandle, &IoStatusBlock, VolumeInfo, VolumeInfoSize, FileFsVolumeInformation);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
goto CleanAndQuit;
}
}
/* If we're to query FileFsAttributeInformation, do it now! */
if (VolumeAttr != NULL)
{
Status = NtQueryVolumeInformationFile(VolumeHandle, &IoStatusBlock, VolumeAttr, VolumeAttrSize, FileFsAttributeInformation);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
goto CleanAndQuit;
}
}
/* If user wants volume name */
if (lpVolumeNameBuffer != NULL)
{
/* Check its buffer can hold it (+ 0) */
if (VolumeInfo->VolumeLabelLength >= nVolumeNameSize)
{
SetLastError(ERROR_BAD_LENGTH);
goto CleanAndQuit;
}
/* Copy and zero */
RtlCopyMemory(lpVolumeNameBuffer, VolumeInfo->VolumeLabel, VolumeInfo->VolumeLabelLength);
lpVolumeNameBuffer[VolumeInfo->VolumeLabelLength / sizeof(WCHAR)] = UNICODE_NULL;
}
/* If user wants wants serial number, return it */
if (lpVolumeSerialNumber != NULL)
{
*lpVolumeSerialNumber = VolumeInfo->VolumeSerialNumber;
}
/* If user wants filesystem name */
if (lpFileSystemNameBuffer != NULL)
{
/* Check its buffer can hold it (+ 0) */
if (VolumeAttr->FileSystemNameLength >= nFileSystemNameSize)
{
SetLastError(ERROR_BAD_LENGTH);
goto CleanAndQuit;
}
/* Copy and zero */
RtlCopyMemory(lpFileSystemNameBuffer, VolumeAttr->FileSystemName, VolumeAttr->FileSystemNameLength);
lpFileSystemNameBuffer[VolumeAttr->FileSystemNameLength / sizeof(WCHAR)] = UNICODE_NULL;
}
/* If user wants wants max compo len, return it */
if (lpMaximumComponentLength != NULL)
{
*lpMaximumComponentLength = VolumeAttr->MaximumComponentNameLength;
}
/* If user wants wants FS flags, return them */
if (lpFileSystemFlags != NULL)
{
*lpFileSystemFlags = VolumeAttr->FileSystemAttributes;
}
/* We did it! */
Ret = TRUE;
CleanAndQuit:
NtClose(VolumeHandle);
if (VolumeInfo != NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeInfo);
}
if (VolumeAttr != NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeAttr);
}
return Ret;
}
2003-07-10 Casper S. Hornstrup <chorns@users.sourceforge.net> * lib/kernel32/debug/break.c: Add @implemented and @unimplemented to APIs. * lib/kernel32/debug/debugger.c: Ditto. * lib/kernel32/debug/output.c: Ditto. * lib/kernel32/except/except.c: Ditto. * lib/kernel32/file/backup.c: Ditto. * lib/kernel32/file/cnotify.c: Ditto. * lib/kernel32/file/copy.c: Ditto. * lib/kernel32/file/create.c: Ditto. * lib/kernel32/file/curdir.c: Ditto. * lib/kernel32/file/delete.c: Ditto. * lib/kernel32/file/deviceio.c: Ditto. * lib/kernel32/file/dir.c: Ditto. * lib/kernel32/file/dosdev.c: Ditto. * lib/kernel32/file/file.c: Ditto. * lib/kernel32/file/find.c: Ditto. * lib/kernel32/file/iocompl.c: Ditto. * lib/kernel32/file/lfile.c: Ditto. * lib/kernel32/file/lock.c: Ditto. * lib/kernel32/file/mailslot.c: Ditto. * lib/kernel32/file/move.c: Ditto. * lib/kernel32/file/npipe.c: Ditto. * lib/kernel32/file/pipe.c: Ditto. * lib/kernel32/file/rw.c: Ditto. * lib/kernel32/file/tape.c: Ditto. * lib/kernel32/file/volume.c: Ditto. * lib/kernel32/mem/global.c: Ditto. * lib/kernel32/mem/heap.c: Ditto. * lib/kernel32/mem/isbad.c: Ditto. * lib/kernel32/mem/local.c: Ditto. * lib/kernel32/mem/procmem.c: Ditto. * lib/kernel32/mem/section.c: Ditto. * lib/kernel32/mem/virtual.c: Ditto. * lib/kernel32/misc/atom.c: Ditto. * lib/kernel32/misc/comm.c: Ditto. * lib/kernel32/misc/computername.c: Ditto. * lib/kernel32/misc/console.c: Ditto. * lib/kernel32/misc/env.c: Ditto. * lib/kernel32/misc/error.c: Ditto. * lib/kernel32/misc/errormsg.c: Ditto. * lib/kernel32/misc/handle.c: Ditto. * lib/kernel32/misc/ldr.c: Ditto. * lib/kernel32/misc/mbchars.c: Ditto. * lib/kernel32/misc/muldiv.c: Ditto. * lib/kernel32/misc/perfcnt.c: Ditto. * lib/kernel32/misc/profile.c: Ditto. * lib/kernel32/misc/res.c: Ditto. * lib/kernel32/misc/stubs.c: Ditto. * lib/kernel32/misc/sysinfo.c: Ditto. * lib/kernel32/misc/time.c: Ditto. * lib/kernel32/misc/toolhelp.c: Ditto. * lib/kernel32/process/cmdline.c: Ditto. * lib/kernel32/process/create.c: Ditto. * lib/kernel32/process/proc.c: Ditto. * lib/kernel32/process/session.c: Ditto. * lib/kernel32/string/lstring.c: Ditto. * lib/kernel32/synch/critical.c: Ditto. * lib/kernel32/synch/event.c: Ditto. * lib/kernel32/synch/intrlck.c: Ditto. * lib/kernel32/synch/mutex.c: Ditto. * lib/kernel32/synch/sem.c: Ditto. * lib/kernel32/synch/timer.c: Ditto. * lib/kernel32/synch/wait.c: Ditto. * lib/kernel32/thread/fiber.c: Ditto. * lib/kernel32/thread/fls.c: Ditto. * lib/kernel32/thread/thread.c: Ditto. * lib/kernel32/thread/tls.c: Ditto. svn path=/trunk/; revision=5045
2003-07-10 18:50:51 +00:00
/*
* @implemented
*/
BOOL
WINAPI
SetVolumeLabelA(IN LPCSTR lpRootPathName,
IN LPCSTR lpVolumeName OPTIONAL) /* NULL if deleting label */
{
BOOL Ret;
UNICODE_STRING VolumeNameU;
PUNICODE_STRING RootPathNameU;
if (lpRootPathName == NULL)
{
lpRootPathName = "\\";
}
RootPathNameU = Basep8BitStringToStaticUnicodeString(lpRootPathName);
if (RootPathNameU == NULL)
{
return FALSE;
}
if (lpVolumeName != NULL)
{
if (!Basep8BitStringToDynamicUnicodeString(&VolumeNameU, lpVolumeName))
{
return FALSE;
}
}
else
{
VolumeNameU.Buffer = NULL;
}
Ret = SetVolumeLabelW(RootPathNameU->Buffer, VolumeNameU.Buffer);
RtlFreeUnicodeString(&VolumeNameU);
return Ret;
}
2003-07-10 Casper S. Hornstrup <chorns@users.sourceforge.net> * lib/kernel32/debug/break.c: Add @implemented and @unimplemented to APIs. * lib/kernel32/debug/debugger.c: Ditto. * lib/kernel32/debug/output.c: Ditto. * lib/kernel32/except/except.c: Ditto. * lib/kernel32/file/backup.c: Ditto. * lib/kernel32/file/cnotify.c: Ditto. * lib/kernel32/file/copy.c: Ditto. * lib/kernel32/file/create.c: Ditto. * lib/kernel32/file/curdir.c: Ditto. * lib/kernel32/file/delete.c: Ditto. * lib/kernel32/file/deviceio.c: Ditto. * lib/kernel32/file/dir.c: Ditto. * lib/kernel32/file/dosdev.c: Ditto. * lib/kernel32/file/file.c: Ditto. * lib/kernel32/file/find.c: Ditto. * lib/kernel32/file/iocompl.c: Ditto. * lib/kernel32/file/lfile.c: Ditto. * lib/kernel32/file/lock.c: Ditto. * lib/kernel32/file/mailslot.c: Ditto. * lib/kernel32/file/move.c: Ditto. * lib/kernel32/file/npipe.c: Ditto. * lib/kernel32/file/pipe.c: Ditto. * lib/kernel32/file/rw.c: Ditto. * lib/kernel32/file/tape.c: Ditto. * lib/kernel32/file/volume.c: Ditto. * lib/kernel32/mem/global.c: Ditto. * lib/kernel32/mem/heap.c: Ditto. * lib/kernel32/mem/isbad.c: Ditto. * lib/kernel32/mem/local.c: Ditto. * lib/kernel32/mem/procmem.c: Ditto. * lib/kernel32/mem/section.c: Ditto. * lib/kernel32/mem/virtual.c: Ditto. * lib/kernel32/misc/atom.c: Ditto. * lib/kernel32/misc/comm.c: Ditto. * lib/kernel32/misc/computername.c: Ditto. * lib/kernel32/misc/console.c: Ditto. * lib/kernel32/misc/env.c: Ditto. * lib/kernel32/misc/error.c: Ditto. * lib/kernel32/misc/errormsg.c: Ditto. * lib/kernel32/misc/handle.c: Ditto. * lib/kernel32/misc/ldr.c: Ditto. * lib/kernel32/misc/mbchars.c: Ditto. * lib/kernel32/misc/muldiv.c: Ditto. * lib/kernel32/misc/perfcnt.c: Ditto. * lib/kernel32/misc/profile.c: Ditto. * lib/kernel32/misc/res.c: Ditto. * lib/kernel32/misc/stubs.c: Ditto. * lib/kernel32/misc/sysinfo.c: Ditto. * lib/kernel32/misc/time.c: Ditto. * lib/kernel32/misc/toolhelp.c: Ditto. * lib/kernel32/process/cmdline.c: Ditto. * lib/kernel32/process/create.c: Ditto. * lib/kernel32/process/proc.c: Ditto. * lib/kernel32/process/session.c: Ditto. * lib/kernel32/string/lstring.c: Ditto. * lib/kernel32/synch/critical.c: Ditto. * lib/kernel32/synch/event.c: Ditto. * lib/kernel32/synch/intrlck.c: Ditto. * lib/kernel32/synch/mutex.c: Ditto. * lib/kernel32/synch/sem.c: Ditto. * lib/kernel32/synch/timer.c: Ditto. * lib/kernel32/synch/wait.c: Ditto. * lib/kernel32/thread/fiber.c: Ditto. * lib/kernel32/thread/fls.c: Ditto. * lib/kernel32/thread/thread.c: Ditto. * lib/kernel32/thread/tls.c: Ditto. svn path=/trunk/; revision=5045
2003-07-10 18:50:51 +00:00
/*
* @implemented
*/
BOOL
WINAPI
SetVolumeLabelW(IN LPCWSTR lpRootPathName,
IN LPCWSTR lpVolumeName OPTIONAL) /* NULL if deleting label */
{
BOOL Ret;
NTSTATUS Status;
PWSTR VolumeRoot;
HANDLE VolumeHandle;
WCHAR VolumeGuid[MAX_PATH];
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
PFILE_FS_LABEL_INFORMATION FsLabelInfo;
UNICODE_STRING VolumeName, NtVolumeName;
/* If no root path provided, default to \ */
VolumeRoot = L"\\";
/* If user wants to set a label, make it a string */
if (lpVolumeName != NULL)
{
RtlInitUnicodeString(&VolumeName, lpVolumeName);
}
else
{
VolumeName.Length = 0;
VolumeName.MaximumLength = 0;
VolumeName.Buffer = NULL;
}
/* If we received a volume, try to get its GUID name */
if (lpRootPathName != NULL)
{
Ret = GetVolumeNameForVolumeMountPointW(lpRootPathName, VolumeGuid, MAX_PATH);
}
else
{
Ret = FALSE;
}
/* If we got the GUID name, use it */
if (Ret)
{
VolumeRoot = VolumeGuid;
}
else
{
/* Otherwise, use the name provided by the caller */
if (lpRootPathName != NULL)
{
VolumeRoot = (PWSTR)lpRootPathName;
}
}
/* Convert to a NT path */
if (!RtlDosPathNameToNtPathName_U(VolumeRoot, &NtVolumeName, NULL, NULL))
{
SetLastError(ERROR_PATH_NOT_FOUND);
return FALSE;
}
/* Check we really end with a backslash */
if (NtVolumeName.Buffer[(NtVolumeName.Length / sizeof(WCHAR)) - 1] != L'\\')
{
RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer);
BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
return FALSE;
}
/* Try to open the root directory */
InitializeObjectAttributes(&ObjectAttributes, &NtVolumeName,
OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE | FILE_WRITE_DATA,
&ObjectAttributes, &IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS(Status))
{
RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer);
BaseSetLastNTError(Status);
return FALSE;
}
/* Validate it's really a root path */
if (!IsThisARootDirectory(VolumeHandle, NULL))
{
RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer);
NtClose(VolumeHandle);
SetLastError(ERROR_DIR_NOT_ROOT);
return FALSE;
}
/* Done */
NtClose(VolumeHandle);
/* Now, open the volume to perform the label change */
NtVolumeName.Length -= sizeof(WCHAR);
InitializeObjectAttributes(&ObjectAttributes, &NtVolumeName,
OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE | FILE_WRITE_DATA,
&ObjectAttributes, &IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT);
RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
return FALSE;
}
/* Assume success */
Ret = TRUE;
/* Allocate a buffer that can hold new label and its size */
FsLabelInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(FILE_FS_LABEL_INFORMATION) + VolumeName.Length);
if (FsLabelInfo != NULL)
{
/* Copy name and set its size */
RtlCopyMemory(FsLabelInfo->VolumeLabel, VolumeName.Buffer, VolumeName.Length);
FsLabelInfo->VolumeLabelLength = VolumeName.Length;
/* And finally, set new label */
Status = NtSetVolumeInformationFile(VolumeHandle, &IoStatusBlock, FsLabelInfo, sizeof(FILE_FS_LABEL_INFORMATION) + VolumeName.Length, FileFsLabelInformation);
}
else
{
/* Allocation failed */
Status = STATUS_NO_MEMORY;
}
/* In case of failure, set status and mark failure */
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
Ret = FALSE;
}
/* We're done */
NtClose(VolumeHandle);
/* Free buffer if required */
if (FsLabelInfo != NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, FsLabelInfo);
}
return Ret;
}
/*
* @implemented (Wine 13 sep 2008)
*/
HANDLE
WINAPI
FindFirstVolumeW(IN LPWSTR volume,
IN DWORD len)
{
DWORD size = 1024;
DWORD br;
HANDLE mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE );
if (mgr == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
for (;;)
{
MOUNTMGR_MOUNT_POINT input;
MOUNTMGR_MOUNT_POINTS *output;
if (!(output = RtlAllocateHeap( RtlGetProcessHeap(), 0, size )))
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
break;
}
memset( &input, 0, sizeof(input) );
if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, &input, sizeof(input),
output, size, &br, NULL ))
{
if (GetLastError() != ERROR_MORE_DATA) break;
size = output->Size;
RtlFreeHeap( RtlGetProcessHeap(), 0, output );
continue;
}
CloseHandle( mgr );
/* abuse the Size field to store the current index */
output->Size = 0;
if (!FindNextVolumeW( output, volume, len ))
{
RtlFreeHeap( RtlGetProcessHeap(), 0, output );
return INVALID_HANDLE_VALUE;
}
return (HANDLE)output;
}
CloseHandle( mgr );
return INVALID_HANDLE_VALUE;
}
/*
* @implemented (Wine 13 sep 2008)
*/
HANDLE
WINAPI
FindFirstVolumeA(IN LPSTR volume,
IN DWORD len)
{
WCHAR *buffer = NULL;
HANDLE handle;
buffer = RtlAllocateHeap( RtlGetProcessHeap(), 0, len * sizeof(WCHAR) );
if (!buffer)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return INVALID_HANDLE_VALUE;
}
handle = FindFirstVolumeW( buffer, len );
if (handle != INVALID_HANDLE_VALUE)
{
if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL ))
{
FindVolumeClose( handle );
handle = INVALID_HANDLE_VALUE;
}
}
RtlFreeHeap( RtlGetProcessHeap(), 0, buffer );
return handle;
}
/*
* @implemented (Wine 13 sep 2008)
*/
BOOL
WINAPI
FindVolumeClose(IN HANDLE hFindVolume)
{
return RtlFreeHeap(RtlGetProcessHeap(), 0, hFindVolume);
}
/*
* @implemented
*/
BOOL
WINAPI
GetVolumePathNameA(IN LPCSTR lpszFileName,
IN LPSTR lpszVolumePathName,
IN DWORD cchBufferLength)
{
BOOL Ret;
PUNICODE_STRING FileNameU;
ANSI_STRING VolumePathName;
UNICODE_STRING VolumePathNameU;
/* Convert file name to unicode */
FileNameU = Basep8BitStringToStaticUnicodeString(lpszFileName);
if (FileNameU == NULL)
{
return FALSE;
}
/* Initialize all the strings we'll need */
VolumePathName.Buffer = lpszVolumePathName;
VolumePathName.Length = 0;
VolumePathName.MaximumLength = cchBufferLength - 1;
VolumePathNameU.Length = 0;
VolumePathNameU.MaximumLength = (cchBufferLength - 1) * sizeof(WCHAR) + sizeof(UNICODE_NULL);
/* Allocate a buffer for calling the -W */
VolumePathNameU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumePathNameU.MaximumLength);
if (VolumePathNameU.Buffer == NULL)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
/* Call the -W implementation */
Ret = GetVolumePathNameW(FileNameU->Buffer, VolumePathNameU.Buffer, cchBufferLength);
/* If it succeed */
if (Ret)
{
NTSTATUS Status;
/* Convert back to ANSI */
RtlInitUnicodeString(&VolumePathNameU, VolumePathNameU.Buffer);
Status = RtlUnicodeStringToAnsiString(&VolumePathName, &VolumePathNameU, FALSE);
/* If conversion failed, just set error code and fail the rest */
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
Ret = FALSE;
}
/* Otherwise, null terminate the string (it's OK, we computed -1) */
else
{
VolumePathName.Buffer[VolumePathName.Length] = ANSI_NULL;
}
}
/* Free the buffer allocated for -W call */
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNameU.Buffer);
return Ret;
}
/*
* @implemented
*/
BOOL
WINAPI
GetVolumePathNameW(IN LPCWSTR lpszFileName,
IN LPWSTR lpszVolumePathName,
IN DWORD cchBufferLength)
{
BOOL MountPoint;
DWORD FullPathLen;
WCHAR OldFilePart;
UNICODE_STRING FullPath;
PWSTR FullPathBuf, FilePart, VolumeNameBuf;
/* Probe for full path len */
FullPathLen = GetFullPathNameW(lpszFileName, 0, NULL, NULL);
if (FullPathLen == 0)
{
return FALSE;
}
/* Allocate a big enough buffer to receive it */
FullPathBuf = RtlAllocateHeap(RtlGetProcessHeap(), 0, (FullPathLen + 10) * sizeof(WCHAR));
if (FullPathBuf == NULL)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
/* And get full path name */
if (GetFullPathNameW(lpszFileName, FullPathLen + 10, FullPathBuf, &FilePart) == 0)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
return FALSE;
}
/* Make a string out of it */
RtlInitUnicodeString(&FullPath, FullPathBuf);
/* We will finish our string with '\', for ease of the parsing after */
if (FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\')
{
FullPath.Length += sizeof(WCHAR);
FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] = L'\\';
FullPath.Buffer[FullPath.Length / sizeof(WCHAR)] = UNICODE_NULL;
}
/* Allocate a buffer big enough to receive our volume name */
VolumeNameBuf = RtlAllocateHeap(RtlGetProcessHeap(), 0, 0x2000 * sizeof(WCHAR));
if (VolumeNameBuf == NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
/* We don't care about file part: we added an extra backslash, so there's no
* file, we're back at the dir level.
* We'll recompute file part afterwards
*/
FilePart = NULL;
/* Keep track of the letter we could drop to shorten the string */
OldFilePart = UNICODE_NULL;
/* As long as querying volume name fails, keep looping */
while (!BasepGetVolumeNameForVolumeMountPoint(FullPath.Buffer, VolumeNameBuf, 0x2000u, &MountPoint))
{
USHORT LastSlash;
/* Not a mount point, but opening returning access denied? Assume it's one, just not
* a reparse backed one (classic mount point, a device)!
*/
if (!MountPoint && GetLastError() == ERROR_ACCESS_DENIED)
{
MountPoint = TRUE;
}
/* BasepGetVolumeNameForVolumeMountPoint failed, but returned a volume name.
* This can happen when we are given a reparse point where MountMgr could find associated
* volume name which is not a valid DOS volume
* A valid DOS name always starts with \\
*/
if (VolumeNameBuf[0] != UNICODE_NULL && (FullPath.Buffer[0] != L'\\' || FullPath.Buffer[1] != L'\\'))
{
CHAR RootPathName[4];
/* Construct a simple <letter>:\ string to get drive type */
RootPathName[0] = FullPath.Buffer[0];
RootPathName[1] = ':';
RootPathName[2] = '\\';
RootPathName[3] = ANSI_NULL;
/* If we weren't given a drive letter actually, or if that's not a remote drive
* Note: in this code path, we're recursive and stop fail loop
*/
if (FullPath.Buffer[1] != L':' || GetDriveTypeA(RootPathName) != DRIVE_REMOTE)
{
BOOL Ret;
/* We won't need the full path, we'll now work with the returned volume name */
RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
/* If it wasn't an NT name which was returned */
if ((VolumeNameBuf[0] != L'\\') || (VolumeNameBuf[1] != L'?') ||
(VolumeNameBuf[2] != L'?') || (VolumeNameBuf[3] != L'\\'))
{
PWSTR GlobalPath;
UNICODE_STRING GlobalRoot;
/* Create a new name in the NT namespace (from Win32) */
RtlInitUnicodeString(&FullPath, VolumeNameBuf);
RtlInitUnicodeString(&GlobalRoot, L"\\\\?\\GLOBALROOT");
/* We allocate a buffer than can contain both the namespace and the volume name */
GlobalPath = RtlAllocateHeap(RtlGetProcessHeap(), 0, FullPath.Length + GlobalRoot.Length);
if (GlobalPath == NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameBuf);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
/* Fill in the new query name */
RtlCopyMemory(GlobalPath, GlobalRoot.Buffer, GlobalRoot.Length);
RtlCopyMemory((PVOID)((ULONG_PTR)GlobalPath + GlobalRoot.Length), FullPath.Buffer, FullPath.Length);
GlobalPath[(FullPath.Length + GlobalRoot.Length) / sizeof(WCHAR)] = UNICODE_NULL;
/* Give it another try */
Ret = GetVolumePathNameW(GlobalPath, lpszVolumePathName, cchBufferLength);
RtlFreeHeap(RtlGetProcessHeap(), 0, GlobalPath);
}
else
{
/* If we don't have a drive letter in the Win32 name space \\.\<letter>: */
if ((VolumeNameBuf[4] != UNICODE_NULL) && (VolumeNameBuf[5] != L':'))
{
/* Shit our starting \\ */
RtlInitUnicodeString(&FullPath, VolumeNameBuf);
RtlMoveMemory(VolumeNameBuf, (PVOID)((ULONG_PTR)VolumeNameBuf + (2 * sizeof(WCHAR))), FullPath.Length - (3 * sizeof(WCHAR)));
}
/* Otherwise, just make sure we're double \ at the being to query again with the
* proper namespace
*/
else
{
VolumeNameBuf[1] = L'\\';
}
/* Give it another try */
Ret = GetVolumePathNameW(VolumeNameBuf, lpszVolumePathName, cchBufferLength);
}
/* And done! */
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameBuf);
return Ret;
}
}
/* No mount point but with a file part? Restore filepart and exit */
if (!MountPoint && FilePart != NULL)
{
FilePart[0] = OldFilePart;
RtlInitUnicodeString(&FullPath, FullPathBuf);
break;
}
/* We cannot go down the path any longer, too small */
if (FullPath.Length <= sizeof(WCHAR))
{
break;
}
/* Prepare the next split */
LastSlash = (FullPath.Length / sizeof(WCHAR)) - 2;
if (FullPath.Length / sizeof(WCHAR) != 2)
{
do
{
if (FullPath.Buffer[LastSlash] == L'\\')
{
break;
}
--LastSlash;
} while (LastSlash != 0);
}
/* We couldn't split path, quit */
if (LastSlash == 0)
{
break;
}
/* If that's a mount point, keep track of the directory name */
if (MountPoint)
{
FilePart = &FullPath.Buffer[LastSlash + 1];
OldFilePart = FilePart[0];
/* And null terminate the string */
FilePart[0] = UNICODE_NULL;
}
/* Otherwise, just null terminate the string */
else
{
FullPath.Buffer[LastSlash + 1] = UNICODE_NULL;
}
/* We went down a bit in the path, fix the string and retry */
RtlInitUnicodeString(&FullPath, FullPathBuf);
}
/* Once here, we'll return something from the full path buffer, so release
* output buffer
*/
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameBuf);
/* Not a mount point, bail out */
if (!MountPoint && FilePart == NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
return FALSE;
}
/* Make sure we have enough room to copy our volume */
if ((cchBufferLength * sizeof(WCHAR)) < FullPath.Length + sizeof(UNICODE_NULL))
{
RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
SetLastError(ERROR_FILENAME_EXCED_RANGE);
return FALSE;
}
/* Copy and null terminate */
RtlCopyMemory(lpszVolumePathName, FullPath.Buffer, FullPath.Length);
lpszVolumePathName[FullPath.Length / sizeof(WCHAR)] = UNICODE_NULL;
RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
/* Done! */
return TRUE;
}
/*
* @implemented
*/
BOOL
WINAPI
FindNextVolumeA(IN HANDLE handle,
IN LPSTR volume,
IN DWORD len)
{
WCHAR *buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, len * sizeof(WCHAR));
BOOL ret;
if (!buffer)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
if ((ret = FindNextVolumeW( handle, buffer, len )))
{
if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL )) ret = FALSE;
}
RtlFreeHeap(RtlGetProcessHeap(), 0, buffer);
return ret;
}
/*
* @implemented
*/
BOOL
WINAPI
FindNextVolumeW(IN HANDLE handle,
IN LPWSTR volume,
IN DWORD len)
{
MOUNTMGR_MOUNT_POINTS *data = handle;
while (data->Size < data->NumberOfMountPoints)
{
static const WCHAR volumeW[] = {'\\','?','?','\\','V','o','l','u','m','e','{',};
WCHAR *link = (WCHAR *)((char *)data + data->MountPoints[data->Size].SymbolicLinkNameOffset);
DWORD size = data->MountPoints[data->Size].SymbolicLinkNameLength;
data->Size++;
/* skip non-volumes */
if (size < sizeof(volumeW) || memcmp( link, volumeW, sizeof(volumeW) )) continue;
if (size + sizeof(WCHAR) >= len * sizeof(WCHAR))
{
SetLastError( ERROR_FILENAME_EXCED_RANGE );
return FALSE;
}
memcpy( volume, link, size );
volume[1] = '\\'; /* map \??\ to \\?\ */
volume[size / sizeof(WCHAR)] = '\\'; /* Windows appends a backslash */
volume[size / sizeof(WCHAR) + 1] = 0;
DPRINT( "returning entry %u %s\n", data->Size - 1, volume );
return TRUE;
}
SetLastError( ERROR_NO_MORE_FILES );
return FALSE;
}
/*
* @implemented
*/
BOOL
WINAPI
GetVolumePathNamesForVolumeNameA(IN LPCSTR lpszVolumeName,
IN LPSTR lpszVolumePathNames,
IN DWORD cchBufferLength,
OUT PDWORD lpcchReturnLength)
{
BOOL Ret;
NTSTATUS Status;
DWORD cchReturnLength;
ANSI_STRING VolumePathName;
PUNICODE_STRING VolumeNameU;
UNICODE_STRING VolumePathNamesU;
/* Convert volume name to unicode */
VolumeNameU = Basep8BitStringToStaticUnicodeString(lpszVolumeName);
if (VolumeNameU == NULL)
{
return FALSE;
}
/* Initialize the strings we'll use later on */
VolumePathName.Length = 0;
VolumePathName.MaximumLength = cchBufferLength;
VolumePathName.Buffer = lpszVolumePathNames;
VolumePathNamesU.Length = 0;
VolumePathNamesU.MaximumLength = sizeof(WCHAR) * cchBufferLength;
/* If caller provided a non 0 sized string, allocate a buffer for our unicode string */
if (VolumePathNamesU.MaximumLength != 0)
{
VolumePathNamesU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.MaximumLength);
if (VolumePathNamesU.Buffer == NULL)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
}
else
{
VolumePathNamesU.Buffer = NULL;
}
/* Call the -W implementation */
Ret = GetVolumePathNamesForVolumeNameW(VolumeNameU->Buffer, VolumePathNamesU.Buffer,
cchBufferLength, &cchReturnLength);
/* Call succeed, we'll return the total length */
if (Ret)
{
VolumePathNamesU.Length = sizeof(WCHAR) * cchReturnLength;
}
else
{
/* Else, if we fail for anything else than too small buffer, quit */
if (GetLastError() != ERROR_MORE_DATA)
{
if (VolumePathNamesU.Buffer != NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.Buffer);
}
return FALSE;
}
/* Otherwise, we'll just copy as much as we can */
VolumePathNamesU.Length = sizeof(WCHAR) * cchBufferLength;
}
/* Convert our output string back to ANSI */
Status = RtlUnicodeStringToAnsiString(&VolumePathName, &VolumePathNamesU, FALSE);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
if (VolumePathNamesU.Buffer != NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.Buffer);
}
return FALSE;
}
/* If caller wants return length, two cases... */
if (lpcchReturnLength != NULL)
{
/* We succeed: return the copied length */
if (Ret)
{
*lpcchReturnLength = VolumePathName.Length;
}
/* We failed, return the size we would have loved having! */
else
{
*lpcchReturnLength = sizeof(WCHAR) * cchReturnLength;
}
}
/* Release our buffer if allocated */
if (VolumePathNamesU.Buffer != NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.Buffer);
}
return Ret;
}
/*
* @implemented
*/
BOOL
WINAPI
GetVolumePathNamesForVolumeNameW(IN LPCWSTR lpszVolumeName,
IN LPWSTR lpszVolumePathNames,
IN DWORD cchBufferLength,
OUT PDWORD lpcchReturnLength)
{
BOOL Ret;
PWSTR MultiSz;
DWORD BytesReturned;
HANDLE MountMgrHandle;
UNICODE_STRING VolumeName;
PMOUNTMGR_TARGET_NAME TargetName;
PMOUNTMGR_VOLUME_PATHS VolumePaths;
ULONG BufferSize, CharsInMgr, CharsInOutput, Paths;
/* First look that our volume name looks somehow correct */
RtlInitUnicodeString(&VolumeName, lpszVolumeName);
if (VolumeName.Buffer[(VolumeName.Length / sizeof(WCHAR)) - 1] != L'\\')
{
BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
return FALSE;
}
/* Validate it's a DOS volume name finishing with a backslash */
if (!MOUNTMGR_IS_DOS_VOLUME_NAME_WB(&VolumeName))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
/* Allocate an input MOUNTMGR_TARGET_NAME */
TargetName = RtlAllocateHeap(RtlGetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT));
if (TargetName == NULL)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
/* And fill it */
RtlZeroMemory(TargetName, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT));
TargetName->DeviceNameLength = VolumeName.Length - sizeof(WCHAR);
RtlCopyMemory(TargetName->DeviceName, VolumeName.Buffer, TargetName->DeviceNameLength);
TargetName->DeviceName[1] = L'?';
/* Open the mount manager */
MountMgrHandle = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
INVALID_HANDLE_VALUE);
if (MountMgrHandle == INVALID_HANDLE_VALUE)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName);
return FALSE;
}
/* Allocate an initial output buffer, just to get length */
VolumePaths = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(MOUNTMGR_VOLUME_PATHS));
if (VolumePaths == NULL)
{
CloseHandle(MountMgrHandle);
RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
/* Query the paths */
Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS,
TargetName, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT),
VolumePaths, sizeof(MOUNTMGR_VOLUME_PATHS), &BytesReturned,
NULL);
/* Loop until we can query everything */
while (!Ret)
{
/* If failed for another reason than too small buffer, fail */
if (GetLastError() != ERROR_MORE_DATA)
{
CloseHandle(MountMgrHandle);
RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName);
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePaths);
return FALSE;
}
/* Get the required length */
BufferSize = VolumePaths->MultiSzLength + sizeof(MOUNTMGR_VOLUME_PATHS);
/* And reallocate our output buffer (big enough this time) */
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePaths);
VolumePaths = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferSize);
if (VolumePaths == NULL)
{
CloseHandle(MountMgrHandle);
RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
/* Query again the mount mgr */
Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS,
TargetName, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT),
VolumePaths, BufferSize, &BytesReturned, NULL);
}
/* We're done, no need for input nor mount mgr any longer */
CloseHandle(MountMgrHandle);
RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName);
/* Initialize:
- Number of paths we saw (useful to count extra \)
- Progress in mount mgr output
- Progress in output buffer
- Direct buffer to returned MultiSz
*/
Paths = 0;
CharsInMgr = 0;
CharsInOutput = 0;
MultiSz = VolumePaths->MultiSz;
/* If we have an output buffer */
if (cchBufferLength != 0)
{
/* Loop on the output to recopy it back to the caller
* Note that we loop until -1 not to handle last 0 (will be done later on)
*/
for (; (CharsInMgr < VolumePaths->MultiSzLength / sizeof(WCHAR) - 1) && (CharsInOutput < cchBufferLength);
++CharsInMgr, ++CharsInOutput)
{
/* When we reach the end of a path */
if (MultiSz[CharsInMgr] == UNICODE_NULL)
{
/* On path done (count), add an extra \ at the end */
++Paths;
lpszVolumePathNames[CharsInOutput] = L'\\';
++CharsInOutput;
/* Make sure we don't overflow */
if (CharsInOutput == cchBufferLength)
{
break;
}
}
/* Copy the char to the caller
* So, in case we're in the end of a path, we wrote two chars to
* the output buffer: \\ and \0
*/
lpszVolumePathNames[CharsInOutput] = MultiSz[CharsInMgr];
}
}
/* If output buffer was too small (ie, we couldn't parse all the input buffer) */
if (CharsInMgr < VolumePaths->MultiSzLength / sizeof(WCHAR) - 1)
{
/* Keep looping on it, to count the number of extra \ that will be required
* So that on the next call, caller can allocate enough space
*/
for (; CharsInMgr < VolumePaths->MultiSzLength / sizeof(WCHAR) - 1; ++CharsInMgr)
{
if (MultiSz[CharsInMgr] == UNICODE_NULL)
{
++Paths;
}
}
}
/* If we couldn't write as much as we wanted to the output buffer
* This handles the case where we could write everything excepted the
* terminating \0 for multi SZ
*/
if (CharsInOutput >= cchBufferLength)
{
/* Fail and set appropriate error code */
Ret = FALSE;
SetLastError(ERROR_MORE_DATA);
/* If caller wants to know how many chars to allocate, return it */
if (lpcchReturnLength != NULL)
{
/* It's amount of extra \ + number of chars in MultiSz (including double \0) */
*lpcchReturnLength = Paths + (VolumePaths->MultiSzLength / sizeof(WCHAR));
}
}
else
{
/* It succeed so terminate the multi SZ (second \0) */
lpszVolumePathNames[CharsInOutput] = UNICODE_NULL;
Ret = TRUE;
/* If caller wants the amount of chars written, return it */
if (lpcchReturnLength != NULL)
{
/* Including the terminating \0 we just added */
*lpcchReturnLength = CharsInOutput + 1;
}
}
/* Free last bits */
RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePaths);
/* And return */
return Ret;
}
/* EOF */