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

503 lines
10 KiB
C

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
curdir.c
Abstract:
This module implements the directory commands.
Author:
Wesley Witt (wesw) 21-Oct-1998
Revision History:
--*/
#include "cmdcons.h"
#pragma hdrstop
//
// Each entry in _CurDirs always starts and ends with a \.
//
LPWSTR _CurDirs[26];
WCHAR _CurDrive;
LPWSTR _NtDrivePrefixes[26];
BOOLEAN AllowAllPaths;
VOID
RcAddDrive(
WCHAR DriveLetter
)
{
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING UnicodeString;
WCHAR name[20];
HANDLE Handle;
NTSTATUS Status;
ASSERT(_NtDrivePrefixes[(int)(DriveLetter - L'A')] == NULL);
swprintf(name,L"\\DosDevices\\%c:", DriveLetter);
INIT_OBJA(&Obja, &UnicodeString, name);
Status = ZwOpenSymbolicLinkObject(&Handle, READ_CONTROL | SYMBOLIC_LINK_QUERY, &Obja);
if (NT_SUCCESS(Status)) {
ZwClose(Handle);
_NtDrivePrefixes[(int)(DriveLetter - L'A')] = SpDupStringW(name);
}
}
VOID
RcRemoveDrive(
WCHAR DriveLetter
)
{
ASSERT(_NtDrivePrefixes[(int)(DriveLetter - L'A')] != NULL);
SpMemFree(_NtDrivePrefixes[(int)(DriveLetter - L'A')]);
_NtDrivePrefixes[(int)(DriveLetter - L'A')] = NULL;
}
VOID
RcInitializeCurrentDirectories(
VOID
)
{
unsigned i;
RtlZeroMemory( _CurDirs, sizeof(_CurDirs) );
RtlZeroMemory( _NtDrivePrefixes, sizeof(_NtDrivePrefixes) );
//
// Initially, the current directory on all drives
// is the root.
//
for( i=0; i<26; i++ ) {
_CurDirs[i] = SpDupStringW(L"\\");
}
//
// Now go set up the NT drive prefixes for each drive in the system.
// For each drive letter, we see whether it exists in the \DosDevices
// directory as a symbolic link.
//
for( i=0; i<26; i++ ) {
RcAddDrive((WCHAR)(i+L'A'));
}
//
// NOTE: need to determine this by tracking the lowest
// valid drive letter from the loop above, taking into account
// floppy drives.
//
//
_CurDrive = L'C';
// fixed by using the drive letter for the selected install of NT
// this is done in in logon.c .
return;
}
VOID
RcTerminateCurrentDirectories(
VOID
)
{
unsigned i;
for( i=0; i<26; i++ ) {
if( _CurDirs[i] ) {
SpMemFree(_CurDirs[i]);
_CurDirs[i] = NULL;
}
if( _NtDrivePrefixes[i] ) {
SpMemFree(_NtDrivePrefixes[i]);
_NtDrivePrefixes[i] = NULL;
}
}
}
BOOLEAN
RcFormFullPath(
IN LPCWSTR PartialPath,
OUT LPWSTR FullPath,
IN BOOLEAN NtPath
)
/*++
Routine Description:
This routine is similar to the Win32 GetFullPathName() API.
It takes an arbitrary pathspec and converts it to a full one,
by merging in the current drive and directory if necessary.
The output is a fully-qualified NT pathname equivalent to
the partial spec given.
Processing includes all your favorite Win32isms, including
collapsing adjacent dots and slashes, stripping trailing spaces,
handling . and .., etc.
Arguments:
PartialPath - supplies a (dos-style) path spec of arbitrary qualification.
FullPath - receives the equivalent fully-qualified NT path. The caller
must ensure that this buffer is large enough.
NtPath - if TRUE, we want a fully canonicalized NT path. Otherwise we want
a DOS path.
Return Value:
FALSE if failure, indicating an invalid drive spec or syntactically
invalid path. TRUE otherwise.
--*/
{
unsigned len;
unsigned len2;
LPCWSTR Prefix;
PDISK_REGION Region;
WCHAR Buffer[MAX_PATH*2];
//
// The first thing we do is to form the fully qualified path
// by merging in the current drive and directory, if necessary.
//
// Check for leading drive in the form X:.
//
if((wcslen(PartialPath) >= 2) && (PartialPath[1] == L':') && RcIsAlpha(PartialPath[0])) {
//
// Got leading drive, transfer it into the target.
//
FullPath[0] = PartialPath[0];
PartialPath += 2;
} else {
//
// No leading drive, use current drive.
//
FullPath[0] = _CurDrive;
}
//
// Make sure we've got a drive we think is valid.
//
Prefix = _NtDrivePrefixes[RcToUpper(FullPath[0])-L'A'];
if(!Prefix) {
return(FALSE);
}
FullPath[1] = L':';
FullPath[2] = 0;
//
// Now deal with the path part. If the next character in the input
// is \ then we have a rooted path, otherwise we need to merge in
// the current directory for the drive.
//
if(PartialPath[0] != L'\\') {
wcscat(FullPath,_CurDirs[RcToUpper(FullPath[0])-L'A']);
}
wcscat(FullPath,PartialPath);
//
// Disallow ending with \ except for the root.
//
len = wcslen(FullPath);
if((len > 3) && (FullPath[len-1] == L'\\')) {
FullPath[len-1] = 0;
}
//
// Now that we've done this, we need to call RtlGetFullPathName_U
// to get full win32 naming semantics, for example, stripping
// trailing spaces, coalescing adjacent dots, processing . and .., etc.
// We get at that API via setupdd.sys.
//
if(!NT_SUCCESS(SpGetFullPathName(FullPath))) {
return(FALSE);
}
len = wcslen(FullPath) * sizeof(WCHAR);
//
// check if the path is too long to be
// handled by our routines [MAX_PATH*2] limit
//
// Note : RcGetNTFileName is called irrespective of whether caller
// requested it or not to do proper error handling at the caller.
//
if ((len < sizeof(Buffer)) && RcGetNTFileName(FullPath, Buffer)){
if (NtPath)
wcscpy(FullPath, Buffer);
}
else
return FALSE;
return TRUE;
}
VOID
RcGetCurrentDriveAndDir(
OUT LPWSTR Output
)
{
ULONG len;
Output[0] = _CurDrive;
Output[1] = L':';
wcscpy(Output+2,_CurDirs[_CurDrive-L'A']);
//
// Strip off trailing \ except in root case.
//
len = wcslen(Output);
if( (len > 3) && (Output[len-1] == L'\\') ) {
Output[len-1] = 0;
}
}
WCHAR
RcGetCurrentDriveLetter(
VOID
)
{
return(_CurDrive);
}
BOOLEAN
RcIsDriveApparentlyValid(
IN WCHAR DriveLetter
)
{
return((BOOLEAN)(_NtDrivePrefixes[RcToUpper(DriveLetter)-L'A'] != NULL));
}
ULONG
RcCmdSwitchDrives(
IN WCHAR DriveLetter
)
{
//
// If there's no NT equivalent for this drive, then we can't
// switch to it.
//
if( !RcIsDriveApparentlyValid(DriveLetter) ) {
RcMessageOut(MSG_INVALID_DRIVE);
return 1;
}
//
// NOTE should we attempt to open the root of the drive,
// so we can mimic cmd.exe's behavior of refusing to set
// the current drive when say there's no floppy in the drive?
// There's really no great reason to do this except that it might
// be a little less confusing for the user.
//
// No.
//
_CurDrive = RcToUpper(DriveLetter);
return 1;
}
ULONG
RcCmdChdir(
IN PTOKENIZED_LINE TokenizedLine
)
{
unsigned u;
WCHAR *p,*Arg;
HANDLE Handle;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING UnicodeString;
OBJECT_ATTRIBUTES Obja;
NTSTATUS Status;
if (RcCmdParseHelp( TokenizedLine, MSG_CHDIR_HELP )) {
return 1;
}
if (TokenizedLine->TokenCount == 1) {
RcGetCurrentDriveAndDir(_CmdConsBlock->TemporaryBuffer);
RcRawTextOut(_CmdConsBlock->TemporaryBuffer,-1);
return 1;
}
p = _CmdConsBlock->TemporaryBuffer;
//
// Get the argument. Special case x:, to print out the
// current directory on that drive.
//
Arg = TokenizedLine->Tokens->Next->String;
if(RcIsAlpha(Arg[0]) && (Arg[1] == L':') && (Arg[2] == 0)) {
Arg[0] = RcToUpper(Arg[0]);
u = Arg[0] - L'A';
if(_NtDrivePrefixes[u] && _CurDirs[u]) {
RcTextOut(Arg);
//
// Strip off the terminating \ except in root case.
//
wcscpy(p,_CurDirs[u]);
u = wcslen(p);
if((u > 1) && (p[u-1] == L'\\')) {
p[u-1] = 0;
}
RcTextOut(p);
RcTextOut(L"\r\n");
} else {
RcMessageOut(MSG_INVALID_DRIVE);
}
return 1;
}
//
// Got a new directory spec. Canonicalize it to a fully qualified
// DOS-style path. Check the drive to make sure it's legal.
//
if(!RcFormFullPath(Arg,p,FALSE)) {
RcMessageOut(MSG_INVALID_PATH);
return 1;
}
if(!_NtDrivePrefixes[RcToUpper(p[0])-L'A']) {
RcMessageOut(MSG_INVALID_DRIVE);
return 1;
}
//
// Check the directory to make sure it exists.
//
if(!RcFormFullPath(Arg,p,TRUE)) {
RcMessageOut(MSG_INVALID_PATH);
return 1;
}
INIT_OBJA(&Obja,&UnicodeString,p);
Status = ZwOpenFile(
&Handle,
FILE_READ_ATTRIBUTES,
&Obja,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE
);
if(!NT_SUCCESS(Status)) {
RcNtError(Status,MSG_INVALID_PATH);
return 1;
}
ZwClose(Handle);
//
// OK, it's a valid directory on a valid drive.
// Form a path that starts and ends with \.
//
if(!RcFormFullPath(Arg,p,FALSE)) {
RcMessageOut(MSG_INVALID_PATH);
return 1;
}
if (!RcIsPathNameAllowed(p,TRUE,FALSE)) {
RcMessageOut(MSG_ACCESS_DENIED);
return 1;
}
p += 2; // skip x:
u = wcslen(p);
if(!u || (p[u-1] != L'\\')) {
p[u] = L'\\';
p[u+1] = 0;
}
u = RcToUpper(p[-2]) - L'A';
if(_CurDirs[u]) {
SpMemFree(_CurDirs[u]);
}
_CurDirs[u] = SpDupStringW(p);
return 1;
}
ULONG
RcCmdSystemRoot(
IN PTOKENIZED_LINE TokenizedLine
)
{
ULONG u;
WCHAR buf[MAX_PATH];
if (RcCmdParseHelp( TokenizedLine, MSG_SYSTEMROOT_HELP )) {
return 1;
}
//
// set the current drive to the correct one.
//
if (SelectedInstall == NULL) {
return 1;
}
_CurDrive = SelectedInstall->DriveLetter;
//
// set the current dir to the correct one.
//
RtlZeroMemory( buf, sizeof(buf) );
wcscat( buf, L"\\" );
wcscat( buf, SelectedInstall->Path );
wcscat( buf, L"\\" );
u = RcToUpper(SelectedInstall->DriveLetter) - L'A';
if( _CurDirs[u] ) {
SpMemFree(_CurDirs[u]);
}
_CurDirs[u] = SpDupStringW( buf );
return 1;
}