503 lines
10 KiB
C
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;
|
|
}
|