/*++ 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; }