windows-nt/Source/XPSP1/NT/base/win32/client/curdir.c
2020-09-26 16:20:57 +08:00

521 lines
13 KiB
C

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
curdir.c
Abstract:
Current directory support
Author:
Mark Lucovsky (markl) 10-Oct-1990
Revision History:
--*/
#include "basedll.h"
BOOL
CheckForSameCurdir(
PUNICODE_STRING PathName
)
{
PCURDIR CurDir;
UNICODE_STRING CurrentDir;
BOOL rv;
CurDir = &(NtCurrentPeb()->ProcessParameters->CurrentDirectory);
if (CurDir->DosPath.Length > 6 ) {
if ( (CurDir->DosPath.Length-2) != PathName->Length ) {
return FALSE;
}
}
else {
if ( CurDir->DosPath.Length != PathName->Length ) {
return FALSE;
}
}
RtlAcquirePebLock();
CurrentDir = CurDir->DosPath;
if ( CurrentDir.Length > 6 ) {
CurrentDir.Length -= 2;
}
rv = FALSE;
if ( RtlEqualUnicodeString(&CurrentDir,PathName,TRUE) ) {
rv = TRUE;
}
RtlReleasePebLock();
return rv;
}
DWORD
APIENTRY
GetFullPathNameA(
LPCSTR lpFileName,
DWORD nBufferLength,
LPSTR lpBuffer,
LPSTR *lpFilePart
)
/*++
Routine Description:
ANSI thunk to GetFullPathNameW
--*/
{
NTSTATUS Status;
ULONG UnicodeLength;
UNICODE_STRING UnicodeString;
UNICODE_STRING UnicodeResult;
ANSI_STRING AnsiResult;
PWSTR Ubuff;
PWSTR FilePart;
PWSTR *FilePartPtr;
INT PrefixLength = 0;
if ( ARGUMENT_PRESENT(lpFilePart) ) {
FilePartPtr = &FilePart;
} else {
FilePartPtr = NULL;
}
if (!Basep8BitStringToDynamicUnicodeString( &UnicodeString, lpFileName )) {
return 0;
}
Ubuff = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), (MAX_PATH<<1) + sizeof(UNICODE_NULL));
if ( !Ubuff ) {
RtlFreeUnicodeString(&UnicodeString);
BaseSetLastNTError(STATUS_NO_MEMORY);
return 0;
}
UnicodeLength = RtlGetFullPathName_U(
UnicodeString.Buffer,
(MAX_PATH<<1),
Ubuff,
FilePartPtr
);
//
// UnicodeLength 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.
//
if ( UnicodeLength <= MAX_PATH * sizeof (WCHAR) ) {
Status = RtlUnicodeToMultiByteSize(&UnicodeLength, Ubuff, UnicodeLength);
//
// At this point, UnicodeLength variable contains
// Ansi based byte length.
//
if ( NT_SUCCESS(Status) ) {
if ( UnicodeLength && ARGUMENT_PRESENT(lpFilePart) && FilePart != NULL ) {
INT UnicodePrefixLength;
UnicodePrefixLength = (INT)(FilePart - Ubuff) * sizeof(WCHAR);
Status = RtlUnicodeToMultiByteSize( &PrefixLength,
Ubuff,
UnicodePrefixLength );
//
// At this point, PrefixLength variable contains
// Ansi based byte length.
//
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
UnicodeLength = 0;
}
}
} else {
BaseSetLastNTError(Status);
UnicodeLength = 0;
}
} else {
//
// we exceed the MAX_PATH limit. we should log the error and
// return zero. however US code returns the byte count of
// buffer required and doesn't log any error.
//
UnicodeLength = 0;
}
if ( UnicodeLength && UnicodeLength < nBufferLength ) {
RtlInitUnicodeString(&UnicodeResult,Ubuff);
Status = BasepUnicodeStringTo8BitString(&AnsiResult,&UnicodeResult,TRUE);
if ( NT_SUCCESS(Status) ) {
RtlMoveMemory(lpBuffer,AnsiResult.Buffer,UnicodeLength+1);
RtlFreeAnsiString(&AnsiResult);
if ( ARGUMENT_PRESENT(lpFilePart) ) {
if ( FilePart == NULL ) {
*lpFilePart = NULL;
} else {
*lpFilePart = lpBuffer + PrefixLength;
}
}
} else {
BaseSetLastNTError(Status);
UnicodeLength = 0;
}
} else {
if ( UnicodeLength ) {
UnicodeLength++;
}
}
RtlFreeUnicodeString(&UnicodeString);
RtlFreeHeap(RtlProcessHeap(), 0,Ubuff);
return (DWORD)UnicodeLength;
}
DWORD
APIENTRY
GetFullPathNameW(
LPCWSTR lpFileName,
DWORD nBufferLength,
LPWSTR lpBuffer,
LPWSTR *lpFilePart
)
/*++
Routine Description:
This function is used to return the fully qualified path name
corresponding to the specified file name.
This function is used to return a fully qualified pathname
corresponding to the specified filename. It does this by merging
the current drive and directory together with the specified file
name. In addition to this, it calculates the address of the file
name portion of the fully qualified pathname.
Arguments:
lpFileName - Supplies the file name of the file whose fully
qualified pathname is to be returned.
nBufferLength - Supplies the length in bytes of the buffer that is
to receive the fully qualified path.
lpBuffer - Returns the fully qualified pathname corresponding to the
specified file.
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.
--*/
{
return (DWORD) RtlGetFullPathName_U(
lpFileName,
nBufferLength*2,
lpBuffer,
lpFilePart
)/2;
}
DWORD
APIENTRY
GetCurrentDirectoryA(
DWORD nBufferLength,
LPSTR lpBuffer
)
/*++
Routine Description:
ANSI thunk to GetCurrentDirectoryW
--*/
{
PUNICODE_STRING Unicode;
ANSI_STRING AnsiString;
NTSTATUS Status;
DWORD ReturnValue;
ULONG cbAnsiString;
if ( nBufferLength > MAXUSHORT ) {
nBufferLength = MAXUSHORT-2;
}
Unicode = &NtCurrentTeb()->StaticUnicodeString;
Unicode->Length = (USHORT)RtlGetCurrentDirectory_U(
Unicode->MaximumLength,
Unicode->Buffer
);
//
// Unicode->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, Unicode->Buffer, Unicode->Length );
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
ReturnValue = 0;
}
else {
if ( nBufferLength > (DWORD)(cbAnsiString ) ) {
AnsiString.Buffer = lpBuffer;
AnsiString.MaximumLength = (USHORT)(nBufferLength+1);
Status = BasepUnicodeStringTo8BitString(&AnsiString,Unicode,FALSE);
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
ReturnValue = 0;
}
else {
ReturnValue = AnsiString.Length;
}
}
else {
// The return value is the size of the buffer required to hold the
// pathname (including the terminating null character).
ReturnValue = cbAnsiString + 1;
}
}
return ReturnValue;
}
DWORD
APIENTRY
GetCurrentDirectoryW(
DWORD nBufferLength,
LPWSTR lpBuffer
)
/*++
Routine Description:
The current directory for a process can be retreived using
GetCurrentDirectory.
Arguments:
nBufferLength - Supplies the length in bytes of the buffer that is to
receive the current directory string.
lpBuffer - Returns the current directory string for the current
process. The string is a null terminated string and specifies
the absolute path to the current directory.
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.
--*/
{
return (DWORD)RtlGetCurrentDirectory_U(nBufferLength*2,lpBuffer)/2;
}
BOOL
APIENTRY
SetCurrentDirectoryA(
LPCSTR lpPathName
)
/*++
Routine Description:
ANSI thunk to SetCurrentDirectoryW
--*/
{
NTSTATUS Status;
PUNICODE_STRING Unicode;
BOOL rv;
Unicode = Basep8BitStringToStaticUnicodeString( lpPathName );
if (Unicode == NULL) {
return FALSE;
}
if ( !CheckForSameCurdir(Unicode) ) {
Status = RtlSetCurrentDirectory_U(Unicode);
if ( !NT_SUCCESS(Status) ) {
//
// claris works 5.0 has a bug where it doesn't strip leading/trailing
// quotes properly. It ends up calling SetCurrentDirectoryA with a
// leading quote, and WinExec with a trailing quote. This error path
// logic will compensate for the leading quote problem
//
if ( Unicode->Buffer[0] == L'"' && Unicode->Length > 2 ) {
Unicode = Basep8BitStringToStaticUnicodeString( lpPathName+1 );
if (Unicode == NULL) {
return FALSE;
}
Status = RtlSetCurrentDirectory_U(Unicode);
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
rv = FALSE;
}
else {
rv = TRUE;
}
}
else {
BaseSetLastNTError(Status);
rv = FALSE;
}
} else {
rv = TRUE;
}
} else {
rv = TRUE;
}
return rv;
}
BOOL
APIENTRY
SetCurrentDirectoryW(
LPCWSTR lpPathName
)
/*++
Routine Description:
The current directory for a process is changed using
SetCurrentDirectory.
Each process has a single current directory. A current directory is
made up of type parts.
- A disk designator either which is either a drive letter followed
by a colon, or a UNC servername/sharename "\\servername\sharename".
- A directory on the disk designator.
For APIs that manipulate files, the file names may be relative to
the current directory. A filename is relative to the entire current
directory if it does not begin with a disk designator or a path name
separator. If the file name begins with a path name separator, then
it is relative to the disk designator of the current directory. If
a file name begins with a disk designator, than it is a fully
qualified absolute path name.
The value of lpPathName supplies the current directory. The value
of lpPathName, may be a relative path name as described above, or a
fully qualified absolute path name. In either case, the fully
qualified absolute path name of the specified directory is
calculated and is stored as the current directory.
Arguments:
lpPathName - Supplies the pathname of the directory that is to be
made the current directory.
Return Value:
TRUE - The operation was successful.
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
NTSTATUS Status;
UNICODE_STRING UnicodeString;
BOOL rv;
RtlInitUnicodeString(&UnicodeString,lpPathName);
if ( !CheckForSameCurdir(&UnicodeString) ) {
Status = RtlSetCurrentDirectory_U(&UnicodeString);
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
rv = FALSE;
}
else {
rv = TRUE;
}
}
else {
rv = TRUE;
}
return rv;
}
DWORD
APIENTRY
GetLogicalDrives(
VOID
)
{
NTSTATUS Status;
PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo;
Status = NtQueryInformationProcess( NtCurrentProcess(),
ProcessDeviceMap,
&ProcessDeviceMapInfo.Query,
sizeof( ProcessDeviceMapInfo.Query ),
NULL
);
if (NT_SUCCESS (Status)) {
if (ProcessDeviceMapInfo.Query.DriveMap == 0) {
SetLastError(NO_ERROR);
}
return ProcessDeviceMapInfo.Query.DriveMap;
} else {
BaseSetLastNTError(Status);
return 0;
}
}