/*++ Copyright (c) 1992-2000 Microsoft Corporation Module Name: subst.cxx Abstract: Utility to associate a path to a drive letter Author: THERESES 12-August-1992 Revision History: --*/ #define _NTAPI_ULIB_ #include "ulib.hxx" #include "arg.hxx" #include "array.hxx" #include "smsg.hxx" #include "rtmsg.h" #include "wstring.hxx" #include "path.hxx" #include "substrng.hxx" #include "system.hxx" #include "ulibcl.hxx" #include "subst.hxx" #include "dir.hxx" #include "ntrtl.h" BOOLEAN QuerySubstedDrive( IN DWORD DriveNumber, OUT LPWSTR PhysicalDrive, IN DWORD PhysicalDriveLength, IN LPDWORD DosError ); VOID DisplaySubstUsage( IN OUT PMESSAGE Message ) /*++ Routine Description: This routine displays the usage for the dos 5 label program. Arguments: Message - Supplies an outlet for the messages. Return Value: None. --*/ { Message->Set(MSG_SUBST_INFO); Message->Display(""); Message->Set(MSG_SUBST_USAGE); Message->Display(""); } BOOLEAN DeleteSubst( IN LPWSTR Drive, IN OUT PMESSAGE Message ) { BOOL Success; FSTRING AuxString; DWORD Status; WCHAR Buffer[ MAX_PATH + 8 ]; Success = QuerySubstedDrive( *Drive - ( WCHAR )'@', Buffer, sizeof( Buffer ) / sizeof( WCHAR ), &Status ); if( Success ) { Success = DefineDosDevice( DDD_REMOVE_DEFINITION, Drive, NULL ); if( !Success ) { Status = GetLastError(); } } if (!Success) { if( Status == ERROR_ACCESS_DENIED ) { AuxString.Initialize( Drive ); Message->Set(MSG_SUBST_ACCESS_DENIED); Message->Display("%W",&AuxString); } else { AuxString.Initialize( Drive ); Message->Set(MSG_SUBST_INVALID_PARAMETER); Message->Display("%W",&AuxString); } } return Success != FALSE; } BOOLEAN AddSubst( IN LPWSTR Drive, IN LPWSTR PhysicalDrive, IN DWORD PhysicalDriveLength, IN OUT PMESSAGE Message ) { DWORD Status; FSTRING AuxString; WCHAR Buffer[ MAX_PATH + 8 ]; if( !QuerySubstedDrive( Drive[0] - '@', Buffer, sizeof( Buffer ) / sizeof( WCHAR ), &Status ) ) { if( Status == ERROR_FILE_NOT_FOUND ) { if ( wcslen(PhysicalDrive) == 3 && PhysicalDrive[1] == ':' && PhysicalDrive[2] == '\\' && PhysicalDrive[3] == 0 ) { UNICODE_STRING string; if ( !RtlDosPathNameToNtPathName_U(PhysicalDrive, &string, NULL, NULL) ) { Status = GetLastError(); } else { string.Buffer[string.Length/sizeof(string.Buffer[0]) - 1] = 0; if ( !DefineDosDevice(DDD_RAW_TARGET_PATH, Drive, string.Buffer) ) { Status = GetLastError(); } else { Status = ERROR_SUCCESS; } RtlFreeUnicodeString(&string); } } else if( !DefineDosDevice( 0, Drive, PhysicalDrive ) ) { Status = GetLastError(); } else { Status = ERROR_SUCCESS; } } } else { Status = ERROR_IS_SUBSTED; } if( Status != ERROR_SUCCESS ) { if( Status == ERROR_IS_SUBSTED ) { Message->Set(MSG_SUBST_ALREADY_SUBSTED); Message->Display(""); } else if (Status == ERROR_FILE_NOT_FOUND) { AuxString.Initialize( PhysicalDrive ); Message->Set(MSG_SUBST_PATH_NOT_FOUND); Message->Display("%W", &AuxString); } else if (Status == ERROR_ACCESS_DENIED) { AuxString.Initialize( PhysicalDrive ); Message->Set(MSG_SUBST_ACCESS_DENIED); Message->Display("%W", &AuxString); } else { AuxString.Initialize( Drive ); Message->Set(MSG_SUBST_INVALID_PARAMETER); Message->Display("%W", &AuxString ); } return( FALSE ); } else { return( TRUE ); } } BOOLEAN QuerySubstedDrive( IN DWORD DriveNumber, OUT LPWSTR PhysicalDrive, IN DWORD PhysicalDriveLength, IN LPDWORD DosError ) { WCHAR DriveName[3]; FSTRING DosDevicesPattern; FSTRING DeviceName; CHNUM Position; DriveName[0] = ( WCHAR )( DriveNumber + '@' ); DriveName[1] = ( WCHAR )':'; DriveName[2] = ( WCHAR )'\0'; if( QueryDosDevice( DriveName, PhysicalDrive, PhysicalDriveLength ) != 0 ) { DosDevicesPattern.Initialize( (LPWSTR)L"\\??\\" ); DeviceName.Initialize( PhysicalDrive ); Position = DeviceName.Strstr( &DosDevicesPattern ); if( Position == 0 ) { DeviceName.DeleteChAt( 0, DosDevicesPattern.QueryChCount() ); *DosError = ERROR_SUCCESS; return( TRUE ); } else { // // This is not a Dos device // *DosError = ERROR_INVALID_PARAMETER; return( FALSE ); } } else { *DosError = GetLastError(); return( FALSE ); } } VOID DumpSubstedDrives ( IN OUT PMESSAGE Message ) { DSTRING Source; WCHAR LinkBuffer[MAX_PATH + 8]; DWORD i; FSTRING AuxString; DWORD ErrorCode; Source.Initialize(L"D:\\"); Message->Set(MSG_SUBST_SUBSTED_DRIVE); for (i=1;i<=MAXIMUM_DRIVES;i++) { if (QuerySubstedDrive(i,LinkBuffer,sizeof(LinkBuffer),&ErrorCode)) { Source.SetChAt((WCHAR)(i+'@'),0); if (wcslen(LinkBuffer) == 2 && LinkBuffer[1] == ':' && LinkBuffer[2] == 0) { LinkBuffer[2] = '\\'; LinkBuffer[3] = 0; } AuxString.Initialize( LinkBuffer ); Message->Display("%W%W", &Source, &AuxString); } } } INT __cdecl main( ) /*++ Routine Description: This routine emulates the dos 5 subst command for NT. Arguments: None. Return Value: 1 - An error occured. 0 - Success. --*/ { STREAM_MESSAGE msg; ARGUMENT_LEXEMIZER arglex; ARRAY lex_array; ARRAY arg_array; STRING_ARGUMENT progname; PATH_ARGUMENT virtualdrive_arg; PATH_ARGUMENT physicaldrive_arg; FLAG_ARGUMENT help_arg; FLAG_ARGUMENT delete_arg; PWSTRING p; BOOL Success=TRUE; if (!msg.Initialize(Get_Standard_Output_Stream(), Get_Standard_Input_Stream(), Get_Standard_Error_Stream())) { return 1; } if (!lex_array.Initialize() || !arg_array.Initialize()) { return 1; } if (!arglex.Initialize(&lex_array)) { return 1; } arglex.PutSwitches( "/" ); arglex.PutStartQuotes( "\"" ); arglex.PutEndQuotes( "\"" ); arglex.PutSeparators( " \t" ); arglex.SetCaseSensitive(FALSE); if (!arglex.PrepareToParse()) { return 1; } if ( !arg_array.Initialize() ) { return 1; } if (!progname.Initialize("*") || !help_arg.Initialize("/?") || !virtualdrive_arg.Initialize("*", FALSE) || !physicaldrive_arg.Initialize("*",FALSE) || !delete_arg.Initialize("/D") ) { return 1; } if (!arg_array.Put(&progname) || !arg_array.Put(&virtualdrive_arg) || !arg_array.Put(&physicaldrive_arg) || !arg_array.Put(&help_arg) || !arg_array.Put(&delete_arg) ) { return 1; } if (!arglex.DoParsing(&arg_array)) { if (arglex.QueryLexemeCount() > MAXIMUM_SUBST_ARGS) { msg.Set(MSG_SUBST_TOO_MANY_PARAMETERS); msg.Display("%W", p = arglex.GetLexemeAt(MAXIMUM_SUBST_ARGS)); } else { msg.Set(MSG_SUBST_INVALID_PARAMETER); msg.Display("%W", p = arglex.QueryInvalidArgument()); } DELETE(p); return 1; } if (help_arg.QueryFlag()) { DisplaySubstUsage(&msg); return 0; } if (delete_arg.IsValueSet() && virtualdrive_arg.IsValueSet() && physicaldrive_arg.IsValueSet()) { msg.Set(MSG_SUBST_TOO_MANY_PARAMETERS); msg.Display("%W", delete_arg.GetPattern() ); return 1; } if (delete_arg.IsValueSet() && !virtualdrive_arg.IsValueSet() && !physicaldrive_arg.IsValueSet()) { msg.Set(MSG_SUBST_INVALID_PARAMETER); msg.Display("%W", delete_arg.GetPattern()); return 1; } // // Validate virtual drive // A virtual drive MUST have the format : // Anything that doesn't have this format is considered an invalid parameter // if( virtualdrive_arg.IsValueSet() && ( ( virtualdrive_arg.GetPath()->GetPathString()->QueryChCount() != 2 ) || ( virtualdrive_arg.GetPath()->GetPathString()->QueryChAt( 1 ) != ( WCHAR )':' ) ) ) { msg.Set(MSG_SUBST_INVALID_PARAMETER); msg.Display("%W", virtualdrive_arg.GetPath()->GetPathString() ); return 1; } // // Validate physical drive // A physical drive CANNOT have the format : // if( physicaldrive_arg.IsValueSet() && ( physicaldrive_arg.GetPath()->GetPathString()->QueryChCount() == 2 ) && ( physicaldrive_arg.GetPath()->GetPathString()->QueryChAt( 1 ) == ( WCHAR )':' ) ) { msg.Set(MSG_SUBST_INVALID_PARAMETER); msg.Display("%W", physicaldrive_arg.GetPath()->GetPathString() ); return 1; } // if (virtualdrive_arg.IsValueSet()) { DSTRING virtualdrivepath; DSTRING colon; PATH TmpPath; PFSN_DIRECTORY Directory; virtualdrivepath.Initialize(virtualdrive_arg.GetPath()->GetPathString()); if (virtualdrivepath.Strupr() ) { if (delete_arg.IsValueSet()) { Success = DeleteSubst(virtualdrivepath.QueryWSTR(),&msg); } else if (physicaldrive_arg.IsValueSet()) { LPWSTR physicaldrivepath; // // verify that the physical drive is an accessible path // Directory = SYSTEM::QueryDirectory( physicaldrive_arg.GetPath() ); if( !Directory ) { msg.Set(MSG_SUBST_PATH_NOT_FOUND); msg.Display("%W", physicaldrive_arg.GetPath()->GetPathString()); return 1; } DELETE( Directory ); TmpPath.Initialize( physicaldrive_arg.GetPath(), TRUE ); physicaldrivepath = ( TmpPath.GetPathString() )->QueryWSTR(); Success = AddSubst(virtualdrivepath.QueryWSTR(), physicaldrivepath, ( TmpPath.GetPathString() )->QueryChCount(), &msg ); DELETE(physicaldrivepath); } else { msg.Set(MSG_SUBST_INVALID_PARAMETER); msg.Display("%W", p = arglex.GetLexemeAt(1)); DELETE(p); return 1; } } } else { if (arglex.QueryLexemeCount() > 1) { msg.Set(MSG_SUBST_INVALID_PARAMETER); msg.Display("%W", p = arglex.GetLexemeAt(1)); DELETE(p); return 1; } else { DumpSubstedDrives(&msg); } } return !Success; }