3733 lines
106 KiB
C
3733 lines
106 KiB
C
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
pathmisc.c
|
|
|
|
Abstract:
|
|
|
|
Win32 misceleneous path functions
|
|
|
|
Author:
|
|
|
|
Mark Lucovsky (markl) 16-Oct-1990
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "basedll.h"
|
|
#include "apcompat.h"
|
|
#include <wow64t.h>
|
|
|
|
BOOL
|
|
IsThisARootDirectory(
|
|
HANDLE RootHandle,
|
|
PUNICODE_STRING FileName OPTIONAL
|
|
)
|
|
{
|
|
PFILE_NAME_INFORMATION FileNameInfo;
|
|
WCHAR Buffer[MAX_PATH+sizeof(FileNameInfo)];
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
NTSTATUS Status;
|
|
BOOL rv;
|
|
|
|
OBJECT_ATTRIBUTES Attributes;
|
|
HANDLE LinkHandle;
|
|
WCHAR LinkValueBuffer[2*MAX_PATH];
|
|
UNICODE_STRING LinkValue;
|
|
ULONG ReturnedLength;
|
|
|
|
rv = FALSE;
|
|
|
|
FileNameInfo = (PFILE_NAME_INFORMATION)Buffer;
|
|
if (RootHandle == NULL) {
|
|
Status = STATUS_INVALID_HANDLE;
|
|
} else {
|
|
Status = NtQueryInformationFile (RootHandle,
|
|
&IoStatusBlock,
|
|
FileNameInfo,
|
|
sizeof(Buffer),
|
|
FileNameInformation);
|
|
}
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
if ( FileNameInfo->FileName[(FileNameInfo->FileNameLength>>1)-1] == (WCHAR)'\\' ) {
|
|
rv = TRUE;
|
|
}
|
|
}
|
|
|
|
if ( !rv ) {
|
|
|
|
//
|
|
// See if this is a dos substed drive (or) redirected net drive
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT (FileName)) {
|
|
|
|
FileName->Length = FileName->Length - sizeof((WCHAR)'\\');
|
|
|
|
InitializeObjectAttributes( &Attributes,
|
|
FileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
Status = NtOpenSymbolicLinkObject (&LinkHandle,
|
|
SYMBOLIC_LINK_QUERY,
|
|
&Attributes);
|
|
FileName->Length = FileName->Length + sizeof((WCHAR)'\\');
|
|
if (NT_SUCCESS (Status)) {
|
|
|
|
//
|
|
// Now query the link and see if there is a redirection
|
|
//
|
|
|
|
LinkValue.Buffer = LinkValueBuffer;
|
|
LinkValue.Length = 0;
|
|
LinkValue.MaximumLength = (USHORT)(sizeof(LinkValueBuffer));
|
|
ReturnedLength = 0;
|
|
Status = NtQuerySymbolicLinkObject( LinkHandle,
|
|
&LinkValue,
|
|
&ReturnedLength
|
|
);
|
|
NtClose( LinkHandle );
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
rv = TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
UINT
|
|
APIENTRY
|
|
GetSystemDirectoryA(
|
|
LPSTR lpBuffer,
|
|
UINT uSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ANSI thunk to GetSystemDirectoryW
|
|
|
|
--*/
|
|
|
|
{
|
|
ANSI_STRING AnsiString;
|
|
NTSTATUS Status;
|
|
ULONG cbAnsiString;
|
|
PUNICODE_STRING WindowsSystemDirectory = &BaseWindowsSystemDirectory;
|
|
|
|
#ifdef WX86
|
|
if (NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll) {
|
|
NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE;
|
|
WindowsSystemDirectory = &BaseWindowsSys32x86Directory;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
// BaseWindowsSystemDirectory.Length contains the byte
|
|
// count of unicode string.
|
|
// Original code does "UnicodeLength / sizeof(WCHAR)" to
|
|
// get the size of corresponding ansi string.
|
|
// This is correct in SBCS environment. However in DBCS
|
|
// environment, it's definitely WRONG.
|
|
|
|
Status = RtlUnicodeToMultiByteSize(&cbAnsiString,
|
|
WindowsSystemDirectory->Buffer,
|
|
WindowsSystemDirectory->MaximumLength
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return 0;
|
|
}
|
|
|
|
if ( (USHORT)uSize < (USHORT)cbAnsiString ) {
|
|
return cbAnsiString;
|
|
}
|
|
|
|
AnsiString.MaximumLength = (USHORT)(uSize);
|
|
AnsiString.Buffer = lpBuffer;
|
|
|
|
Status = BasepUnicodeStringTo8BitString(
|
|
&AnsiString,
|
|
WindowsSystemDirectory,
|
|
FALSE
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return 0;
|
|
}
|
|
return AnsiString.Length;
|
|
}
|
|
|
|
|
|
UINT
|
|
APIENTRY
|
|
GetSystemDirectoryW(
|
|
LPWSTR lpBuffer,
|
|
UINT uSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function obtains the pathname of the Windows system
|
|
subdirectory. The system subdirectory contains such files as
|
|
Windows libraries, drivers, and font files.
|
|
|
|
The pathname retrieved by this function does not end with a
|
|
backslash unless the system directory is the root directory. For
|
|
example, if the system directory is named WINDOWS\SYSTEM on drive
|
|
C:, the pathname of the system subdirectory retrieved by this
|
|
function is C:\WINDOWS\SYSTEM.
|
|
|
|
Arguments:
|
|
|
|
lpBuffer - Points to the buffer that is to receive the
|
|
null-terminated character string containing the pathname.
|
|
|
|
uSize - Specifies the maximum size (in bytes) of the buffer. This
|
|
value should be set to at least MAX_PATH to allow sufficient room in
|
|
the buffer for the pathname.
|
|
|
|
Return Value:
|
|
|
|
The return value is the length of the string copied to lpBuffer, not
|
|
including the terminating null character. If the return value is
|
|
greater than uSize, the return value is the size of the buffer
|
|
required to hold the pathname. The return value is zero if the
|
|
function failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
PUNICODE_STRING WindowsSystemDirectory = &BaseWindowsSystemDirectory;
|
|
|
|
#ifdef WX86
|
|
if (NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll) {
|
|
NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE;
|
|
WindowsSystemDirectory = &BaseWindowsSys32x86Directory;
|
|
}
|
|
#endif
|
|
|
|
if ( uSize*2 < WindowsSystemDirectory->MaximumLength ) {
|
|
return WindowsSystemDirectory->MaximumLength/2;
|
|
}
|
|
RtlMoveMemory(
|
|
lpBuffer,
|
|
WindowsSystemDirectory->Buffer,
|
|
WindowsSystemDirectory->Length
|
|
);
|
|
lpBuffer[(WindowsSystemDirectory->Length>>1)] = UNICODE_NULL;
|
|
return WindowsSystemDirectory->Length/2;
|
|
}
|
|
|
|
UINT
|
|
APIENTRY
|
|
GetSystemWindowsDirectoryA(
|
|
LPSTR lpBuffer,
|
|
UINT uSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ANSI thunk to GetSystemWindowsDirectoryW
|
|
|
|
--*/
|
|
|
|
{
|
|
ANSI_STRING AnsiString;
|
|
NTSTATUS Status;
|
|
ULONG cbAnsiString;
|
|
|
|
// BaseWindowsDirectory.Length contains the byte
|
|
// count of unicode string.
|
|
// Original code does "UnicodeLength / sizeof(WCHAR)" to
|
|
// get the size of corresponding ansi string.
|
|
// This is correct in SBCS environment. However in DBCS
|
|
// environment, it's definitely WRONG.
|
|
|
|
Status = RtlUnicodeToMultiByteSize( &cbAnsiString,
|
|
BaseWindowsDirectory.Buffer,
|
|
BaseWindowsDirectory.MaximumLength);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return 0;
|
|
}
|
|
|
|
if ( (USHORT)uSize < (USHORT)cbAnsiString ) {
|
|
return cbAnsiString;
|
|
}
|
|
|
|
AnsiString.MaximumLength = (USHORT)(uSize);
|
|
AnsiString.Buffer = lpBuffer;
|
|
|
|
Status = BasepUnicodeStringTo8BitString(
|
|
&AnsiString,
|
|
&BaseWindowsDirectory,
|
|
FALSE
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return 0;
|
|
}
|
|
return AnsiString.Length;
|
|
}
|
|
|
|
UINT
|
|
APIENTRY
|
|
GetSystemWindowsDirectoryW(
|
|
LPWSTR lpBuffer,
|
|
UINT uSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function obtains the pathname of the system Windows directory.
|
|
|
|
Arguments:
|
|
|
|
lpBuffer - Points to the buffer that is to receive the
|
|
null-terminated character string containing the pathname.
|
|
|
|
uSize - Specifies the maximum size (in wchars) of the buffer. This
|
|
value should be set to at least MAX_PATH to allow sufficient room in
|
|
the buffer for the pathname.
|
|
|
|
Return Value:
|
|
|
|
The return value is the length of the string copied to lpBuffer, not
|
|
including the terminating null character. If the return value is
|
|
greater than uSize, the return value is the size of the buffer
|
|
required to hold the pathname. The return value is zero if the
|
|
function failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
if ( uSize*2 < BaseWindowsDirectory.MaximumLength ) {
|
|
return BaseWindowsDirectory.MaximumLength/2;
|
|
}
|
|
RtlMoveMemory(
|
|
lpBuffer,
|
|
BaseWindowsDirectory.Buffer,
|
|
BaseWindowsDirectory.Length
|
|
);
|
|
lpBuffer[(BaseWindowsDirectory.Length>>1)] = UNICODE_NULL;
|
|
return BaseWindowsDirectory.Length/2;
|
|
}
|
|
|
|
UINT
|
|
APIENTRY
|
|
GetSystemWow64DirectoryA(
|
|
LPSTR lpBuffer,
|
|
UINT uSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function obtains the pathname of the system wow64 directory.
|
|
|
|
Arguments:
|
|
|
|
lpBuffer - Points to the buffer that is to receive the
|
|
null-terminated character string containing the pathname.
|
|
|
|
uSize - Specifies the maximum size (in bytes) of the buffer. This
|
|
value should be set to at least MAX_PATH to allow sufficient room in
|
|
the buffer for the pathname.
|
|
|
|
Return Value:
|
|
|
|
The return value is the length of the string copied to lpBuffer, not
|
|
including the terminating null character. If the return value is
|
|
greater than uSize, the return value is the size of the buffer
|
|
required to hold the pathname. The return value is zero if the
|
|
function failed.
|
|
|
|
--*/
|
|
{
|
|
#if ! defined(BUILD_WOW6432) && ! defined(_WIN64)
|
|
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
|
|
return 0;
|
|
|
|
#else // BUILD_WOW6432 || _WIN64
|
|
|
|
const CHAR syswowdir[] = "\\" WOW64_SYSTEM_DIRECTORY;
|
|
UINT Available, Needed;
|
|
|
|
if (uSize < sizeof(syswowdir)) {
|
|
|
|
// We don't even have enough room to hold the syswow64
|
|
// subdirectory component, much less the whole path. Pass in a
|
|
// zero length so that we get back the length needed.
|
|
Available = 0;
|
|
|
|
} else {
|
|
|
|
// We might have enough room; decrement the size passed in by the
|
|
// amount of overhead we'll use.
|
|
Available = uSize - sizeof(syswowdir) + 1 /* NULL compensation */;
|
|
|
|
}
|
|
|
|
Needed = GetSystemWindowsDirectoryA(lpBuffer, Available);
|
|
|
|
if (Needed == 0) {
|
|
|
|
// The call failed -- just return zero.
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (Needed <= Available) {
|
|
|
|
// We had enough buffer space, even with our overhead; we can go
|
|
// ahead and tack on the syswow64 directory name.
|
|
|
|
RtlMoveMemory(lpBuffer + Needed,
|
|
syswowdir,
|
|
sizeof(syswowdir));
|
|
}
|
|
|
|
return (Needed + sizeof(syswowdir) - 1);
|
|
|
|
#endif // BUILD_WOW6432 || _WIN64
|
|
}
|
|
|
|
UINT
|
|
APIENTRY
|
|
GetSystemWow64DirectoryW(
|
|
LPWSTR lpBuffer,
|
|
UINT uSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function obtains the pathname of the system wow64 directory.
|
|
|
|
Arguments:
|
|
|
|
lpBuffer - Points to the buffer that is to receive the
|
|
null-terminated character string containing the pathname.
|
|
|
|
uSize - Specifies the maximum size (in wchars) of the buffer. This
|
|
value should be set to at least MAX_PATH to allow sufficient room in
|
|
the buffer for the pathname.
|
|
|
|
Return Value:
|
|
|
|
The return value is the length of the string copied to lpBuffer, not
|
|
including the terminating null character. If the return value is
|
|
greater than uSize, the return value is the size of the buffer
|
|
required to hold the pathname. The return value is zero if the
|
|
function failed.
|
|
|
|
--*/
|
|
{
|
|
#if ! defined(BUILD_WOW6432) && ! defined(_WIN64)
|
|
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
|
|
return 0;
|
|
|
|
#else // BUILD_WOW6432 || _WIN64
|
|
|
|
const WCHAR syswowdir[] = L"\\" WOW64_SYSTEM_DIRECTORY_U;
|
|
UINT Available, Needed;
|
|
const UINT SysWCharSize = sizeof(syswowdir) / sizeof(WCHAR);
|
|
|
|
if (uSize < SysWCharSize) {
|
|
|
|
// We don't even have enough room to hold the syswow64
|
|
// subdirectory component, much less the whole path. Pass in a
|
|
// zero length so that we get back the length needed.
|
|
Available = 0;
|
|
|
|
} else {
|
|
|
|
// We might have enough room; decrement the size passed in by the
|
|
// amount of overhead we'll use.
|
|
Available = uSize - SysWCharSize + 1 /* NULL compensation */;
|
|
|
|
}
|
|
|
|
Needed = GetSystemWindowsDirectoryW(lpBuffer, Available);
|
|
|
|
if (Needed == 0) {
|
|
|
|
// The call failed -- just return zero.
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (Needed <= Available) {
|
|
|
|
// We had enough buffer space, even with our overhead; we can go
|
|
// ahead and tack on the syswow64 directory name.
|
|
|
|
RtlMoveMemory(lpBuffer + Needed,
|
|
syswowdir,
|
|
sizeof(syswowdir));
|
|
}
|
|
|
|
return (Needed + SysWCharSize - 1);
|
|
|
|
#endif // BUILD_WOW6432 || _WIN64
|
|
}
|
|
|
|
|
|
UINT
|
|
APIENTRY
|
|
GetWindowsDirectoryA(
|
|
LPSTR lpBuffer,
|
|
UINT uSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
if (gpTermsrvGetWindowsDirectoryA) {
|
|
|
|
//
|
|
// If Terminal Server get the Per User Windows Directory
|
|
//
|
|
|
|
UINT retval;
|
|
if (retval = gpTermsrvGetWindowsDirectoryA(lpBuffer, uSize)) {
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
|
|
return GetSystemWindowsDirectoryA(lpBuffer,uSize);
|
|
}
|
|
|
|
UINT
|
|
APIENTRY
|
|
GetWindowsDirectoryW(
|
|
LPWSTR lpBuffer,
|
|
UINT uSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function obtains the pathname of the Windows directory. The
|
|
Windows directory contains such files as Windows applications,
|
|
initialization files, and help files.
|
|
425
|
|
The pathname retrieved by this function does not end with a
|
|
backslash unless the Windows directory is the root directory. For
|
|
example, if the Windows directory is named WINDOWS on drive C:, the
|
|
pathname of the Windows directory retrieved by this function is
|
|
C:\WINDOWS If Windows was installed in the root directory of drive
|
|
C:, the pathname retrieved by this function is C:\
|
|
|
|
Arguments:
|
|
|
|
lpBuffer - Points to the buffer that is to receive the
|
|
null-terminated character string containing the pathname.
|
|
|
|
uSize - Specifies the maximum size (in bytes) of the buffer. This
|
|
value should be set to at least MAX_PATH to allow sufficient room in
|
|
the buffer for the pathname.
|
|
|
|
Return Value:
|
|
|
|
The return value is the length of the string copied to lpBuffer, not
|
|
including the terminating null character. If the return value is
|
|
greater than uSize, the return value is the size of the buffer
|
|
required to hold the pathname. The return value is zero if the
|
|
function failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (gpTermsrvGetWindowsDirectoryW) {
|
|
//
|
|
// If Terminal Server get the Per User Windows Directory
|
|
//
|
|
|
|
UINT retval;
|
|
if (retval = gpTermsrvGetWindowsDirectoryW(lpBuffer, uSize)) {
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
return GetSystemWindowsDirectoryW(lpBuffer,uSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
UINT
|
|
APIENTRY
|
|
GetDriveTypeA(
|
|
LPCSTR lpRootPathName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ANSI thunk to GetDriveTypeW
|
|
|
|
--*/
|
|
|
|
{
|
|
PUNICODE_STRING Unicode;
|
|
LPCWSTR lpRootPathName_U;
|
|
|
|
if (ARGUMENT_PRESENT(lpRootPathName)) {
|
|
Unicode = Basep8BitStringToStaticUnicodeString( lpRootPathName );
|
|
if (Unicode == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
lpRootPathName_U = (LPCWSTR)Unicode->Buffer;
|
|
}
|
|
else {
|
|
lpRootPathName_U = NULL;
|
|
}
|
|
|
|
return GetDriveTypeW(lpRootPathName_U);
|
|
}
|
|
|
|
UINT
|
|
APIENTRY
|
|
GetDriveTypeW(
|
|
LPCWSTR lpRootPathName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function determines whether a disk drive is removeable, fixed,
|
|
remote, CD ROM, or a RAM disk.
|
|
|
|
The return value is zero if the function cannot determine the drive
|
|
type, or 1 if the specified root directory does not exist.
|
|
|
|
Arguments:
|
|
|
|
lpRootPathName - An optional parameter, that if specified, supplies
|
|
the root directory of the disk whose drive type is to be
|
|
determined. If this parameter is not specified, then the root
|
|
of the current directory is used.
|
|
|
|
Return Value:
|
|
|
|
The return value specifies the type of drive. It can be one of the
|
|
following values:
|
|
|
|
DRIVE_UNKNOWN - The drive type can not be determined.
|
|
|
|
DRIVE_NO_ROOT_DIR - The root directory does not exist.
|
|
|
|
DRIVE_REMOVABLE - Disk can be removed from the drive.
|
|
|
|
DRIVE_FIXED - Disk cannot be removed from the drive.
|
|
|
|
DRIVE_REMOTE - Drive is a remote (network) drive.
|
|
|
|
DRIVE_CDROM - Drive is a CD rom drive.
|
|
|
|
DRIVE_RAMDISK - Drive is a RAM disk.
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR wch;
|
|
ULONG n, DriveNumber;
|
|
WCHAR DefaultPath[MAX_PATH];
|
|
PWSTR RootPathName;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
HANDLE Handle;
|
|
UNICODE_STRING FileName, volumeNameString;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
BOOLEAN TranslationStatus;
|
|
PVOID FreeBuffer;
|
|
DWORD ReturnValue;
|
|
FILE_FS_DEVICE_INFORMATION DeviceInfo;
|
|
PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo;
|
|
WCHAR volumeName[MAX_PATH];
|
|
|
|
if (!ARGUMENT_PRESENT(lpRootPathName)) {
|
|
n = RtlGetCurrentDirectory_U(sizeof(DefaultPath), DefaultPath);
|
|
RootPathName = DefaultPath;
|
|
if (n > (3 * sizeof(WCHAR))) {
|
|
RootPathName[3]=UNICODE_NULL;
|
|
}
|
|
}
|
|
else
|
|
if (lpRootPathName == (PWSTR)IntToPtr(0xFFFFFFFF)) {
|
|
//
|
|
// Hack to be compatible with undocumented feature of old
|
|
// implementation.
|
|
//
|
|
|
|
return 0;
|
|
}
|
|
else {
|
|
//
|
|
// If input string is just C: then convert to C:\ so it does
|
|
// not default to current directory which may or may not be
|
|
// at the root.
|
|
//
|
|
RootPathName = (PWSTR)lpRootPathName;
|
|
if (wcslen( RootPathName ) == 2) {
|
|
wch = RtlUpcaseUnicodeChar( *RootPathName );
|
|
if (wch >= (WCHAR)'A' &&
|
|
wch <= (WCHAR)'Z' &&
|
|
RootPathName[1] == (WCHAR)':'
|
|
) {
|
|
RootPathName = wcscpy(DefaultPath, lpRootPathName);
|
|
RootPathName[2] = (WCHAR)'\\';
|
|
RootPathName[3] = UNICODE_NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If input string is of the form C:\ then look in the drive letter
|
|
// cache maintained by the kernel to see if the drive type is already
|
|
// known.
|
|
//
|
|
wch = RtlUpcaseUnicodeChar( *RootPathName );
|
|
if (wch >= (WCHAR)'A' &&
|
|
wch <= (WCHAR)'Z' &&
|
|
RootPathName[1]==(WCHAR)':' &&
|
|
RootPathName[2]==(WCHAR)'\\' &&
|
|
RootPathName[3]==UNICODE_NULL
|
|
) {
|
|
Status = NtQueryInformationProcess( NtCurrentProcess(),
|
|
ProcessDeviceMap,
|
|
&ProcessDeviceMapInfo.Query,
|
|
sizeof( ProcessDeviceMapInfo.Query ),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
RtlZeroMemory( &ProcessDeviceMapInfo, sizeof( ProcessDeviceMapInfo ) );
|
|
}
|
|
|
|
DriveNumber = wch - (WCHAR)'A';
|
|
if (ProcessDeviceMapInfo.Query.DriveMap & (1 << DriveNumber)) {
|
|
switch ( ProcessDeviceMapInfo.Query.DriveType[ DriveNumber ] ) {
|
|
case DOSDEVICE_DRIVE_UNKNOWN:
|
|
return DRIVE_UNKNOWN;
|
|
|
|
case DOSDEVICE_DRIVE_REMOVABLE:
|
|
return DRIVE_REMOVABLE;
|
|
|
|
case DOSDEVICE_DRIVE_FIXED:
|
|
return DRIVE_FIXED;
|
|
|
|
case DOSDEVICE_DRIVE_REMOTE:
|
|
return DRIVE_REMOTE;
|
|
|
|
case DOSDEVICE_DRIVE_CDROM:
|
|
return DRIVE_CDROM;
|
|
|
|
case DOSDEVICE_DRIVE_RAMDISK:
|
|
return DRIVE_RAMDISK;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Either not C:\ or kernel does not know the drive type, so try to
|
|
// calculate the drive type by opening the root directory and doing
|
|
// a query volume information.
|
|
//
|
|
|
|
|
|
//
|
|
// If curdir is a UNC connection, and default path is used,
|
|
// the RtlGetCurrentDirectory logic is wrong, so throw it away.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(lpRootPathName)) {
|
|
RootPathName = L"\\";
|
|
}
|
|
|
|
TranslationStatus = RtlDosPathNameToNtPathName_U( RootPathName,
|
|
&FileName,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (!TranslationStatus) {
|
|
return DRIVE_NO_ROOT_DIR;
|
|
}
|
|
FreeBuffer = FileName.Buffer;
|
|
|
|
//
|
|
// Check to make sure a root was specified
|
|
//
|
|
|
|
if (FileName.Buffer[(FileName.Length >> 1)-1] != '\\') {
|
|
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
|
|
return DRIVE_NO_ROOT_DIR;
|
|
}
|
|
|
|
FileName.Length -= 2;
|
|
InitializeObjectAttributes( &Obja,
|
|
&FileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
Status = NtOpenFile( &Handle,
|
|
(ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
|
|
);
|
|
|
|
//
|
|
//
|
|
// substd drives are really directories, so if we are dealing with one
|
|
// of them, bypass this
|
|
//
|
|
|
|
if ( Status == STATUS_FILE_IS_A_DIRECTORY ) {
|
|
|
|
if (BasepGetVolumeNameFromReparsePoint(lpRootPathName, volumeName,
|
|
MAX_PATH, NULL)) {
|
|
|
|
RtlInitUnicodeString(&volumeNameString, volumeName);
|
|
|
|
volumeNameString.Buffer[1] = '?';
|
|
volumeNameString.Length -= sizeof(WCHAR);
|
|
|
|
InitializeObjectAttributes( &Obja,
|
|
&volumeNameString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
}
|
|
|
|
Status = NtOpenFile(
|
|
&Handle,
|
|
(ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
}
|
|
|
|
else {
|
|
|
|
//
|
|
// check for substed drives another way just in case
|
|
//
|
|
|
|
FileName.Length = FileName.Length + sizeof((WCHAR)'\\');
|
|
if (!IsThisARootDirectory(NULL,&FileName) ) {
|
|
FileName.Length = FileName.Length - sizeof((WCHAR)'\\');
|
|
if (NT_SUCCESS(Status)) {
|
|
NtClose(Handle);
|
|
}
|
|
Status = NtOpenFile(
|
|
&Handle,
|
|
(ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
}
|
|
}
|
|
RtlFreeHeap( RtlProcessHeap(), 0, FreeBuffer );
|
|
if (!NT_SUCCESS( Status )) {
|
|
return DRIVE_NO_ROOT_DIR;
|
|
}
|
|
|
|
//
|
|
// Determine if this is a network or disk file system. If it
|
|
// is a disk file system determine if this is removable or not
|
|
//
|
|
|
|
Status = NtQueryVolumeInformationFile( Handle,
|
|
&IoStatusBlock,
|
|
&DeviceInfo,
|
|
sizeof(DeviceInfo),
|
|
FileFsDeviceInformation
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
ReturnValue = DRIVE_UNKNOWN;
|
|
}
|
|
else
|
|
if (DeviceInfo.Characteristics & FILE_REMOTE_DEVICE) {
|
|
ReturnValue = DRIVE_REMOTE;
|
|
}
|
|
else {
|
|
switch (DeviceInfo.DeviceType) {
|
|
|
|
case FILE_DEVICE_NETWORK:
|
|
case FILE_DEVICE_NETWORK_FILE_SYSTEM:
|
|
ReturnValue = DRIVE_REMOTE;
|
|
break;
|
|
|
|
case FILE_DEVICE_CD_ROM:
|
|
case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
|
|
ReturnValue = DRIVE_CDROM;
|
|
break;
|
|
|
|
case FILE_DEVICE_VIRTUAL_DISK:
|
|
ReturnValue = DRIVE_RAMDISK;
|
|
break;
|
|
|
|
case FILE_DEVICE_DISK:
|
|
case FILE_DEVICE_DISK_FILE_SYSTEM:
|
|
|
|
if ( DeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA ) {
|
|
ReturnValue = DRIVE_REMOVABLE;
|
|
}
|
|
else {
|
|
ReturnValue = DRIVE_FIXED;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ReturnValue = DRIVE_UNKNOWN;
|
|
break;
|
|
}
|
|
}
|
|
|
|
NtClose( Handle );
|
|
return ReturnValue;
|
|
}
|
|
|
|
DWORD
|
|
APIENTRY
|
|
SearchPathA(
|
|
LPCSTR lpPath,
|
|
LPCSTR lpFileName,
|
|
LPCSTR lpExtension,
|
|
DWORD nBufferLength,
|
|
LPSTR lpBuffer,
|
|
LPSTR *lpFilePart
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ANSI thunk to SearchPathW
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
UNICODE_STRING xlpPath;
|
|
PUNICODE_STRING Unicode;
|
|
UNICODE_STRING xlpExtension;
|
|
PWSTR xlpBuffer;
|
|
DWORD ReturnValue;
|
|
ANSI_STRING AnsiString;
|
|
UNICODE_STRING UnicodeString;
|
|
NTSTATUS Status;
|
|
PWSTR FilePart;
|
|
PWSTR *FilePartPtr;
|
|
|
|
if ( ARGUMENT_PRESENT(lpFilePart) ) {
|
|
FilePartPtr = &FilePart;
|
|
}
|
|
else {
|
|
FilePartPtr = NULL;
|
|
}
|
|
|
|
Unicode = Basep8BitStringToStaticUnicodeString( lpFileName );
|
|
if (Unicode == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(lpExtension) ) {
|
|
|
|
if (!Basep8BitStringToDynamicUnicodeString( &xlpExtension, lpExtension )) {
|
|
return 0;
|
|
}
|
|
|
|
} else {
|
|
xlpExtension.Buffer = NULL;
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(lpPath) ) {
|
|
|
|
if (!Basep8BitStringToDynamicUnicodeString( &xlpPath, lpPath )) {
|
|
if ( ARGUMENT_PRESENT(lpExtension) ) {
|
|
RtlFreeUnicodeString(&xlpExtension);
|
|
}
|
|
return 0;
|
|
}
|
|
} else {
|
|
xlpPath.Buffer = NULL;
|
|
}
|
|
|
|
xlpBuffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), nBufferLength<<1);
|
|
if ( !xlpBuffer ) {
|
|
BaseSetLastNTError(STATUS_NO_MEMORY);
|
|
ReturnValue = 0;
|
|
goto bail0;
|
|
}
|
|
ReturnValue = SearchPathW(
|
|
xlpPath.Buffer,
|
|
Unicode->Buffer,
|
|
xlpExtension.Buffer,
|
|
nBufferLength,
|
|
xlpBuffer,
|
|
FilePartPtr
|
|
);
|
|
//
|
|
// === DBCS modification note [takaok] ===
|
|
//
|
|
// SearchPathW retruns:
|
|
//
|
|
// buffer size needed(including null terminator) if buffer size is too small.
|
|
// number of characters( not including null terminator) if buffer size is enougth
|
|
//
|
|
// This means SearchPathW never returns value which is equal to nBufferLength.
|
|
//
|
|
|
|
if ( ReturnValue > nBufferLength ) {
|
|
//
|
|
// To know the ansi buffer size needed, we should get all of
|
|
// unicode string.
|
|
//
|
|
RtlFreeHeap(RtlProcessHeap(), 0,xlpBuffer);
|
|
xlpBuffer = RtlAllocateHeap(RtlProcessHeap(),
|
|
MAKE_TAG( TMP_TAG ),
|
|
ReturnValue * sizeof(WCHAR));
|
|
if ( !xlpBuffer ) {
|
|
BaseSetLastNTError(STATUS_NO_MEMORY);
|
|
goto bail0;
|
|
}
|
|
ReturnValue = SearchPathW(
|
|
xlpPath.Buffer,
|
|
Unicode->Buffer,
|
|
xlpExtension.Buffer,
|
|
ReturnValue,
|
|
xlpBuffer,
|
|
FilePartPtr
|
|
);
|
|
if ( ReturnValue > 0 ) {
|
|
//
|
|
// We called SearchPathW with the enough size of buffer.
|
|
// So, ReturnValue is the size of the path not including the
|
|
// terminating null character.
|
|
//
|
|
Status = RtlUnicodeToMultiByteSize( &ReturnValue,
|
|
xlpBuffer,
|
|
ReturnValue * sizeof(WCHAR));
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
ReturnValue = 0;
|
|
}
|
|
else {
|
|
ReturnValue += 1;
|
|
}
|
|
}
|
|
} else if ( ReturnValue > 0 ) {
|
|
|
|
INT AnsiByteCount;
|
|
|
|
//
|
|
// We have unicode string. We need to compute the ansi byte count
|
|
// of the string.
|
|
//
|
|
// ReturnValue : unicode character count not including null terminator
|
|
// AnsiByteCount : ansi byte count not including null terminator
|
|
//
|
|
Status = RtlUnicodeToMultiByteSize( &AnsiByteCount,
|
|
xlpBuffer,
|
|
ReturnValue * sizeof(WCHAR) );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
ReturnValue = 0;
|
|
}
|
|
else {
|
|
if ( AnsiByteCount < (INT)nBufferLength ) {
|
|
//
|
|
// The string (including null terminator) fits to the buffer
|
|
//
|
|
Status = RtlUnicodeToMultiByteN ( lpBuffer,
|
|
nBufferLength - 1,
|
|
&AnsiByteCount,
|
|
xlpBuffer,
|
|
ReturnValue * sizeof(WCHAR)
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
ReturnValue = 0;
|
|
}
|
|
else {
|
|
|
|
lpBuffer[ AnsiByteCount ] = '\0';
|
|
|
|
//
|
|
// The return value is the byte count copied to the buffer
|
|
// not including the terminating null character.
|
|
//
|
|
ReturnValue = AnsiByteCount;
|
|
|
|
|
|
if ( ARGUMENT_PRESENT(lpFilePart) ) {
|
|
if ( FilePart == NULL ) {
|
|
*lpFilePart = NULL;
|
|
} else {
|
|
|
|
INT PrefixLength;
|
|
|
|
PrefixLength = (INT)(FilePart - xlpBuffer);
|
|
Status = RtlUnicodeToMultiByteSize( &PrefixLength,
|
|
xlpBuffer,
|
|
PrefixLength * sizeof(WCHAR));
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
ReturnValue = 0;
|
|
}
|
|
else {
|
|
*lpFilePart = lpBuffer + PrefixLength;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// We should return the size of the buffer required to
|
|
// hold the path. The size should include the
|
|
// terminating null character.
|
|
//
|
|
ReturnValue = AnsiByteCount + 1;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
RtlFreeHeap(RtlProcessHeap(), 0,xlpBuffer);
|
|
bail0:
|
|
if ( ARGUMENT_PRESENT(lpExtension) ) {
|
|
RtlFreeUnicodeString(&xlpExtension);
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(lpPath) ) {
|
|
RtlFreeUnicodeString(&xlpPath);
|
|
}
|
|
return ReturnValue;
|
|
}
|
|
|
|
|
|
|
|
#ifdef WX86
|
|
|
|
ULONG
|
|
GetFullPathNameWithWx86Override(
|
|
PCWSTR lpFileName,
|
|
ULONG nBufferLength,
|
|
PWSTR lpBuffer,
|
|
PWSTR *lpFilePart
|
|
)
|
|
{
|
|
UNICODE_STRING FullPathName, PathUnicode, Wx86PathName;
|
|
PUNICODE_STRING FoundFileName;
|
|
RTL_PATH_TYPE PathType;
|
|
PWSTR FilePart;
|
|
ULONG Length, LengthPath;
|
|
ULONG PathNameLength;
|
|
|
|
FullPathName.Buffer = NULL;
|
|
Wx86PathName.Buffer = NULL;
|
|
|
|
if (lpFilePart) {
|
|
*lpFilePart = NULL;
|
|
}
|
|
|
|
FullPathName.MaximumLength = (USHORT)(MAX_PATH * sizeof(WCHAR)) + sizeof(WCHAR);
|
|
FullPathName.Length = 0;
|
|
FullPathName.Buffer = RtlAllocateHeap(RtlProcessHeap(),
|
|
MAKE_TAG( TMP_TAG ),
|
|
FullPathName.MaximumLength
|
|
);
|
|
if (!FullPathName.Buffer) {
|
|
PathNameLength = 0;
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto WDOExitCleanup;
|
|
}
|
|
|
|
FoundFileName = &FullPathName;
|
|
PathNameLength = RtlGetFullPathName_U(lpFileName,
|
|
FullPathName.MaximumLength,
|
|
FullPathName.Buffer,
|
|
&FilePart
|
|
);
|
|
|
|
if (!PathNameLength || PathNameLength >= FullPathName.MaximumLength) {
|
|
PathNameLength = 0;
|
|
goto WDOExitCleanup;
|
|
}
|
|
|
|
FullPathName.Length = (USHORT)PathNameLength;
|
|
|
|
|
|
PathUnicode = FullPathName;
|
|
PathUnicode.Length = (USHORT)((ULONG_PTR)FilePart -
|
|
(ULONG_PTR)FullPathName.Buffer);
|
|
|
|
PathUnicode.Length -= sizeof(WCHAR);
|
|
if (!RtlEqualUnicodeString(&PathUnicode, &BaseWindowsSystemDirectory, TRUE)) {
|
|
goto WDOExitCleanup;
|
|
}
|
|
|
|
|
|
Wx86PathName.MaximumLength = BaseWindowsSys32x86Directory.Length +
|
|
FullPathName.Length - PathUnicode.Length +
|
|
2*sizeof(WCHAR);
|
|
Wx86PathName.Length = 0;
|
|
Wx86PathName.Buffer = RtlAllocateHeap(RtlProcessHeap(),
|
|
MAKE_TAG( TMP_TAG ),
|
|
Wx86PathName.MaximumLength
|
|
);
|
|
|
|
if (!Wx86PathName.Buffer) {
|
|
goto WDOExitCleanup;
|
|
}
|
|
|
|
RtlCopyUnicodeString(&Wx86PathName, &BaseWindowsSys32x86Directory);
|
|
Length = Wx86PathName.Length + sizeof(WCHAR);
|
|
RtlAppendUnicodeToString (&Wx86PathName, FilePart - 1);
|
|
if (RtlDoesFileExists_U(Wx86PathName.Buffer)) {
|
|
FoundFileName = &Wx86PathName;
|
|
FilePart = Wx86PathName.Buffer + Length/sizeof(WCHAR);
|
|
}
|
|
|
|
|
|
|
|
WDOExitCleanup:
|
|
|
|
if (PathNameLength) {
|
|
if (FoundFileName->Length >= nBufferLength) {
|
|
PathNameLength = FoundFileName->Length + sizeof(WCHAR);
|
|
}
|
|
else {
|
|
RtlMoveMemory(lpBuffer,
|
|
FoundFileName->Buffer,
|
|
FoundFileName->Length + sizeof(WCHAR)
|
|
);
|
|
|
|
PathNameLength = FoundFileName->Length;
|
|
Length = (ULONG)(FilePart - FoundFileName->Buffer);
|
|
|
|
if (lpFilePart) {
|
|
*lpFilePart = lpBuffer + Length/sizeof(WCHAR);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (FullPathName.Buffer) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, FullPathName.Buffer);
|
|
}
|
|
|
|
if (Wx86PathName.Buffer) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Wx86PathName.Buffer);
|
|
}
|
|
|
|
return PathNameLength;
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
APIENTRY
|
|
SearchPathW(
|
|
LPCWSTR lpPath,
|
|
LPCWSTR lpFileName,
|
|
LPCWSTR lpExtension,
|
|
DWORD nBufferLength,
|
|
LPWSTR lpBuffer,
|
|
LPWSTR *lpFilePart
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to search for a file specifying a search path
|
|
and a filename. It returns with a fully qualified pathname of the
|
|
found file.
|
|
|
|
This function is used to locate a file using the specified path. If
|
|
the file is found, its fully qualified pathname is returned. In
|
|
addition to this, it calculates the address of the file name portion
|
|
of the fully qualified pathname.
|
|
|
|
Arguments:
|
|
|
|
lpPath - An optional parameter, that if specified, supplies the
|
|
search path to be used when locating the file. If this
|
|
parameter is not specified, the default windows search path is
|
|
used. The default path is:
|
|
|
|
- The current directory
|
|
|
|
- The windows directory
|
|
|
|
- The windows system directory
|
|
|
|
- The directories listed in the path environment variable
|
|
|
|
lpFileName - Supplies the file name of the file to search for.
|
|
|
|
lpExtension - An optional parameter, that if specified, supplies an
|
|
extension to be added to the filename when doing the search.
|
|
The extension is only added if the specified filename does not
|
|
end with an extension.
|
|
|
|
nBufferLength - Supplies the length in characters of the buffer that
|
|
is to receive the fully qualified path.
|
|
|
|
lpBuffer - Returns the fully qualified pathname corresponding to the
|
|
file that was found.
|
|
|
|
lpFilePart - Returns the address of the last component of the fully
|
|
qualified pathname.
|
|
|
|
Return Value:
|
|
|
|
The return value is the length of the string copied to lpBuffer, not
|
|
including the terminating null character. If the return value is
|
|
greater than nBufferLength, the return value is the size of the buffer
|
|
required to hold the pathname. The return value is zero if the
|
|
function failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING Path;
|
|
UNICODE_STRING FileName;
|
|
UNICODE_STRING DefaultExtension;
|
|
UNICODE_STRING CallersBuffer;
|
|
LPWSTR AllocatedPath = NULL;
|
|
RTL_PATH_TYPE PathType;
|
|
SIZE_T BytesRequired = 0;
|
|
SIZE_T FilePartPrefixCch = 0;
|
|
NTSTATUS Status;
|
|
DWORD dwReturnValue = 0;
|
|
|
|
//
|
|
// The guts of this function are now in common ntdll code; however the win32 search
|
|
// path has a few interesting differences from the ntdll search path code. First, it
|
|
// does not search the path if the filename is ".\foo" or "..\foo" and second, when the
|
|
// filename passed in is not a relative path but the file is not found, the default
|
|
// extension is applied regardless of whether the existing filename has an extension.
|
|
//
|
|
// These flags enable those feature-compatibility modes.
|
|
//
|
|
ULONG SearchPathFlags =
|
|
RTL_DOS_SEARCH_PATH_FLAG_DISALLOW_DOT_RELATIVE_PATH_SEARCH |
|
|
RTL_DOS_SEARCH_PATH_FLAG_APPLY_DEFAULT_EXTENSION_WHEN_NOT_RELATIVE_PATH_EVEN_IF_FILE_HAS_EXTENSION;
|
|
|
|
if (lpFilePart != NULL)
|
|
*lpFilePart = NULL;
|
|
|
|
Path.Buffer = NULL;
|
|
|
|
RtlInitUnicodeString(&FileName, lpFileName);
|
|
|
|
//
|
|
// trim trailing spaces, and then check for a real filelength
|
|
// if length is 0 (NULL, "", or " ") passed in then abort the
|
|
// search
|
|
//
|
|
|
|
while ((FileName.Length >= sizeof(WCHAR)) &&
|
|
(FileName.Buffer[(FileName.Length / sizeof(WCHAR)) - 1] == L' '))
|
|
FileName.Length -= sizeof(WCHAR);
|
|
|
|
if (FileName.Length == 0) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto Exit;
|
|
}
|
|
|
|
RtlInitUnicodeString(&DefaultExtension, lpExtension);
|
|
|
|
if ( !ARGUMENT_PRESENT(lpPath) ) {
|
|
SIZE_T Cch;
|
|
|
|
Path.Buffer = BaseComputeProcessSearchPath();
|
|
if (Path.Buffer == NULL) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto Exit;
|
|
}
|
|
|
|
Cch = lstrlenW(Path.Buffer);
|
|
|
|
if (Cch > UNICODE_STRING_MAX_CHARS) {
|
|
SetLastError(ERROR_FILENAME_EXCED_RANGE);
|
|
goto Exit;
|
|
}
|
|
|
|
Path.Length = (USHORT) (Cch * sizeof(WCHAR));
|
|
Path.MaximumLength = Path.Length;
|
|
SearchPathFlags |= RTL_DOS_SEARCH_PATH_FLAG_APPLY_ISOLATION_REDIRECTION;
|
|
} else {
|
|
Status = RtlInitUnicodeStringEx(&Path, lpPath);
|
|
if (NT_ERROR(Status)) {
|
|
BaseSetLastNTError(Status);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
CallersBuffer.Length = 0;
|
|
|
|
if (nBufferLength > UNICODE_STRING_MAX_CHARS) {
|
|
CallersBuffer.MaximumLength = UNICODE_STRING_MAX_BYTES;
|
|
} else {
|
|
CallersBuffer.MaximumLength = (USHORT) (nBufferLength * sizeof(WCHAR));
|
|
}
|
|
CallersBuffer.Buffer = lpBuffer;
|
|
|
|
Status = RtlDosSearchPath_Ustr(
|
|
SearchPathFlags,
|
|
&Path,
|
|
&FileName,
|
|
&DefaultExtension,
|
|
&CallersBuffer,
|
|
NULL, // dynamicstring
|
|
NULL, // fullfilenameout
|
|
&FilePartPrefixCch,
|
|
&BytesRequired);
|
|
if (NT_ERROR(Status)) {
|
|
|
|
#if DBG
|
|
// Don't bother with debug spew for the two common expected cases.
|
|
if ((Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL)) {
|
|
DbgPrint("%s on file %wZ failed; NTSTATUS = %08lx\n", __FUNCTION__, &FileName, Status);
|
|
DbgPrint(" Path = %wZ\n", &Path);
|
|
}
|
|
#endif // DBG
|
|
|
|
if (Status == STATUS_BUFFER_TOO_SMALL) {
|
|
SIZE_T CchRequired = BytesRequired / sizeof(WCHAR);
|
|
if (CchRequired > 0xffffffff) {
|
|
SetLastError(ERROR_FILENAME_EXCED_RANGE);
|
|
goto Exit;
|
|
}
|
|
dwReturnValue = (DWORD) CchRequired;
|
|
goto Exit;
|
|
}
|
|
|
|
// Only set the last error if it wasn't an insufficient buffer; this is just preserving
|
|
// Windows 2000 behavior.
|
|
BaseSetLastNTError(Status);
|
|
goto Exit;
|
|
}
|
|
|
|
#ifdef WX86
|
|
if (UseKnownWx86Dll) {
|
|
WCHAR TempBuffer[MAX_PATH];
|
|
|
|
RtlCopyMemory(TempBuffer, lpBuffer, CallersBuffer.Length);
|
|
TempBuffer[CallersBuffer.Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
dwReturnValue = GetFullPathNameWithWx86Override(
|
|
TempBuffer,
|
|
nBufferLength,
|
|
lpBuffer,
|
|
lpFilePart
|
|
);
|
|
goto Exit;
|
|
|
|
} else if (lpFilePart != NULL) {
|
|
*lpFilePart = lpBuffer + FilePartPrefixCch;
|
|
}
|
|
#else
|
|
if (lpFilePart != NULL) {
|
|
*lpFilePart = lpBuffer + FilePartPrefixCch;
|
|
}
|
|
#endif // WX86
|
|
|
|
dwReturnValue = CallersBuffer.Length / sizeof(WCHAR);
|
|
|
|
Exit:
|
|
if ((Path.Buffer != lpPath) && (Path.Buffer != NULL))
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Path.Buffer);
|
|
|
|
return dwReturnValue;
|
|
}
|
|
|
|
|
|
DWORD
|
|
APIENTRY
|
|
GetTempPathA(
|
|
DWORD nBufferLength,
|
|
LPSTR lpBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ANSI thunk to GetTempPathW
|
|
|
|
--*/
|
|
|
|
{
|
|
ANSI_STRING AnsiString;
|
|
UNICODE_STRING UnicodeString;
|
|
NTSTATUS Status;
|
|
ULONG cbAnsiString;
|
|
|
|
UnicodeString.MaximumLength = (USHORT)((nBufferLength<<1)+sizeof(UNICODE_NULL));
|
|
UnicodeString.Buffer = RtlAllocateHeap(
|
|
RtlProcessHeap(), MAKE_TAG( TMP_TAG ),
|
|
UnicodeString.MaximumLength
|
|
);
|
|
if ( !UnicodeString.Buffer ) {
|
|
BaseSetLastNTError(STATUS_NO_MEMORY);
|
|
return 0;
|
|
}
|
|
UnicodeString.Length = (USHORT)GetTempPathW(
|
|
(DWORD)(UnicodeString.MaximumLength-sizeof(UNICODE_NULL))/2,
|
|
UnicodeString.Buffer
|
|
)*2;
|
|
if ( UnicodeString.Length > (USHORT)(UnicodeString.MaximumLength-sizeof(UNICODE_NULL)) ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0,UnicodeString.Buffer);
|
|
|
|
//
|
|
// given buffer size is too small.
|
|
// allocate enough size of buffer and try again
|
|
//
|
|
// we need to get entire unicode temporary path
|
|
// otherwise we can't figure out the exact length
|
|
// of corresponding ansi string (cbAnsiString).
|
|
|
|
UnicodeString.Buffer = RtlAllocateHeap ( RtlProcessHeap(),
|
|
MAKE_TAG( TMP_TAG ),
|
|
UnicodeString.Length+ sizeof(UNICODE_NULL));
|
|
if ( !UnicodeString.Buffer ) {
|
|
BaseSetLastNTError(STATUS_NO_MEMORY);
|
|
return 0;
|
|
}
|
|
|
|
UnicodeString.Length = (USHORT)GetTempPathW(
|
|
(DWORD)(UnicodeString.Length)/2,
|
|
UnicodeString.Buffer) * 2;
|
|
Status = RtlUnicodeToMultiByteSize( &cbAnsiString,
|
|
UnicodeString.Buffer,
|
|
UnicodeString.Length );
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, UnicodeString.Buffer);
|
|
BaseSetLastNTError(Status);
|
|
return 0;
|
|
}
|
|
else if ( nBufferLength <= cbAnsiString ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, UnicodeString.Buffer);
|
|
return cbAnsiString + sizeof(ANSI_NULL);
|
|
}
|
|
}
|
|
AnsiString.Buffer = lpBuffer;
|
|
AnsiString.MaximumLength = (USHORT)(nBufferLength+1);
|
|
Status = BasepUnicodeStringTo8BitString(&AnsiString,&UnicodeString,FALSE);
|
|
RtlFreeHeap(RtlProcessHeap(), 0,UnicodeString.Buffer);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
return 0;
|
|
}
|
|
return AnsiString.Length;
|
|
}
|
|
|
|
DWORD
|
|
APIENTRY
|
|
GetTempPathW(
|
|
DWORD nBufferLength,
|
|
LPWSTR lpBuffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to return the pathname of the directory that
|
|
should be used to create temporary files.
|
|
|
|
Arguments:
|
|
|
|
nBufferLength - Supplies the length in bytes of the buffer that is
|
|
to receive the temporary file path.
|
|
|
|
lpBuffer - Returns the pathname of the directory that should be used
|
|
to create temporary files in.
|
|
|
|
Return Value:
|
|
|
|
The return value is the length of the string copied to lpBuffer, not
|
|
including the terminating null character. If the return value is
|
|
greater than nSize, the return value is the size of the buffer
|
|
required to hold the pathname. The return value is zero if the
|
|
function failed.
|
|
|
|
--*/
|
|
{
|
|
return BasepGetTempPathW(0, nBufferLength, lpBuffer);
|
|
}
|
|
|
|
DWORD
|
|
APIENTRY
|
|
BasepGetTempPathW(
|
|
ULONG Flags,
|
|
DWORD nBufferLength,
|
|
LPWSTR lpBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to return the pathname of the directory that
|
|
should be used to create temporary files.
|
|
|
|
Arguments:
|
|
|
|
nBufferLength - Supplies the length in bytes of the buffer that is
|
|
to receive the temporary file path.
|
|
|
|
lpBuffer - Returns the pathname of the directory that should be used
|
|
to create temporary files in.
|
|
|
|
Flags -
|
|
|
|
Return Value:
|
|
|
|
The return value is the length of the string copied to lpBuffer, not
|
|
including the terminating null character. If the return value is
|
|
greater than nSize, the return value is the size of the buffer
|
|
required to hold the pathname. The return value is zero if the
|
|
function failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
DWORD Length;
|
|
BOOLEAN AddTrailingSlash;
|
|
UNICODE_STRING EnvironmentValue;
|
|
NTSTATUS Status;
|
|
LPWSTR Name;
|
|
ULONG Position;
|
|
|
|
if (
|
|
(Flags & ~BASEP_GET_TEMP_PATH_PRESERVE_TEB) != 0
|
|
) {
|
|
BaseSetLastNTError(STATUS_INVALID_PARAMETER_1);
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Some apps don't work with the new long path for the temp directory
|
|
//
|
|
|
|
if (APPCOMPATFLAG(KACF_GETTEMPPATH)) {
|
|
|
|
#define OLD_TEMP_PATH L"c:\\temp\\"
|
|
#define OLD_TEMP_PATH_SIZE (sizeof(OLD_TEMP_PATH) / sizeof(WCHAR))
|
|
|
|
BOOL bRet;
|
|
|
|
//
|
|
// If there isn't enough space provided in the buffer return
|
|
// the desired size.
|
|
//
|
|
|
|
if (nBufferLength < OLD_TEMP_PATH_SIZE) {
|
|
return OLD_TEMP_PATH_SIZE;
|
|
}
|
|
|
|
wcscpy(lpBuffer, OLD_TEMP_PATH);
|
|
|
|
//
|
|
// Use the correct drive letter
|
|
//
|
|
|
|
lpBuffer[0] = BaseWindowsDirectory.Buffer[0];
|
|
|
|
bRet = CreateDirectoryW(lpBuffer, NULL);
|
|
|
|
if (!bRet) {
|
|
|
|
if (GetLastError() != ERROR_ALREADY_EXISTS)
|
|
return 0;
|
|
}
|
|
|
|
return OLD_TEMP_PATH_SIZE - 1;
|
|
}
|
|
|
|
nBufferLength *= 2;
|
|
EnvironmentValue = NtCurrentTeb()->StaticUnicodeString;
|
|
if (Flags & BASEP_GET_TEMP_PATH_PRESERVE_TEB) {
|
|
EnvironmentValue.Buffer = (PWSTR)RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), EnvironmentValue.MaximumLength);
|
|
if (EnvironmentValue.Buffer == NULL) {
|
|
BaseSetLastNTError(STATUS_NO_MEMORY);
|
|
return 0;
|
|
}
|
|
}
|
|
__try {
|
|
|
|
AddTrailingSlash = FALSE;
|
|
|
|
Status = RtlQueryEnvironmentVariable_U(NULL,&BaseTmpVariableName,&EnvironmentValue);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
Status = RtlQueryEnvironmentVariable_U(NULL,&BaseTempVariableName,&EnvironmentValue);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
Status = RtlQueryEnvironmentVariable_U(NULL,&BaseUserProfileVariableName,&EnvironmentValue);
|
|
}
|
|
}
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
Name = EnvironmentValue.Buffer;
|
|
if ( Name[(EnvironmentValue.Length>>1)-1] != (WCHAR)'\\' ) {
|
|
AddTrailingSlash = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
Name = BaseWindowsDirectory.Buffer;
|
|
if ( Name[(BaseWindowsDirectory.Length>>1)-1] != (WCHAR)'\\' ) {
|
|
AddTrailingSlash = TRUE;
|
|
}
|
|
}
|
|
|
|
Length = RtlGetFullPathName_U(
|
|
Name,
|
|
nBufferLength,
|
|
lpBuffer,
|
|
NULL
|
|
);
|
|
Position = Length>>1;
|
|
|
|
//
|
|
// Make sure there is room for a trailing back slash
|
|
//
|
|
|
|
if ( Length && Length < nBufferLength ) {
|
|
if ( lpBuffer[Position-1] != '\\' ) {
|
|
if ( Length+sizeof((WCHAR)'\\') < nBufferLength ) {
|
|
lpBuffer[Position] = (WCHAR)'\\';
|
|
lpBuffer[Position+1] = UNICODE_NULL;
|
|
return (Length+sizeof((WCHAR)'\\'))/2;
|
|
}
|
|
else {
|
|
return (Length+sizeof((WCHAR)'\\')+sizeof(UNICODE_NULL))/2;
|
|
}
|
|
}
|
|
else {
|
|
return Length/2;
|
|
}
|
|
}
|
|
else {
|
|
if ( AddTrailingSlash ) {
|
|
Length += sizeof((WCHAR)'\\');
|
|
}
|
|
return Length/2;
|
|
}
|
|
}
|
|
__finally {
|
|
if (Flags & BASEP_GET_TEMP_PATH_PRESERVE_TEB) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, EnvironmentValue.Buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
UINT
|
|
APIENTRY
|
|
GetTempFileNameA(
|
|
LPCSTR lpPathName,
|
|
LPCSTR lpPrefixString,
|
|
UINT uUnique,
|
|
LPSTR lpTempFileName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ANSI thunk to GetTempFileNameW
|
|
|
|
--*/
|
|
|
|
{
|
|
PUNICODE_STRING Unicode;
|
|
UNICODE_STRING UnicodePrefix;
|
|
NTSTATUS Status;
|
|
UINT ReturnValue;
|
|
UNICODE_STRING UnicodeResult;
|
|
|
|
Unicode = Basep8BitStringToStaticUnicodeString( lpPathName );
|
|
if (Unicode == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (!Basep8BitStringToDynamicUnicodeString( &UnicodePrefix, lpPrefixString )) {
|
|
return 0;
|
|
}
|
|
|
|
UnicodeResult.MaximumLength = (USHORT)((MAX_PATH<<1)+sizeof(UNICODE_NULL));
|
|
UnicodeResult.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), UnicodeResult.MaximumLength);
|
|
if ( !UnicodeResult.Buffer ) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
RtlFreeUnicodeString(&UnicodePrefix);
|
|
return 0;
|
|
}
|
|
|
|
ReturnValue = GetTempFileNameW(
|
|
Unicode->Buffer,
|
|
UnicodePrefix.Buffer,
|
|
uUnique,
|
|
UnicodeResult.Buffer
|
|
);
|
|
if ( ReturnValue ) {
|
|
ANSI_STRING AnsiString;
|
|
|
|
RtlInitUnicodeString(&UnicodeResult,UnicodeResult.Buffer);
|
|
AnsiString.Buffer = lpTempFileName;
|
|
AnsiString.MaximumLength = MAX_PATH+1;
|
|
Status = BasepUnicodeStringTo8BitString(&AnsiString,&UnicodeResult,FALSE);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
ReturnValue = 0;
|
|
}
|
|
}
|
|
RtlFreeUnicodeString(&UnicodePrefix);
|
|
RtlFreeHeap(RtlProcessHeap(), 0,UnicodeResult.Buffer);
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
UINT
|
|
APIENTRY
|
|
GetTempFileNameW(
|
|
LPCWSTR lpPathName,
|
|
LPCWSTR lpPrefixString,
|
|
UINT uUnique,
|
|
LPWSTR lpTempFileName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates a temporary filename of the following form:
|
|
|
|
drive:\path\prefixuuuu.tmp
|
|
|
|
In this syntax line, drive:\path\ is the path specified by the
|
|
lpPathName parameter; prefix is all the letters (up to the first
|
|
three) of the string pointed to by the lpPrefixString parameter; and
|
|
uuuu is the hexadecimal value of the number specified by the
|
|
uUnique parameter.
|
|
|
|
To avoid problems resulting from converting OEM character an string
|
|
to an ANSI string, an application should call the CreateFile
|
|
function to create the temporary file.
|
|
|
|
If the uUnique parameter is zero, GetTempFileName attempts to form a
|
|
unique number based on the current system time. If a file with the
|
|
resulting filename exists, the number is increased by one and the
|
|
test for existence is repeated. This continues until a unique
|
|
filename is found; GetTempFileName then creates a file by that name
|
|
and closes it. No attempt is made to create and open the file when
|
|
uUnique is nonzero.
|
|
|
|
Arguments:
|
|
|
|
lpPathName - Specifies the null terminated pathname of the directory
|
|
to create the temporary file within.
|
|
|
|
lpPrefixString - Points to a null-terminated character string to be
|
|
used as the temporary filename prefix. This string must consist
|
|
of characters in the OEM-defined character set.
|
|
|
|
uUnique - Specifies an unsigned integer.
|
|
|
|
lpTempFileName - Points to the buffer that is to receive the
|
|
temporary filename. This string consists of characters in the
|
|
OEM-defined character set. This buffer should be at least MAX_PATH
|
|
characters in length to allow sufficient room for the pathname.
|
|
|
|
Return Value:
|
|
|
|
The return value specifies a unique numeric value used in the
|
|
temporary filename. If a nonzero value was given for the uUnique
|
|
parameter, the return value specifies this same number.
|
|
|
|
--*/
|
|
|
|
{
|
|
#if !defined(BUILD_WOW6432)
|
|
BASE_API_MSG m;
|
|
PBASE_GETTEMPFILE_MSG a = &m.u.GetTempFile;
|
|
#endif
|
|
LPWSTR p,savedp;
|
|
ULONG Length;
|
|
HANDLE FileHandle;
|
|
ULONG PassCount;
|
|
DWORD LastError;
|
|
UNICODE_STRING UnicodePath, UnicodePrefix;
|
|
CHAR UniqueAsAnsi[8];
|
|
CHAR *c;
|
|
ULONG i;
|
|
|
|
#if defined(BUILD_WOW6432)
|
|
UINT uNewUnique;
|
|
#endif
|
|
|
|
PassCount = 0;
|
|
RtlInitUnicodeString(&UnicodePath,lpPathName);
|
|
Length = UnicodePath.Length;
|
|
|
|
RtlMoveMemory(lpTempFileName,lpPathName,UnicodePath.MaximumLength);
|
|
if ( !Length || lpTempFileName[(Length>>1)-1] != (WCHAR)'\\' ) {
|
|
lpTempFileName[Length>>1] = (WCHAR)'\\';
|
|
Length += sizeof(UNICODE_NULL);
|
|
}
|
|
|
|
lpTempFileName[(Length>>1)-1] = UNICODE_NULL;
|
|
i = GetFileAttributesW(lpTempFileName);
|
|
if (i == 0xFFFFFFFF) {
|
|
lpTempFileName[(Length>>1)-1] = (WCHAR)'\\';
|
|
lpTempFileName[(Length>>1)] = UNICODE_NULL;
|
|
i = GetFileAttributesW(lpTempFileName);
|
|
lpTempFileName[(Length>>1)-1] = UNICODE_NULL;
|
|
}
|
|
if ( (i == 0xFFFFFFFF) ||
|
|
!(i & FILE_ATTRIBUTE_DIRECTORY) ) {
|
|
SetLastError(ERROR_DIRECTORY);
|
|
return FALSE;
|
|
}
|
|
lpTempFileName[(Length>>1)-1] = (WCHAR)'\\';
|
|
|
|
RtlInitUnicodeString(&UnicodePrefix,lpPrefixString);
|
|
if ( UnicodePrefix.Length > (USHORT)6 ) {
|
|
UnicodePrefix.Length = (USHORT)6;
|
|
}
|
|
p = &lpTempFileName[Length>>1];
|
|
Length = UnicodePrefix.Length;
|
|
RtlMoveMemory(p,lpPrefixString,Length);
|
|
p += (Length>>1);
|
|
savedp = p;
|
|
//
|
|
// If uUnique is not specified, then get one
|
|
//
|
|
|
|
uUnique = uUnique & 0x0000ffff;
|
|
|
|
try_again:
|
|
p = savedp;
|
|
if ( !uUnique ) {
|
|
|
|
#if defined(BUILD_WOW6432)
|
|
uNewUnique = CsrBasepGetTempFile();
|
|
if ( uNewUnique == 0 ) {
|
|
#else
|
|
CsrClientCallServer( (PCSR_API_MSG)&m,
|
|
NULL,
|
|
CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX,
|
|
BasepGetTempFile
|
|
),
|
|
sizeof( *a )
|
|
);
|
|
a->uUnique = (UINT)m.ReturnValue;
|
|
if ( m.ReturnValue == 0 ) {
|
|
#endif
|
|
|
|
PassCount++;
|
|
if ( PassCount & 0xffff0000 ) {
|
|
return 0;
|
|
}
|
|
goto try_again;
|
|
}
|
|
}
|
|
else {
|
|
#if defined(BUILD_WOW6432)
|
|
uNewUnique = uUnique;
|
|
#else
|
|
a->uUnique = uUnique;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Convert the unique value to a 4 byte character string
|
|
//
|
|
|
|
#if defined(BUILD_WOW6432)
|
|
RtlIntegerToChar ((ULONG) uNewUnique,16,5,UniqueAsAnsi);
|
|
#else
|
|
RtlIntegerToChar ((ULONG) a->uUnique,16,5,UniqueAsAnsi);
|
|
#endif
|
|
c = UniqueAsAnsi;
|
|
for(i=0;i<4;i++){
|
|
*p = RtlAnsiCharToUnicodeChar(&c);
|
|
if ( *p == UNICODE_NULL ) {
|
|
break;
|
|
}
|
|
p++;
|
|
}
|
|
RtlMoveMemory(p,BaseDotTmpSuffixName.Buffer,BaseDotTmpSuffixName.MaximumLength);
|
|
|
|
if ( !uUnique ) {
|
|
|
|
//
|
|
// test for resulting name being a device (prefix com, uUnique 1-9...
|
|
//
|
|
|
|
if ( RtlIsDosDeviceName_U(lpTempFileName) ) {
|
|
PassCount++;
|
|
if ( PassCount & 0xffff0000 ) {
|
|
SetLastError(ERROR_INVALID_NAME);
|
|
return 0;
|
|
}
|
|
goto try_again;
|
|
}
|
|
|
|
FileHandle = CreateFileW(
|
|
lpTempFileName,
|
|
GENERIC_READ,
|
|
0,
|
|
NULL,
|
|
CREATE_NEW,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
//
|
|
// If the create worked, then we are ok. Just close the file.
|
|
// Otherwise, try again.
|
|
//
|
|
|
|
if ( FileHandle != INVALID_HANDLE_VALUE ) {
|
|
NtClose(FileHandle);
|
|
}
|
|
else {
|
|
|
|
//
|
|
// FIXFIX This test should be inverted when time permits
|
|
// sufficient testing to nail down the error codes that
|
|
// would indicate is is reasonable to continue the loop
|
|
// as opposed to stop the loop. All it currently takes is
|
|
// CreateFile coming back with an error we don't know about
|
|
// to make us spin here for a long time.
|
|
//
|
|
|
|
LastError = GetLastError();
|
|
switch (LastError) {
|
|
case ERROR_INVALID_PARAMETER :
|
|
case ERROR_WRITE_PROTECT :
|
|
case ERROR_FILE_NOT_FOUND :
|
|
case ERROR_BAD_PATHNAME :
|
|
case ERROR_INVALID_NAME :
|
|
case ERROR_PATH_NOT_FOUND :
|
|
case ERROR_NETWORK_ACCESS_DENIED :
|
|
case ERROR_DISK_CORRUPT :
|
|
case ERROR_FILE_CORRUPT :
|
|
case ERROR_DISK_FULL :
|
|
return 0;
|
|
case ERROR_ACCESS_DENIED :
|
|
// It's possible for us to hit this if there's a
|
|
// directory with the name we're trying; in that
|
|
// case, we can usefully continue.
|
|
// CreateFile() uses BaseSetLastNTError() to set
|
|
// LastStatusValue to the actual NT error in the
|
|
// TEB; we just need to check it, and only abort
|
|
// if it's not a directory.
|
|
// This was bug #397477.
|
|
if (NtCurrentTeb()->LastStatusValue
|
|
!= STATUS_FILE_IS_A_DIRECTORY)
|
|
return 0;
|
|
}
|
|
|
|
PassCount++;
|
|
if ( PassCount & 0xffff0000 ) {
|
|
return 0;
|
|
}
|
|
goto try_again;
|
|
}
|
|
}
|
|
#if defined(BUILD_WOW6432)
|
|
return uNewUnique;
|
|
#else
|
|
return a->uUnique;
|
|
#endif
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
GetDiskFreeSpaceA(
|
|
LPCSTR lpRootPathName,
|
|
LPDWORD lpSectorsPerCluster,
|
|
LPDWORD lpBytesPerSector,
|
|
LPDWORD lpNumberOfFreeClusters,
|
|
LPDWORD lpTotalNumberOfClusters
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ANSI thunk to GetDiskFreeSpaceW
|
|
|
|
--*/
|
|
|
|
{
|
|
PUNICODE_STRING Unicode;
|
|
|
|
if (!ARGUMENT_PRESENT( lpRootPathName )) {
|
|
lpRootPathName = "\\";
|
|
}
|
|
|
|
Unicode = Basep8BitStringToStaticUnicodeString( lpRootPathName );
|
|
if (Unicode == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
return ( GetDiskFreeSpaceW(
|
|
(LPCWSTR)Unicode->Buffer,
|
|
lpSectorsPerCluster,
|
|
lpBytesPerSector,
|
|
lpNumberOfFreeClusters,
|
|
lpTotalNumberOfClusters
|
|
)
|
|
);
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
GetDiskFreeSpaceW(
|
|
LPCWSTR lpRootPathName,
|
|
LPDWORD lpSectorsPerCluster,
|
|
LPDWORD lpBytesPerSector,
|
|
LPDWORD lpNumberOfFreeClusters,
|
|
LPDWORD lpTotalNumberOfClusters
|
|
)
|
|
|
|
#define MAKE2GFRIENDLY(lpOut, dwSize) \
|
|
\
|
|
if (!bAppHack) { \
|
|
*lpOut = dwSize; \
|
|
} else { \
|
|
dwTemp = SizeInfo.SectorsPerAllocationUnit * SizeInfo.BytesPerSector; \
|
|
\
|
|
if (0x7FFFFFFF / dwTemp < dwSize) { \
|
|
\
|
|
*lpOut = 0x7FFFFFFF / dwTemp; \
|
|
} else { \
|
|
*lpOut = dwSize; \
|
|
} \
|
|
}
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The free space on a disk and the size parameters can be returned
|
|
using GetDiskFreeSpace.
|
|
|
|
Arguments:
|
|
|
|
lpRootPathName - An optional parameter, that if specified, supplies
|
|
the root directory of the disk whose free space is to be
|
|
returned for. If this parameter is not specified, then the root
|
|
of the current directory is used.
|
|
|
|
lpSectorsPerCluster - Returns the number of sectors per cluster
|
|
where a cluster is the allocation granularity on the disk.
|
|
|
|
lpBytesPerSector - Returns the number of bytes per sector.
|
|
|
|
lpNumberOfFreeClusters - Returns the total number of free clusters
|
|
on the disk.
|
|
|
|
lpTotalNumberOfClusters - Returns the total number of clusters on
|
|
the disk.
|
|
|
|
Return Value:
|
|
|
|
TRUE - The operation was successful.
|
|
|
|
FALSE/NULL - The operation failed. Extended error status is available
|
|
using GetLastError.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
HANDLE Handle;
|
|
UNICODE_STRING FileName;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
BOOLEAN TranslationStatus;
|
|
PVOID FreeBuffer;
|
|
FILE_FS_SIZE_INFORMATION SizeInfo;
|
|
WCHAR DefaultPath[2];
|
|
DWORD dwTemp;
|
|
BOOL bAppHack;
|
|
|
|
DefaultPath[0] = (WCHAR)'\\';
|
|
DefaultPath[1] = UNICODE_NULL;
|
|
|
|
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
|
ARGUMENT_PRESENT(lpRootPathName) ? lpRootPathName : DefaultPath,
|
|
&FileName,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if ( !TranslationStatus ) {
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
return FALSE;
|
|
}
|
|
|
|
FreeBuffer = FileName.Buffer;
|
|
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&FileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
|
|
Status = NtOpenFile(
|
|
&Handle,
|
|
(ACCESS_MASK)FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE | FILE_OPEN_FOR_FREE_SPACE_QUERY
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
|
|
|
|
//
|
|
// Prior releases of NT where these parameters were not optional
|
|
// zeroed out this field even in the failure case. Some applications
|
|
// failed to check the return value from this function and instead
|
|
// relied on this side effect. I'm putting that back now so the apps
|
|
// can still treat an unformatted volume as a zero size volume.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT( lpBytesPerSector )) {
|
|
*lpBytesPerSector = 0;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
|
|
|
|
//
|
|
// Determine the size parameters of the volume.
|
|
//
|
|
|
|
Status = NtQueryVolumeInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
&SizeInfo,
|
|
sizeof(SizeInfo),
|
|
FileFsSizeInformation
|
|
);
|
|
NtClose(Handle);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// See if the calling process needs hack to work with HDD > 2GB
|
|
// 2GB is 0x80000000 bytes and some apps treat that as a signed LONG.
|
|
//
|
|
|
|
if (APPCOMPATFLAG(KACF_GETDISKFREESPACE)) {
|
|
|
|
bAppHack = TRUE;
|
|
} else {
|
|
bAppHack = FALSE;
|
|
}
|
|
|
|
//
|
|
// Deal with 64 bit sizes
|
|
//
|
|
|
|
if ( SizeInfo.TotalAllocationUnits.HighPart ) {
|
|
SizeInfo.TotalAllocationUnits.LowPart = (ULONG)-1;
|
|
}
|
|
if ( SizeInfo.AvailableAllocationUnits.HighPart ) {
|
|
SizeInfo.AvailableAllocationUnits.LowPart = (ULONG)-1;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT( lpSectorsPerCluster )) {
|
|
*lpSectorsPerCluster = SizeInfo.SectorsPerAllocationUnit;
|
|
}
|
|
if (ARGUMENT_PRESENT( lpBytesPerSector )) {
|
|
*lpBytesPerSector = SizeInfo.BytesPerSector;
|
|
}
|
|
if (ARGUMENT_PRESENT( lpNumberOfFreeClusters )) {
|
|
MAKE2GFRIENDLY(lpNumberOfFreeClusters, SizeInfo.AvailableAllocationUnits.LowPart);
|
|
}
|
|
if (ARGUMENT_PRESENT( lpTotalNumberOfClusters )) {
|
|
MAKE2GFRIENDLY(lpTotalNumberOfClusters, SizeInfo.TotalAllocationUnits.LowPart);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
WINBASEAPI
|
|
BOOL
|
|
WINAPI
|
|
GetDiskFreeSpaceExA(
|
|
LPCSTR lpDirectoryName,
|
|
PULARGE_INTEGER lpFreeBytesAvailableToCaller,
|
|
PULARGE_INTEGER lpTotalNumberOfBytes,
|
|
PULARGE_INTEGER lpTotalNumberOfFreeBytes
|
|
)
|
|
{
|
|
PUNICODE_STRING Unicode;
|
|
|
|
if (!ARGUMENT_PRESENT( lpDirectoryName )) {
|
|
lpDirectoryName = "\\";
|
|
}
|
|
|
|
Unicode = Basep8BitStringToStaticUnicodeString( lpDirectoryName );
|
|
if (Unicode == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
return ( GetDiskFreeSpaceExW(
|
|
(LPCWSTR)Unicode->Buffer,
|
|
lpFreeBytesAvailableToCaller,
|
|
lpTotalNumberOfBytes,
|
|
lpTotalNumberOfFreeBytes
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
WINBASEAPI
|
|
BOOL
|
|
WINAPI
|
|
GetDiskFreeSpaceExW(
|
|
LPCWSTR lpDirectoryName,
|
|
PULARGE_INTEGER lpFreeBytesAvailableToCaller,
|
|
PULARGE_INTEGER lpTotalNumberOfBytes,
|
|
PULARGE_INTEGER lpTotalNumberOfFreeBytes
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
HANDLE Handle;
|
|
UNICODE_STRING FileName;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
BOOLEAN TranslationStatus;
|
|
PVOID FreeBuffer;
|
|
union {
|
|
FILE_FS_SIZE_INFORMATION Normal;
|
|
FILE_FS_FULL_SIZE_INFORMATION Full;
|
|
} SizeInfo;
|
|
|
|
WCHAR DefaultPath[2];
|
|
ULARGE_INTEGER BytesPerAllocationUnit;
|
|
ULARGE_INTEGER FreeBytesAvailableToCaller;
|
|
ULARGE_INTEGER TotalNumberOfBytes;
|
|
|
|
DefaultPath[0] = (WCHAR)'\\';
|
|
DefaultPath[1] = UNICODE_NULL;
|
|
|
|
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
|
ARGUMENT_PRESENT(lpDirectoryName) ? lpDirectoryName : DefaultPath,
|
|
&FileName,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if ( !TranslationStatus ) {
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
return FALSE;
|
|
}
|
|
|
|
FreeBuffer = FileName.Buffer;
|
|
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&FileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
|
|
Status = NtOpenFile(
|
|
&Handle,
|
|
(ACCESS_MASK)FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE | FILE_OPEN_FOR_FREE_SPACE_QUERY
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
if ( GetLastError() == ERROR_FILE_NOT_FOUND ) {
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
}
|
|
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
|
|
return FALSE;
|
|
}
|
|
|
|
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
|
|
|
|
//
|
|
// If the caller wants the volume total then try to get a full
|
|
// file size.
|
|
//
|
|
|
|
if ( ARGUMENT_PRESENT(lpTotalNumberOfFreeBytes) ) {
|
|
|
|
Status = NtQueryVolumeInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
&SizeInfo,
|
|
sizeof(SizeInfo.Full),
|
|
FileFsFullSizeInformation
|
|
);
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
|
|
NtClose(Handle);
|
|
|
|
BytesPerAllocationUnit.QuadPart =
|
|
SizeInfo.Full.BytesPerSector * SizeInfo.Full.SectorsPerAllocationUnit;
|
|
|
|
if ( ARGUMENT_PRESENT(lpFreeBytesAvailableToCaller) ) {
|
|
lpFreeBytesAvailableToCaller->QuadPart =
|
|
BytesPerAllocationUnit.QuadPart *
|
|
SizeInfo.Full.CallerAvailableAllocationUnits.QuadPart;
|
|
}
|
|
if ( ARGUMENT_PRESENT(lpTotalNumberOfBytes) ) {
|
|
lpTotalNumberOfBytes->QuadPart =
|
|
BytesPerAllocationUnit.QuadPart * SizeInfo.Full.TotalAllocationUnits.QuadPart;
|
|
}
|
|
lpTotalNumberOfFreeBytes->QuadPart =
|
|
BytesPerAllocationUnit.QuadPart *
|
|
SizeInfo.Full.ActualAvailableAllocationUnits.QuadPart;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Determine the size parameters of the volume.
|
|
//
|
|
|
|
Status = NtQueryVolumeInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
&SizeInfo,
|
|
sizeof(SizeInfo.Normal),
|
|
FileFsSizeInformation
|
|
);
|
|
NtClose(Handle);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
|
|
BytesPerAllocationUnit.QuadPart =
|
|
SizeInfo.Normal.BytesPerSector * SizeInfo.Normal.SectorsPerAllocationUnit;
|
|
|
|
FreeBytesAvailableToCaller.QuadPart =
|
|
BytesPerAllocationUnit.QuadPart * SizeInfo.Normal.AvailableAllocationUnits.QuadPart;
|
|
|
|
TotalNumberOfBytes.QuadPart =
|
|
BytesPerAllocationUnit.QuadPart * SizeInfo.Normal.TotalAllocationUnits.QuadPart;
|
|
|
|
if ( ARGUMENT_PRESENT(lpFreeBytesAvailableToCaller) ) {
|
|
lpFreeBytesAvailableToCaller->QuadPart = FreeBytesAvailableToCaller.QuadPart;
|
|
}
|
|
if ( ARGUMENT_PRESENT(lpTotalNumberOfBytes) ) {
|
|
lpTotalNumberOfBytes->QuadPart = TotalNumberOfBytes.QuadPart;
|
|
}
|
|
if ( ARGUMENT_PRESENT(lpTotalNumberOfFreeBytes) ) {
|
|
lpTotalNumberOfFreeBytes->QuadPart = FreeBytesAvailableToCaller.QuadPart;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
GetVolumeInformationA(
|
|
LPCSTR lpRootPathName,
|
|
LPSTR lpVolumeNameBuffer,
|
|
DWORD nVolumeNameSize,
|
|
LPDWORD lpVolumeSerialNumber,
|
|
LPDWORD lpMaximumComponentLength,
|
|
LPDWORD lpFileSystemFlags,
|
|
LPSTR lpFileSystemNameBuffer,
|
|
DWORD nFileSystemNameSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ANSI thunk to GetVolumeInformationW
|
|
|
|
--*/
|
|
|
|
{
|
|
PUNICODE_STRING Unicode;
|
|
NTSTATUS Status;
|
|
UNICODE_STRING UnicodeVolumeName;
|
|
UNICODE_STRING UnicodeFileSystemName;
|
|
ANSI_STRING AnsiVolumeName;
|
|
ANSI_STRING AnsiFileSystemName;
|
|
BOOL ReturnValue;
|
|
|
|
if (!ARGUMENT_PRESENT( lpRootPathName )) {
|
|
lpRootPathName = "\\";
|
|
}
|
|
|
|
Unicode = Basep8BitStringToStaticUnicodeString( lpRootPathName );
|
|
if (Unicode == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
UnicodeVolumeName.Buffer = NULL;
|
|
UnicodeFileSystemName.Buffer = NULL;
|
|
UnicodeVolumeName.MaximumLength = 0;
|
|
UnicodeFileSystemName.MaximumLength = 0;
|
|
AnsiVolumeName.Buffer = lpVolumeNameBuffer;
|
|
AnsiVolumeName.MaximumLength = (USHORT)(nVolumeNameSize+1);
|
|
AnsiFileSystemName.Buffer = lpFileSystemNameBuffer;
|
|
AnsiFileSystemName.MaximumLength = (USHORT)(nFileSystemNameSize+1);
|
|
|
|
try {
|
|
if ( ARGUMENT_PRESENT(lpVolumeNameBuffer) ) {
|
|
UnicodeVolumeName.MaximumLength = AnsiVolumeName.MaximumLength << 1;
|
|
UnicodeVolumeName.Buffer = RtlAllocateHeap(
|
|
RtlProcessHeap(), MAKE_TAG( TMP_TAG ),
|
|
UnicodeVolumeName.MaximumLength
|
|
);
|
|
|
|
if ( !UnicodeVolumeName.Buffer ) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(lpFileSystemNameBuffer) ) {
|
|
UnicodeFileSystemName.MaximumLength = AnsiFileSystemName.MaximumLength << 1;
|
|
UnicodeFileSystemName.Buffer = RtlAllocateHeap(
|
|
RtlProcessHeap(), MAKE_TAG( TMP_TAG ),
|
|
UnicodeFileSystemName.MaximumLength
|
|
);
|
|
|
|
if ( !UnicodeFileSystemName.Buffer ) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
ReturnValue = GetVolumeInformationW(
|
|
(LPCWSTR)Unicode->Buffer,
|
|
UnicodeVolumeName.Buffer,
|
|
nVolumeNameSize,
|
|
lpVolumeSerialNumber,
|
|
lpMaximumComponentLength,
|
|
lpFileSystemFlags,
|
|
UnicodeFileSystemName.Buffer,
|
|
nFileSystemNameSize
|
|
);
|
|
|
|
if ( ReturnValue ) {
|
|
|
|
if ( ARGUMENT_PRESENT(lpVolumeNameBuffer) ) {
|
|
RtlInitUnicodeString(
|
|
&UnicodeVolumeName,
|
|
UnicodeVolumeName.Buffer
|
|
);
|
|
|
|
Status = BasepUnicodeStringTo8BitString(
|
|
&AnsiVolumeName,
|
|
&UnicodeVolumeName,
|
|
FALSE
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(lpFileSystemNameBuffer) ) {
|
|
RtlInitUnicodeString(
|
|
&UnicodeFileSystemName,
|
|
UnicodeFileSystemName.Buffer
|
|
);
|
|
|
|
Status = BasepUnicodeStringTo8BitString(
|
|
&AnsiFileSystemName,
|
|
&UnicodeFileSystemName,
|
|
FALSE
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally {
|
|
if ( UnicodeVolumeName.Buffer ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0,UnicodeVolumeName.Buffer);
|
|
}
|
|
if ( UnicodeFileSystemName.Buffer ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0,UnicodeFileSystemName.Buffer);
|
|
}
|
|
}
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
GetVolumeInformationW(
|
|
LPCWSTR lpRootPathName,
|
|
LPWSTR lpVolumeNameBuffer,
|
|
DWORD nVolumeNameSize,
|
|
LPDWORD lpVolumeSerialNumber,
|
|
LPDWORD lpMaximumComponentLength,
|
|
LPDWORD lpFileSystemFlags,
|
|
LPWSTR lpFileSystemNameBuffer,
|
|
DWORD nFileSystemNameSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns information about the file system whose root
|
|
directory is specified.
|
|
|
|
Arguments:
|
|
|
|
lpRootPathName - An optional parameter, that if specified, supplies
|
|
the root directory of the file system that information is to be
|
|
returned about. If this parameter is not specified, then the
|
|
root of the current directory is used.
|
|
|
|
lpVolumeNameBuffer - An optional parameter that if specified returns
|
|
the name of the specified volume.
|
|
|
|
nVolumeNameSize - Supplies the length of the volume name buffer.
|
|
This parameter is ignored if the volume name buffer is not
|
|
supplied.
|
|
|
|
lpVolumeSerialNumber - An optional parameter that if specified
|
|
points to a DWORD. The DWORD contains the 32-bit of the volume
|
|
serial number.
|
|
|
|
lpMaximumComponentLength - An optional parameter that if specified
|
|
returns the maximum length of a filename component supported by
|
|
the specified file system. A filename component is that portion
|
|
of a filename between pathname seperators.
|
|
|
|
lpFileSystemFlags - An optional parameter that if specified returns
|
|
flags associated with the specified file system.
|
|
|
|
lpFileSystemFlags Flags:
|
|
|
|
FS_CASE_IS_PRESERVED - Indicates that the case of file names
|
|
is preserved when the name is placed on disk.
|
|
|
|
FS_CASE_SENSITIVE - Indicates that the file system supports
|
|
case sensitive file name lookup.
|
|
|
|
FS_UNICODE_STORED_ON_DISK - Indicates that the file system
|
|
supports unicode in file names as they appear on disk.
|
|
|
|
lpFileSystemNameBuffer - An optional parameter that if specified returns
|
|
the name for the specified file system (e.g. FAT, HPFS...).
|
|
|
|
nFileSystemNameSize - Supplies the length of the file system name
|
|
buffer. This parameter is ignored if the file system name
|
|
buffer is not supplied.
|
|
|
|
Return Value:
|
|
|
|
TRUE - The operation was successful.
|
|
|
|
FALSE/NULL - The operation failed. Extended error status is available
|
|
using GetLastError.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
HANDLE Handle;
|
|
UNICODE_STRING FileName;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
BOOLEAN TranslationStatus;
|
|
PVOID FreeBuffer;
|
|
PFILE_FS_ATTRIBUTE_INFORMATION AttributeInfo;
|
|
PFILE_FS_VOLUME_INFORMATION VolumeInfo;
|
|
ULONG AttributeInfoLength;
|
|
ULONG VolumeInfoLength;
|
|
WCHAR DefaultPath[2];
|
|
BOOL rv;
|
|
ULONG HardErrorValue;
|
|
#if defined(BUILD_WOW6432)
|
|
ULONG HardErrorValue64;
|
|
#endif
|
|
PTEB Teb;
|
|
|
|
rv = FALSE;
|
|
DefaultPath[0] = (WCHAR)'\\';
|
|
DefaultPath[1] = UNICODE_NULL;
|
|
|
|
nVolumeNameSize *= 2;
|
|
nFileSystemNameSize *= 2;
|
|
|
|
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
|
ARGUMENT_PRESENT(lpRootPathName) ? lpRootPathName : DefaultPath,
|
|
&FileName,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if ( !TranslationStatus ) {
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
return FALSE;
|
|
}
|
|
|
|
FreeBuffer = FileName.Buffer;
|
|
|
|
//
|
|
// Check to make sure a root was specified
|
|
//
|
|
|
|
if ( FileName.Buffer[(FileName.Length >> 1)-1] != '\\' ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
|
|
BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
|
|
return FALSE;
|
|
}
|
|
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&FileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
AttributeInfo = NULL;
|
|
VolumeInfo = NULL;
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
Teb = NtCurrentTeb();
|
|
HardErrorValue = Teb->HardErrorsAreDisabled;
|
|
Teb->HardErrorsAreDisabled = 1;
|
|
#if defined(BUILD_WOW6432)
|
|
HardErrorValue64 = NtCurrentTeb64()->HardErrorsAreDisabled;
|
|
NtCurrentTeb64()->HardErrorsAreDisabled = 1;
|
|
#endif
|
|
Status = NtOpenFile(
|
|
&Handle,
|
|
(ACCESS_MASK)FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT
|
|
);
|
|
Teb->HardErrorsAreDisabled = HardErrorValue;
|
|
#if defined(BUILD_WOW6432)
|
|
NtCurrentTeb64()->HardErrorsAreDisabled = HardErrorValue64;
|
|
#endif
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !IsThisARootDirectory(Handle,&FileName) ) {
|
|
NtClose(Handle);
|
|
SetLastError(ERROR_DIR_NOT_ROOT);
|
|
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
|
|
return FALSE;
|
|
}
|
|
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
|
|
|
|
if ( ARGUMENT_PRESENT(lpVolumeNameBuffer) ||
|
|
ARGUMENT_PRESENT(lpVolumeSerialNumber) ) {
|
|
if ( ARGUMENT_PRESENT(lpVolumeNameBuffer) ) {
|
|
VolumeInfoLength = sizeof(*VolumeInfo)+nVolumeNameSize;
|
|
}
|
|
else {
|
|
VolumeInfoLength = sizeof(*VolumeInfo)+MAX_PATH;
|
|
}
|
|
VolumeInfo = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), VolumeInfoLength);
|
|
|
|
if ( !VolumeInfo ) {
|
|
NtClose(Handle);
|
|
BaseSetLastNTError(STATUS_NO_MEMORY);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(lpFileSystemNameBuffer) ||
|
|
ARGUMENT_PRESENT(lpMaximumComponentLength) ||
|
|
ARGUMENT_PRESENT(lpFileSystemFlags) ) {
|
|
if ( ARGUMENT_PRESENT(lpFileSystemNameBuffer) ) {
|
|
AttributeInfoLength = sizeof(*AttributeInfo) + nFileSystemNameSize;
|
|
}
|
|
else {
|
|
AttributeInfoLength = sizeof(*AttributeInfo) + MAX_PATH;
|
|
}
|
|
AttributeInfo = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), AttributeInfoLength);
|
|
if ( !AttributeInfo ) {
|
|
NtClose(Handle);
|
|
if ( VolumeInfo ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0,VolumeInfo);
|
|
}
|
|
BaseSetLastNTError(STATUS_NO_MEMORY);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
try {
|
|
if ( VolumeInfo ) {
|
|
Status = NtQueryVolumeInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
VolumeInfo,
|
|
VolumeInfoLength,
|
|
FileFsVolumeInformation
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
rv = FALSE;
|
|
goto finally_exit;
|
|
}
|
|
}
|
|
|
|
if ( AttributeInfo ) {
|
|
Status = NtQueryVolumeInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
AttributeInfo,
|
|
AttributeInfoLength,
|
|
FileFsAttributeInformation
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
rv = FALSE;
|
|
goto finally_exit;
|
|
}
|
|
}
|
|
try {
|
|
|
|
if ( ARGUMENT_PRESENT(lpVolumeNameBuffer) ) {
|
|
if ( VolumeInfo->VolumeLabelLength >= nVolumeNameSize ) {
|
|
SetLastError(ERROR_BAD_LENGTH);
|
|
rv = FALSE;
|
|
goto finally_exit;
|
|
}
|
|
else {
|
|
RtlMoveMemory( lpVolumeNameBuffer,
|
|
VolumeInfo->VolumeLabel,
|
|
VolumeInfo->VolumeLabelLength );
|
|
|
|
*(lpVolumeNameBuffer + (VolumeInfo->VolumeLabelLength >> 1)) = UNICODE_NULL;
|
|
}
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(lpVolumeSerialNumber) ) {
|
|
*lpVolumeSerialNumber = VolumeInfo->VolumeSerialNumber;
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(lpFileSystemNameBuffer) ) {
|
|
|
|
if ( AttributeInfo->FileSystemNameLength >= nFileSystemNameSize ) {
|
|
SetLastError(ERROR_BAD_LENGTH);
|
|
rv = FALSE;
|
|
goto finally_exit;
|
|
}
|
|
else {
|
|
RtlMoveMemory( lpFileSystemNameBuffer,
|
|
AttributeInfo->FileSystemName,
|
|
AttributeInfo->FileSystemNameLength );
|
|
|
|
*(lpFileSystemNameBuffer + (AttributeInfo->FileSystemNameLength >> 1)) = UNICODE_NULL;
|
|
}
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(lpMaximumComponentLength) ) {
|
|
*lpMaximumComponentLength = AttributeInfo->MaximumComponentNameLength;
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(lpFileSystemFlags) ) {
|
|
*lpFileSystemFlags = AttributeInfo->FileSystemAttributes;
|
|
}
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
BaseSetLastNTError(STATUS_ACCESS_VIOLATION);
|
|
return FALSE;
|
|
}
|
|
rv = TRUE;
|
|
finally_exit:;
|
|
}
|
|
finally {
|
|
NtClose(Handle);
|
|
if ( VolumeInfo ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0,VolumeInfo);
|
|
}
|
|
if ( AttributeInfo ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0,AttributeInfo);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
DWORD
|
|
APIENTRY
|
|
GetLogicalDriveStringsA(
|
|
DWORD nBufferLength,
|
|
LPSTR lpBuffer
|
|
)
|
|
{
|
|
ULONG DriveMap;
|
|
ANSI_STRING RootName;
|
|
int i;
|
|
PUCHAR Dst;
|
|
DWORD BytesLeft;
|
|
DWORD BytesNeeded;
|
|
BOOL WeFailed;
|
|
CHAR szDrive[] = "A:\\";
|
|
|
|
BytesNeeded = 0;
|
|
BytesLeft = nBufferLength;
|
|
Dst = (PUCHAR)lpBuffer;
|
|
WeFailed = FALSE;
|
|
|
|
RtlInitAnsiString(&RootName, szDrive);
|
|
DriveMap = GetLogicalDrives();
|
|
for ( i=0; i<26; i++ ) {
|
|
RootName.Buffer[0] = (CHAR)((CHAR)i+'A');
|
|
if (DriveMap & (1 << i) ) {
|
|
|
|
BytesNeeded += RootName.MaximumLength;
|
|
if ( BytesNeeded < (USHORT)BytesLeft ) {
|
|
RtlMoveMemory(Dst,RootName.Buffer,RootName.MaximumLength);
|
|
Dst += RootName.MaximumLength;
|
|
*Dst = '\0';
|
|
}
|
|
else {
|
|
WeFailed = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( WeFailed ) {
|
|
BytesNeeded++;
|
|
}
|
|
//
|
|
// Need to handle network uses;
|
|
//
|
|
|
|
return( BytesNeeded );
|
|
}
|
|
|
|
DWORD
|
|
APIENTRY
|
|
GetLogicalDriveStringsW(
|
|
DWORD nBufferLength,
|
|
LPWSTR lpBuffer
|
|
)
|
|
{
|
|
ULONG DriveMap;
|
|
UNICODE_STRING RootName;
|
|
int i;
|
|
PUCHAR Dst;
|
|
DWORD BytesLeft;
|
|
DWORD BytesNeeded;
|
|
BOOL WeFailed;
|
|
WCHAR wszDrive[] = L"A:\\";
|
|
|
|
nBufferLength = nBufferLength*2;
|
|
BytesNeeded = 0;
|
|
BytesLeft = nBufferLength;
|
|
Dst = (PUCHAR)lpBuffer;
|
|
WeFailed = FALSE;
|
|
|
|
RtlInitUnicodeString(&RootName, wszDrive);
|
|
|
|
DriveMap = GetLogicalDrives();
|
|
for ( i=0; i<26; i++ ) {
|
|
RootName.Buffer[0] = (WCHAR)((CHAR)i+'A');
|
|
if (DriveMap & (1 << i) ) {
|
|
|
|
BytesNeeded += RootName.MaximumLength;
|
|
if ( BytesNeeded < (USHORT)BytesLeft ) {
|
|
RtlMoveMemory(Dst,RootName.Buffer,RootName.MaximumLength);
|
|
Dst += RootName.MaximumLength;
|
|
*(PWSTR)Dst = UNICODE_NULL;
|
|
}
|
|
else {
|
|
WeFailed = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( WeFailed ) {
|
|
BytesNeeded += 2;
|
|
}
|
|
|
|
//
|
|
// Need to handle network uses;
|
|
//
|
|
|
|
return( BytesNeeded/2 );
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
SetVolumeLabelA(
|
|
LPCSTR lpRootPathName,
|
|
LPCSTR lpVolumeName
|
|
)
|
|
{
|
|
PUNICODE_STRING Unicode;
|
|
UNICODE_STRING UnicodeVolumeName;
|
|
BOOL ReturnValue;
|
|
|
|
if (!ARGUMENT_PRESENT( lpRootPathName )) {
|
|
lpRootPathName = "\\";
|
|
}
|
|
|
|
Unicode = Basep8BitStringToStaticUnicodeString( lpRootPathName );
|
|
|
|
if (Unicode == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT(lpVolumeName) ) {
|
|
if (!Basep8BitStringToDynamicUnicodeString( &UnicodeVolumeName, lpVolumeName )) {
|
|
return FALSE;
|
|
}
|
|
|
|
} else {
|
|
UnicodeVolumeName.Buffer = NULL;
|
|
}
|
|
|
|
ReturnValue = SetVolumeLabelW((LPCWSTR)Unicode->Buffer,(LPCWSTR)UnicodeVolumeName.Buffer);
|
|
|
|
RtlFreeUnicodeString(&UnicodeVolumeName);
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
SetVolumeLabelW(
|
|
LPCWSTR lpRootPathName,
|
|
LPCWSTR lpVolumeName
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
HANDLE Handle;
|
|
UNICODE_STRING FileName;
|
|
UNICODE_STRING LabelName;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
BOOLEAN TranslationStatus;
|
|
PVOID FreeBuffer;
|
|
PFILE_FS_LABEL_INFORMATION LabelInformation;
|
|
ULONG LabelInfoLength;
|
|
WCHAR DefaultPath[2];
|
|
BOOL rv;
|
|
WCHAR volumeName[MAX_PATH];
|
|
BOOL usingVolumeName;
|
|
|
|
rv = FALSE;
|
|
DefaultPath[0] = (WCHAR)'\\';
|
|
DefaultPath[1] = UNICODE_NULL;
|
|
|
|
if ( ARGUMENT_PRESENT(lpVolumeName) ) {
|
|
RtlInitUnicodeString(&LabelName,lpVolumeName);
|
|
}
|
|
else {
|
|
LabelName.Length = 0;
|
|
LabelName.MaximumLength = 0;
|
|
LabelName.Buffer = NULL;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(lpRootPathName)) {
|
|
if (GetVolumeNameForVolumeMountPointW(lpRootPathName, volumeName,
|
|
MAX_PATH)) {
|
|
|
|
usingVolumeName = TRUE;
|
|
} else {
|
|
usingVolumeName = FALSE;
|
|
}
|
|
} else {
|
|
usingVolumeName = FALSE;
|
|
}
|
|
|
|
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
|
usingVolumeName ? volumeName : (ARGUMENT_PRESENT(lpRootPathName) ? lpRootPathName : DefaultPath),
|
|
&FileName,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if ( !TranslationStatus ) {
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
return FALSE;
|
|
}
|
|
|
|
FreeBuffer = FileName.Buffer;
|
|
|
|
//
|
|
// Check to make sure a root was specified
|
|
//
|
|
|
|
if ( FileName.Buffer[(FileName.Length >> 1)-1] != '\\' ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
|
|
BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
|
|
return FALSE;
|
|
}
|
|
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&FileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
|
|
Status = NtOpenFile(
|
|
&Handle,
|
|
(ACCESS_MASK)FILE_WRITE_DATA | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !IsThisARootDirectory(Handle,NULL) ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
|
|
NtClose(Handle);
|
|
SetLastError(ERROR_DIR_NOT_ROOT);
|
|
return FALSE;
|
|
}
|
|
|
|
NtClose(Handle);
|
|
|
|
//
|
|
// Now open the volume DASD by ignoring the ending backslash
|
|
//
|
|
|
|
FileName.Length -= 2;
|
|
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&FileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Open the volume
|
|
//
|
|
|
|
Status = NtOpenFile(
|
|
&Handle,
|
|
(ACCESS_MASK)FILE_WRITE_DATA | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Set the volume label
|
|
//
|
|
|
|
LabelInformation = NULL;
|
|
|
|
try {
|
|
|
|
rv = TRUE;
|
|
|
|
//
|
|
// the label info buffer contains a single wchar that is the basis of
|
|
// the label name. Subtract this out so the info length is the length
|
|
// of the label and the structure (not including the extra wchar)
|
|
//
|
|
|
|
if ( LabelName.Length ) {
|
|
LabelInfoLength = sizeof(*LabelInformation) + LabelName.Length - sizeof(WCHAR);
|
|
}
|
|
else {
|
|
LabelInfoLength = sizeof(*LabelInformation);
|
|
}
|
|
|
|
LabelInformation = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), LabelInfoLength);
|
|
if ( LabelInformation ) {
|
|
RtlCopyMemory(
|
|
LabelInformation->VolumeLabel,
|
|
LabelName.Buffer,
|
|
LabelName.Length
|
|
);
|
|
LabelInformation->VolumeLabelLength = LabelName.Length;
|
|
Status = NtSetVolumeInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
(PVOID) LabelInformation,
|
|
LabelInfoLength,
|
|
FileFsLabelInformation
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
rv = FALSE;
|
|
BaseSetLastNTError(Status);
|
|
}
|
|
}
|
|
else {
|
|
rv = FALSE;
|
|
BaseSetLastNTError(STATUS_NO_MEMORY);
|
|
}
|
|
}
|
|
finally {
|
|
NtClose(Handle);
|
|
if ( LabelInformation ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0,LabelInformation);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
CheckNameLegalDOS8Dot3A(
|
|
IN LPCSTR lpName,
|
|
OUT LPSTR lpOemName OPTIONAL,
|
|
IN DWORD OemNameSize OPTIONAL,
|
|
OUT PBOOL pbNameContainsSpaces OPTIONAL,
|
|
OUT PBOOL pbNameLegal
|
|
)
|
|
/*++
|
|
|
|
ANSI thunk to IsNameLegalDOS8Dot3W
|
|
|
|
--*/
|
|
|
|
{
|
|
ANSI_STRING AnsiStr;
|
|
PUNICODE_STRING pUnicodeStr;
|
|
NTSTATUS Status;
|
|
BOOL Result;
|
|
|
|
if( (lpName == NULL) || (pbNameLegal == NULL) ||
|
|
((lpOemName == NULL) && (OemNameSize != 0)) ||
|
|
(OemNameSize > MAXUSHORT)
|
|
) {
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
pUnicodeStr = Basep8BitStringToStaticUnicodeString( lpName );
|
|
|
|
if( pUnicodeStr == NULL ) {
|
|
//
|
|
// LastError already set by Basep8BitStringToStaticUnicodeString
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
Result = CheckNameLegalDOS8Dot3W(
|
|
(LPCWSTR)(pUnicodeStr->Buffer),
|
|
lpOemName,
|
|
OemNameSize,
|
|
pbNameContainsSpaces,
|
|
pbNameLegal
|
|
);
|
|
|
|
return Result;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
CheckNameLegalDOS8Dot3W(
|
|
IN LPCWSTR lpName,
|
|
OUT LPSTR lpOemName OPTIONAL,
|
|
IN DWORD OemNameSize OPTIONAL,
|
|
OUT PBOOL pbNameContainsSpaces OPTIONAL,
|
|
OUT PBOOL pbNameLegal
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function determines whether this name can successfully be used to
|
|
create a file on the FAT file system.
|
|
|
|
This routine can therefore also be used to determine if a name is
|
|
appropriate to be passed back to a Win31 or DOS app, i.e. whether
|
|
the downlevel APP will understand the name.
|
|
|
|
Arguments:
|
|
|
|
lpName - The UNICODE name to test for conformance to 8.3 symantics.
|
|
|
|
lpOemName - If specified, will receive the Oem name corresponding
|
|
to the passed in lpName. Storage must be provided by the caller.
|
|
The name is undefined if the routine returns FALSE or lpName
|
|
does not conform to 8.3 symantics.
|
|
|
|
OemNameSize - If lpOemName is specified, then OemNameSize must specify
|
|
the size of the lpOemName buffer in chars. If lpOemName is not
|
|
specified, then OemNameSize must be set to zero.
|
|
|
|
pbNameContainsSpaces - If the name is a valid 8.3 FAT name, then this
|
|
parameter will indicate if the names contains spaces. If
|
|
the name is not 8.3 compliant, this parameter is undefined. In
|
|
many instances, the alternate name is more appropriate to
|
|
use if spaces are present in the principle name, even if
|
|
it is 8.3 compliant.
|
|
|
|
pbNameLegal - If the function returns TRUE, then this
|
|
parameter will indicate if the passed in UNICODE name forms a valid
|
|
8.3 FAT name when upcased to the current Oem code page. If
|
|
the name is not 8.3 compliant, this parameter is undefined.
|
|
TRUE - passed in UNICODE name forms a valid 8.3 FAT name
|
|
FALSE - passed in UNICODE name does not forms a valid 8.3 FAT name
|
|
|
|
Return Value:
|
|
|
|
TRUE - function succeeds
|
|
FALSE - Function fails. Extended error status is available using
|
|
GetLastError.
|
|
|
|
--*/
|
|
{
|
|
|
|
#define BASEP_LOCAL_OEM_BUFFER_SIZE (12 * sizeof(ANSI_NULL))
|
|
|
|
UNICODE_STRING UnicodeStr;
|
|
OEM_STRING OemStr;
|
|
POEM_STRING pOemStr;
|
|
UCHAR OemBuffer[BASEP_LOCAL_OEM_BUFFER_SIZE];
|
|
BOOLEAN SpacesInName, Result;
|
|
|
|
if( (lpName == NULL) || (pbNameLegal == NULL) ||
|
|
((lpOemName == NULL) && (OemNameSize != 0)) ||
|
|
(OemNameSize > MAXUSHORT)
|
|
) {
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
if( lpOemName != NULL ) {
|
|
//
|
|
// Use a local buffer so that RtlIsNameLegalDOS8Dot3 will not fail
|
|
// due to insufficent OemName buffer size
|
|
//
|
|
OemStr.Length = 0;
|
|
OemStr.MaximumLength = BASEP_LOCAL_OEM_BUFFER_SIZE;
|
|
OemStr.Buffer = OemBuffer;
|
|
pOemStr = &OemStr;
|
|
}
|
|
else {
|
|
pOemStr = NULL;
|
|
}
|
|
|
|
RtlInitUnicodeString( &UnicodeStr, lpName );
|
|
|
|
Result = RtlIsNameLegalDOS8Dot3(
|
|
&UnicodeStr,
|
|
pOemStr,
|
|
&SpacesInName
|
|
);
|
|
|
|
if( Result != FALSE ) {
|
|
|
|
if( pOemStr != NULL ) {
|
|
|
|
if( OemNameSize < (OemStr.Length + sizeof(ANSI_NULL)) ) {
|
|
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
return FALSE;
|
|
}
|
|
RtlCopyMemory( lpOemName, OemStr.Buffer, OemStr.Length );
|
|
lpOemName[OemStr.Length/sizeof(ANSI_NULL)] = ANSI_NULL;
|
|
}
|
|
|
|
if( pbNameContainsSpaces != NULL ) {
|
|
*pbNameContainsSpaces = SpacesInName;
|
|
}
|
|
}
|
|
|
|
*pbNameLegal = Result;
|
|
|
|
return TRUE;
|
|
|
|
#undef BASEP_LOCAL_OEM_BUFFER_SIZE
|
|
}
|
|
|
|
|
|
#if 0
|
|
//
|
|
// frankar, let me know if this is needed...
|
|
//
|
|
UINT
|
|
WINAPI
|
|
GetZawSysDirectoryA(
|
|
LPSTR lpBuffer,
|
|
UINT uSize
|
|
)
|
|
{
|
|
ANSI_STRING AnsiString;
|
|
UNICODE_STRING UnicodeString;
|
|
NTSTATUS Status;
|
|
ULONG cbAnsiString;
|
|
|
|
UnicodeString.MaximumLength = (USHORT)((uSize<<1)+sizeof(UNICODE_NULL));
|
|
UnicodeString.Buffer = RtlAllocateHeap(
|
|
RtlProcessHeap(), MAKE_TAG( TMP_TAG ),
|
|
UnicodeString.MaximumLength
|
|
);
|
|
if ( !UnicodeString.Buffer ) {
|
|
BaseSetLastNTError(STATUS_NO_MEMORY);
|
|
return 0;
|
|
}
|
|
UnicodeString.Length = (USHORT)GetZawSysDirectoryW(
|
|
UnicodeString.Buffer,
|
|
(DWORD)(UnicodeString.MaximumLength-sizeof(UNICODE_NULL))/2
|
|
)*2;
|
|
if ( UnicodeString.Length > (USHORT)(UnicodeString.MaximumLength-sizeof(UNICODE_NULL)) ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0,UnicodeString.Buffer);
|
|
|
|
//
|
|
// given buffer size is too small.
|
|
// allocate enough size of buffer and try again
|
|
//
|
|
// we need to get entire unicode path
|
|
// otherwise we can't figure out the exact length
|
|
// of corresponding ansi string (cbAnsiString).
|
|
|
|
UnicodeString.Buffer = RtlAllocateHeap ( RtlProcessHeap(),
|
|
MAKE_TAG( TMP_TAG ),
|
|
UnicodeString.Length+ sizeof(UNICODE_NULL));
|
|
if ( !UnicodeString.Buffer ) {
|
|
BaseSetLastNTError(STATUS_NO_MEMORY);
|
|
return 0;
|
|
}
|
|
|
|
UnicodeString.Length = (USHORT)GetZawSysDirectoryW(
|
|
UnicodeString.Buffer,
|
|
(DWORD)(UnicodeString.MaximumLength-sizeof(UNICODE_NULL))/2,
|
|
) * 2;
|
|
Status = RtlUnicodeToMultiByteSize( &cbAnsiString,
|
|
UnicodeString.Buffer,
|
|
UnicodeString.Length );
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, UnicodeString.Buffer);
|
|
BaseSetLastNTError(Status);
|
|
return 0;
|
|
}
|
|
else if ( nBufferLength < cbAnsiString ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, UnicodeString.Buffer);
|
|
return cbAnsiString;
|
|
}
|
|
}
|
|
AnsiString.Buffer = lpBuffer;
|
|
AnsiString.MaximumLength = (USHORT)(uSize+1);
|
|
Status = BasepUnicodeStringTo8BitString(&AnsiString,&UnicodeString,FALSE);
|
|
RtlFreeHeap(RtlProcessHeap(), 0,UnicodeString.Buffer);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
return 0;
|
|
}
|
|
return AnsiString.Length;
|
|
}
|
|
|
|
UINT
|
|
WINAPI
|
|
GetZawWindDirectoryA(
|
|
LPSTR lpBuffer,
|
|
UINT uSize
|
|
)
|
|
{
|
|
ANSI_STRING AnsiString;
|
|
UNICODE_STRING UnicodeString;
|
|
NTSTATUS Status;
|
|
ULONG cbAnsiString;
|
|
|
|
UnicodeString.MaximumLength = (USHORT)((uSize<<1)+sizeof(UNICODE_NULL));
|
|
UnicodeString.Buffer = RtlAllocateHeap(
|
|
RtlProcessHeap(), MAKE_TAG( TMP_TAG ),
|
|
UnicodeString.MaximumLength
|
|
);
|
|
if ( !UnicodeString.Buffer ) {
|
|
BaseSetLastNTError(STATUS_NO_MEMORY);
|
|
return 0;
|
|
}
|
|
UnicodeString.Length = (USHORT)GetZawWindDirectoryW(
|
|
UnicodeString.Buffer,
|
|
(DWORD)(UnicodeString.MaximumLength-sizeof(UNICODE_NULL))/2
|
|
)*2;
|
|
if ( UnicodeString.Length > (USHORT)(UnicodeString.MaximumLength-sizeof(UNICODE_NULL)) ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0,UnicodeString.Buffer);
|
|
|
|
//
|
|
// given buffer size is too small.
|
|
// allocate enough size of buffer and try again
|
|
//
|
|
// we need to get entire unicode path
|
|
// otherwise we can't figure out the exact length
|
|
// of corresponding ansi string (cbAnsiString).
|
|
|
|
UnicodeString.Buffer = RtlAllocateHeap ( RtlProcessHeap(),
|
|
MAKE_TAG( TMP_TAG ),
|
|
UnicodeString.Length+ sizeof(UNICODE_NULL));
|
|
if ( !UnicodeString.Buffer ) {
|
|
BaseSetLastNTError(STATUS_NO_MEMORY);
|
|
return 0;
|
|
}
|
|
|
|
UnicodeString.Length = (USHORT)GetZawWindDirectoryW(
|
|
UnicodeString.Buffer,
|
|
(DWORD)(UnicodeString.MaximumLength-sizeof(UNICODE_NULL))/2
|
|
) * 2;
|
|
Status = RtlUnicodeToMultiByteSize( &cbAnsiString,
|
|
UnicodeString.Buffer,
|
|
UnicodeString.Length );
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, UnicodeString.Buffer);
|
|
BaseSetLastNTError(Status);
|
|
return 0;
|
|
}
|
|
else if ( nBufferLength < cbAnsiString ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, UnicodeString.Buffer);
|
|
return cbAnsiString;
|
|
}
|
|
}
|
|
AnsiString.Buffer = lpBuffer;
|
|
AnsiString.MaximumLength = (USHORT)(uSize+1);
|
|
Status = BasepUnicodeStringTo8BitString(&AnsiString,&UnicodeString,FALSE);
|
|
RtlFreeHeap(RtlProcessHeap(), 0,UnicodeString.Buffer);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
return 0;
|
|
}
|
|
return AnsiString.Length;
|
|
}
|
|
|
|
UINT
|
|
WINAPI
|
|
GetZawSysDirectoryW(
|
|
LPWSTR lpBuffer,
|
|
UINT uSize
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE CurrentUserKey;
|
|
HANDLE DirKey;
|
|
UNICODE_STRING KeyName;
|
|
UNICODE_STRING KeyValueName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
ULONG DataLength;
|
|
ULONG ValueInfoBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION)+MAX_PATH/2];
|
|
PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
|
|
|
|
Status = RtlOpenCurrentUser(GENERIC_READ,&CurrentUserKey);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
bail_gzsd:
|
|
return GetSystemDirectoryW(lpBuffer,uSize);
|
|
}
|
|
|
|
RtlInitUnicodeString(&KeyName,L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ZAW");
|
|
|
|
InitializeObjectAttributes( &ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
CurrentUserKey,
|
|
NULL
|
|
);
|
|
Status = NtOpenKey( &DirKey,
|
|
KEY_READ | KEY_NOTIFY | KEY_WRITE,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
NtClose(CurrentUserKey);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto bail_gzsd;
|
|
}
|
|
|
|
RtlInitUnicodeString(&KeyValueName,L"ZawSys");
|
|
ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)&ValueInfoBuffer;
|
|
Status = NtQueryValueKey( DirKey,
|
|
&KeyValueName,
|
|
KeyValuePartialInformation,
|
|
ValueInfo,
|
|
sizeof(ValueInfoBuffer),
|
|
&DataLength
|
|
);
|
|
NtClose(DirKey);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto bail_gzsd;
|
|
}
|
|
if ( ValueInfo->DataLength > (uSize<<1) ) {
|
|
goto bail_gzsd;
|
|
}
|
|
RtlCopyMemory(lpBuffer,ValueInfo->Data,ValueInfo->DataLength);
|
|
return (ValueInfo->DataLength >> 1)-1;
|
|
}
|
|
|
|
UINT
|
|
WINAPI
|
|
GetZawWindDirectoryW(
|
|
LPWSTR lpBuffer,
|
|
UINT uSize
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE CurrentUserKey;
|
|
HANDLE DirKey;
|
|
UNICODE_STRING KeyName;
|
|
UNICODE_STRING KeyValueName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
ULONG DataLength;
|
|
ULONG ValueInfoBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION)+MAX_PATH/2];
|
|
PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
|
|
|
|
Status = RtlOpenCurrentUser(GENERIC_READ,&CurrentUserKey);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
bail_gzwd:
|
|
return GetWindowsDirectoryW(lpBuffer,uSize);
|
|
}
|
|
|
|
RtlInitUnicodeString(&KeyName,L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ZAW");
|
|
|
|
InitializeObjectAttributes( &ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
CurrentUserKey,
|
|
NULL
|
|
);
|
|
Status = NtOpenKey( &DirKey,
|
|
KEY_READ | KEY_NOTIFY | KEY_WRITE,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
NtClose(CurrentUserKey);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto bail_gzwd;
|
|
}
|
|
|
|
RtlInitUnicodeString(&KeyValueName,L"ZawWind");
|
|
ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)&ValueInfoBuffer;
|
|
Status = NtQueryValueKey( DirKey,
|
|
&KeyValueName,
|
|
KeyValuePartialInformation,
|
|
ValueInfo,
|
|
sizeof(ValueInfoBuffer),
|
|
&DataLength
|
|
);
|
|
NtClose(DirKey);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto bail_gzwd;
|
|
}
|
|
if ( ValueInfo->DataLength > (uSize<<1) ) {
|
|
goto bail_gzwd;
|
|
}
|
|
RtlCopyMemory(lpBuffer,ValueInfo->Data,ValueInfo->DataLength);
|
|
return (ValueInfo->DataLength >> 1)-1;
|
|
}
|
|
#endif
|