/*++ Copyright (c) 1991-2001 Microsoft Corporation Module Name: convert.cxx Abstract: This module contains the definition of the CONVERT class, which implements the File System Conversion Utility. Author: Ramon J. San Andres (ramonsa) sep-23-1991 Environment: ULIB, User Mode --*/ #define _NTAPI_ULIB_ #include "ulib.hxx" #if defined(FE_SB) && defined(_X86_) #include "machine.hxx" #endif #include "ulibcl.hxx" #include "arg.hxx" #include "file.hxx" #include "smsg.hxx" #include "rtmsg.h" #include "wstring.hxx" #include "system.hxx" #include "autoreg.hxx" #include "ifssys.hxx" #include "ifsentry.hxx" #include "hmem.hxx" #include "convert.hxx" #include "supera.hxx" extern "C" { #include #include "undo.h" } #include "fatofs.hxx" #define AUTOCHK_PROGRAM_NAME L"AUTOCHK.EXE" #define AUTOCONV_PROGRAM_NAME L"AUTOCONV.EXE" #define AUTOCHK_NAME L"AUTOCHK" #define AUTOCONV_NAME L"AUTOCONV" #define VALUE_NAME_PATH L"PATH" #define VALUE_NAME_ARGS L"ARGUMENTS" #define VALUE_NAME_FS L"TARGET FILESYSTEM" // // Scheduling status codes // #define CONV_STATUS_NONE 0 #define CONV_STATUS_SCHEDULED 1 static WCHAR NameBuffer[16]; // holds cvf name DWORD WINAPI SceConfigureConvertedFileSecurity( IN PWSTR pszDriveName, IN DWORD dwConvertDisposition ); INT __cdecl main ( ) /*++ Routine Description: Entry point for the conversion utility. Arguments: None. Return Value: One of the CONVERT exit codes. Notes: --*/ { INT ExitCode = EXIT_ERROR; // Let's be pessimistic DEFINE_CLASS_DESCRIPTOR( CONVERT ); { CONVERT Convert; // // Initialize the CONVERT object. // if ( Convert.Initialize( &ExitCode ) ) { // // Do the conversion // ExitCode = Convert.Convert(); } } return ExitCode; } DEFINE_CONSTRUCTOR( CONVERT, PROGRAM ); NONVIRTUAL VOID CONVERT::Construct ( ) /*++ Routine Description: converts a CONVERT object Arguments: None. Return Value: None. Notes: --*/ { _Autochk = NULL; _Autoconv = NULL; } NONVIRTUAL VOID CONVERT::Destroy ( ) /*++ Routine Description: Destroys a CONVERT object Arguments: None. Return Value: None. Notes: --*/ { DELETE( _Autochk ); DELETE( _Autoconv ); } CONVERT::~CONVERT ( ) /*++ Routine Description: Destructs a CONVERT object Arguments: None. Return Value: None. Notes: --*/ { Destroy(); } BOOLEAN CONVERT::Initialize ( OUT PINT ExitCode ) /*++ Routine Description: Initializes the CONVERT object. Initialization consist of allocating memory for certain object members and argument parsing. Arguments: ExitCode - Supplies pointer to CONVERT exit code. Return Value: BOOLEAN - TRUE if initialization succeeded, FALSE otherwise. Notes: --*/ { Destroy(); // // Initialize program object // if ( PROGRAM::Initialize( MSG_CONV_USAGE ) ) { // // Parse the arguments // return ParseArguments( ExitCode ); } // // Could not initialize the program object. // *ExitCode = EXIT_ERROR; return FALSE; } INT CONVERT::Convert ( ) /*++ Routine Description: Converts the file system in a volume. Depending on the current file system, it loads the appropriate conversion library and calls its conversion entry point. Arguments: None Return Value: INT - One of the CONVERT return codes Notes: --*/ { DSTRING CurrentFsName; // Name of current FS in volume DSTRING LibraryName; // Name of library to load DSTRING EntryPoint; // Name of entry point in DLL DSTRING fat_name; DSTRING fat32_name; DSTRING driveletter; DSTRING user_old_label; DSTRING null_string; PWSTRING old_volume_label = NULL; PATH dos_drive_path; INT ExitCode = EXIT_SUCCESS; // CONVERT exit code CONVERT_STATUS ConvertStatus; // Conversion status NTSTATUS Status; // NT API status HANDLE FsUtilityHandle; // Handle to DLL CONVERT_FN Convert; // Pointer to entry point in DLL IS_CONVERSION_AVAIL_FN IsConversionAvailable; DWORD OldErrorMode; DRIVE_TYPE drive_type; VOL_SERIAL_NUMBER old_serial; BOOLEAN Error = FALSE; BOOLEAN Success; BOOLEAN Result; ULONG flags; #if defined(FE_SB) && defined(_X86_) if(IsPC98_N()){ CONVERT Convert2; Convert2.ChangeBPB1(&_NtDrive); } #endif // Check to see if this is an ARC System Partition--if it // is, don't convert it. // if( IFS_SYSTEM::IsArcSystemPartition( &_NtDrive, &Error ) ) { DisplayMessage( MSG_CONV_ARC_SYSTEM_PARTITION, ERROR_MESSAGE ); return EXIT_ERROR; } // // Ask the volume what file system it has, and use that name to // figure out what DLL to load. // if ( !IFS_SYSTEM::QueryFileSystemName( &_NtDrive, &CurrentFsName, &Status )) { if ( Status == STATUS_ACCESS_DENIED ) { DisplayMessage( MSG_DASD_ACCESS_DENIED, ERROR_MESSAGE ); } else { DisplayMessage( MSG_FS_NOT_DETERMINED, ERROR_MESSAGE, "%W", &_DisplayDrive ); } return EXIT_ERROR; } CurrentFsName.Strupr(); _FsName.Strupr(); if( CurrentFsName == _FsName ) { DisplayMessage( MSG_CONV_ALREADY_CONVERTED, ERROR_MESSAGE, "%W%W", &_DisplayDrive, &_FsName ); return EXIT_ERROR; } if (!fat_name.Initialize("FAT") || !fat32_name.Initialize("FAT32")) { DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE); return EXIT_ERROR; } if (CurrentFsName == fat_name || CurrentFsName == fat32_name) { if ( !LibraryName.Initialize( "CNVFAT" ) || !EntryPoint.Initialize( "IsConversionAvailable" ) ) { DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE ); return( EXIT_ERROR ); } // // Get pointer to the conversion entry point and convert the volume. // if (NULL == (IsConversionAvailable = (IS_CONVERSION_AVAIL_FN)SYSTEM::QueryLibraryEntryPoint( &LibraryName, &EntryPoint, &FsUtilityHandle ))) { // // There is no conversion DLL for the file system in the volume. // DisplayMessage( MSG_FS_NOT_SUPPORTED, ERROR_MESSAGE, "%s%W", "CONVERT", &CurrentFsName ); return EXIT_ERROR; } if (IsConversionAvailable(&_FsName) == -1) { DisplayMessage ( MSG_CONV_CONVERSION_NOT_AVAILABLE, ERROR_MESSAGE, "%W%W", &CurrentFsName, &_FsName ); return EXIT_ERROR; } } else { DisplayMessage( MSG_FS_NOT_SUPPORTED, ERROR_MESSAGE, "%s%W", "CONVERT", &CurrentFsName ); return EXIT_ERROR; } // // Display the current file system type. (Standard in all file system utilities) // DisplayMessage( MSG_FILE_SYSTEM_TYPE, NORMAL_MESSAGE, "%W", &CurrentFsName ); // // We also initialize the name of the conversion entry point in the DLL // ("Convert") // if ( !EntryPoint.Initialize( "ConvertFAT" )) { DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE ); return( EXIT_ERROR ); } // // Get pointer to the conversion entry point and convert the volume. // if (NULL == (Convert = (CONVERT_FN)SYSTEM::QueryLibraryEntryPoint( &LibraryName, &EntryPoint, &FsUtilityHandle ))) { // // There is no conversion DLL for the file system in the volume. // DisplayMessage( MSG_FS_NOT_SUPPORTED, ERROR_MESSAGE, "%s%W", "CONVERT", &CurrentFsName ); return EXIT_ERROR; } // If the volume has a label, prompt the user for it. // Note that if it has no label we do nothing. OldErrorMode = SetErrorMode( SEM_FAILCRITICALERRORS ); drive_type = SYSTEM::QueryDriveType(&_DosDrive); if (null_string.Initialize( "" ) && (drive_type != RemovableDrive) && dos_drive_path.Initialize( &_DosDrive) && (old_volume_label = SYSTEM::QueryVolumeLabel( &dos_drive_path, &old_serial )) != NULL && old_volume_label->Stricmp( &null_string ) != 0 ) { // This drive has a label. To give the user // a bit more protection, prompt for the old label: DisplayMessage( MSG_ENTER_CURRENT_LABEL, NORMAL_MESSAGE, "%W", &_DisplayDrive ); _Message.QueryStringInput( &user_old_label ); if( old_volume_label->Stricmp( &user_old_label ) != 0 ) { // Re-enable hard error popups. SetErrorMode( OldErrorMode ); DisplayMessage( MSG_WRONG_CURRENT_LABEL, ERROR_MESSAGE ); DELETE( old_volume_label ); return EXIT_ERROR; } } // Re-enable hard error popups SetErrorMode( OldErrorMode ); DELETE( old_volume_label ); BOOLEAN delete_uninstall_backup = FALSE; OSVERSIONINFOEX os_version_info; if (Uninstall_Valid == IsUninstallImageValid(Uninstall_FatToNtfsConversion, &os_version_info)) { DisplayMessage( MSG_CONV_DELETE_UNINSTALL_BACKUP, NORMAL_MESSAGE ); if (!_Message.IsYesResponse(FALSE)) { return EXIT_ERROR; } delete_uninstall_backup = TRUE; } flags = _Verbose ? CONVERT_VERBOSE_FLAG : 0; flags |= _NoChkdsk ? CONVERT_NOCHKDSK_FLAG : 0; flags |= _ForceDismount ? CONVERT_FORCE_DISMOUNT_FLAG : 0; flags |= _NoSecurity ? CONVERT_NOSECURITY_FLAG : 0; // // no pause flag needed since we don't want to pause // Result = Convert( &_NtDrive, &_FsName, &_CvtZoneFileName, &_Message, flags, &ConvertStatus); SYSTEM::FreeLibraryHandle( FsUtilityHandle ); if ( Result ) { DWORD sce_result = NO_ERROR; ExitCode = EXIT_SUCCESS; if (!_NoSecurity && _DosDrive.QueryChCount() == 2 && _DosDrive.QueryChAt(1) == ':') { DSTRING msg; sce_result = SceConfigureConvertedFileSecurity( (PWSTR)_DosDrive.GetWSTR(), 0 ); if ( sce_result != NO_ERROR ) { if ( SYSTEM::QueryWindowsErrorMessage( sce_result, &msg ) ) { DisplayMessage( MSG_CONV_SCE_FAILURE_WITH_MESSAGE, ERROR_MESSAGE, "%W", &msg ); } else { DisplayMessage( MSG_CONV_SCE_SET_FAILURE, ERROR_MESSAGE, "%d", sce_result ); } } } if (!NT_SUCCESS(SUPERAREA::GenerateLabelNotification(&_NtDrive))) { DisplayMessage( MSG_CONV_UNABLE_TO_NOTIFY, ERROR_MESSAGE ); ExitCode = EXIT_ERROR; } if (delete_uninstall_backup && !RemoveUninstallImage()) { DWORD last_error = GetLastError(); DSTRING errmsg; if (SYSTEM::QueryWindowsErrorMessage( last_error, &errmsg )) { DisplayMessage( MSG_CONV_DELETE_UNINSTALL_BACKUP_ERROR, ERROR_MESSAGE, "%W", &errmsg ); } else { DisplayMessage( MSG_CONV_DELETE_UNINSTALL_BACKUP_ERROR, ERROR_MESSAGE, "%d", last_error ); } ExitCode = EXIT_ERROR; } // // We're done. // if ( sce_result == NO_ERROR && ExitCode == EXIT_SUCCESS ) { DisplayMessage( MSG_CONV_CONVERSION_COMPLETE, NORMAL_MESSAGE ); return EXIT_SUCCESS; } else { return EXIT_ERROR; } } else { // // The conversion was not successful. Determine what the problem // was and return the appropriate CONVERT exit code. // switch ( ConvertStatus ) { case CONVERT_STATUS_CONVERTED: // // This is an inconsistent state, Convert should return // TRUE if the conversion was successful! // DebugPrintTrace(( "CONVERT Error: Conversion failed, but status is success!\n" )); DebugAssert( FALSE ); DisplayMessage( MSG_CONV_CONVERSION_MAYHAVE_FAILED, ERROR_MESSAGE, "%W%W", &_DisplayDrive, &_FsName ); ExitCode = EXIT_ERROR; break; case CONVERT_STATUS_INVALID_FILESYSTEM: // // The conversion DLL does not recognize the target file system. // DisplayMessage( MSG_CONV_INVALID_FILESYSTEM, ERROR_MESSAGE, "%W", &_FsName ); ExitCode = EXIT_UNKNOWN; break; case CONVERT_STATUS_CONVERSION_NOT_AVAILABLE: // // The target file system is valid, but the conversion is not // available. // DisplayMessage( MSG_CONV_CONVERSION_NOT_AVAILABLE, ERROR_MESSAGE, "%W%W", &CurrentFsName, &_FsName ); ExitCode = EXIT_NOCANDO; break; case CONVERT_STATUS_NTFS_RESERVED_NAMES: DisplayMessage( MSG_CONV_NTFS_RESERVED_NAMES, ERROR_MESSAGE, "%W", &_DisplayDrive ); ExitCode = EXIT_ERROR; break; case CONVERT_STATUS_WRITE_PROTECTED: DisplayMessage( MSG_CONV_WRITE_PROTECTED, ERROR_MESSAGE, "%W", &_DisplayDrive ); ExitCode = EXIT_ERROR; break; case CONVERT_STATUS_CANNOT_LOCK_DRIVE: // // The drive cannot be locked. We must schedule ChkDsk and AutoConv // to do the job during the next system boot. // DisplayMessage( MSG_CONVERT_ON_REBOOT_PROMPT, NORMAL_MESSAGE, "%W", &_DisplayDrive ); // Note that ScheduleAutoConv reports its success or // failure, so no additional messages are required. // if ( _Message.IsYesResponse( FALSE ) && ScheduleAutoConv() ) { if (!_NoSecurity && _DosDrive.QueryChCount() == 2 && _DosDrive.QueryChAt(1) == ':') { DSTRING msg; DWORD sce_result; sce_result = SceConfigureConvertedFileSecurity( (PWSTR)_DosDrive.GetWSTR(), 1 ); if ( sce_result != NO_ERROR ) { if ( SYSTEM::QueryWindowsErrorMessage( sce_result, &msg ) ) { DisplayMessage( MSG_CONV_SCE_FAILURE_WITH_MESSAGE, ERROR_MESSAGE, "%W", &msg ); } else { DisplayMessage( MSG_CONV_SCE_SCHEDULE_FAILURE, ERROR_MESSAGE, "%d", sce_result ); } // // error in setting up security for files // ExitCode = EXIT_ERROR; } else { ExitCode = EXIT_SCHEDULED; } } else { // // no need to worry about security and so return success // ExitCode = EXIT_SCHEDULED; } if (delete_uninstall_backup && !RemoveUninstallImage()) { DWORD last_error = GetLastError(); DSTRING errmsg; if (SYSTEM::QueryWindowsErrorMessage( last_error, &errmsg )) { DisplayMessage( MSG_CONV_DELETE_UNINSTALL_BACKUP_ERROR, ERROR_MESSAGE, "%W", &errmsg ); } else { DisplayMessage( MSG_CONV_DELETE_UNINSTALL_BACKUP_ERROR, ERROR_MESSAGE, "%d", last_error ); } ExitCode = EXIT_ERROR; } } else { // // Don't want to schedule a convert or scheduling failed // ExitCode = EXIT_ERROR; } break; case CONVERT_STATUS_INSUFFICIENT_FREE_SPACE: case CONVERT_STATUS_DRIVE_IS_DIRTY: case CONVERT_STATUS_ERROR: // // The conversion failed. // DisplayMessage( MSG_CONV_CONVERSION_FAILED, ERROR_MESSAGE, "%W%W", &_DisplayDrive, &_FsName ); if(ConvertStatus == CONVERT_STATUS_INSUFFICIENT_FREE_SPACE) { ExitCode = EXIT_NOFREESPACE; } else { ExitCode = EXIT_ERROR; } break; default: // // Invalid status code // DebugPrintTrace(( "CONVERT Error: Convert status code %X invalid!\n", ConvertStatus )); DisplayMessage( MSG_CONV_CONVERSION_FAILED, ERROR_MESSAGE, "%W%W", &_DisplayDrive, &_FsName ); ExitCode = EXIT_ERROR; break; } return ExitCode; } } PPATH CONVERT::FindSystemFile( IN PWSTR FileName ) /*++ Routine Description: Makes sure that the given file is in the system directory. Arguments: FileName - Supplies the name of the file to look for. Return Value: PPATH - Path to the file found --*/ { DSTRING Name; PPATH Path = NULL; PFSN_FILE File = NULL; if ( !(Path = SYSTEM::QuerySystemDirectory() ) ) { DisplayMessage( MSG_CONV_CANNOT_FIND_SYSTEM_DIR, ERROR_MESSAGE ); return FALSE; } if ( !Name.Initialize( FileName ) || !Path->AppendBase( &Name ) ) { DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE ); DELETE( Path ); return FALSE; } if ( !(File = SYSTEM::QueryFile( Path )) ) { DisplayMessage( MSG_CONV_CANNOT_FIND_FILE, ERROR_MESSAGE, "%W", Path->GetPathString() ); DELETE( Path ); return FALSE; } DELETE( File ); return Path; } BOOLEAN CONVERT::ParseArguments( OUT PINT ExitCode ) /*++ Routine Description: Parses the command line and sets the parameters used by the conversion utility. The arguments accepted are: drive: Drive to convert /fs:fsname File system to convert to /v Verbose mode /? Help /CVTAREA:filename Filename for convert zone as place holder for the $MFT, $Logfile, and Volume bitmap /NoSecurity Allow everyone access /NoChkdsk Skip chkdsk /x Force a dismount on the volume if necessary Arguments: ExitCode - Supplies pointer to CONVERT exit code Return Value: BOOLEAN - TRUE if arguments were parsed correctly and program can continue. FALSE if the program should exit. ExitCode contains the value with which the program should exit. Note that this does not necessarily means an error (e.g. user requested help). --*/ { UCHAR SequenceNumber; PATH path; DSTRING drive_path_string; PATH_ANALYZE_CODE rst; DebugPtrAssert( ExitCode ); // // Parse command line // if ( !ParseCommandLine( NULL, TRUE ) ) { *ExitCode = EXIT_ERROR; return FALSE; } // // If the user requested help, give it. // if ( _Help ) { DisplayMessage( MSG_CONV_USAGE ); *ExitCode = EXIT_SUCCESS; return FALSE; } #ifdef DBLSPACE_ENABLED if (_Compress && !_Uncompress) { // // We don't allow you to specify /c (compress resulting // filesystem) unless the source filesystem has dblspace. // DisplayMessage(MSG_CONV_SLASH_C_INVALID, ERROR_MESSAGE); *ExitCode = EXIT_ERROR; return FALSE; } #endif // DBLSPACE_ENABLED // // If the command line did not specify a drive, we use the // current drive. // if ( _DosDrive.QueryChCount() == 0 ) { if ( !SYSTEM::QueryCurrentDosDriveName( &_DosDrive ) ) { DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE ); *ExitCode = EXIT_ERROR; return FALSE; } } if ( !path.Initialize( &_DosDrive ) ) { DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE ); *ExitCode = EXIT_ERROR; return FALSE; } rst = path.AnalyzePath( &_GuidDrive, &_FullPath, &drive_path_string ); switch (rst) { case PATH_OK: case PATH_COULD_BE_FLOPPY: if (drive_path_string.QueryChCount() != 0) { DisplayMessage( MSG_CONV_INVALID_DRIVE_SPEC, ERROR_MESSAGE ); *ExitCode = EXIT_ERROR; return FALSE; } if (path.IsGuidVolName()) { if (!_DisplayDrive.Initialize(&_GuidDrive)) { DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE ); *ExitCode = EXIT_ERROR; return FALSE; } } else { if (!_DisplayDrive.Initialize(_FullPath.GetPathString())) { DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE ); *ExitCode = EXIT_ERROR; return FALSE; } } if (_FullPath.GetPathString()->QueryChCount() == 2 && _FullPath.GetPathString()->QueryChAt(1) == (WCHAR)':') { // if there is a drive letter for this drive, use it // instead of the guid volume name if (!_DosDrive.Initialize(_FullPath.GetPathString())) { DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE ); *ExitCode = EXIT_ERROR; return FALSE; } } else { if (!_DosDrive.Initialize(&_GuidDrive)) { DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE ); *ExitCode = EXIT_ERROR; return FALSE; } } break; case PATH_OUT_OF_MEMORY: DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE ); *ExitCode = EXIT_ERROR; return FALSE; case PATH_NO_MOUNT_POINT_FOR_VOLUME_NAME_PATH: DisplayMessage( MSG_CONV_NO_MOUNT_POINT_FOR_GUID_VOLNAME_PATH, ERROR_MESSAGE ); *ExitCode = EXIT_ERROR; return FALSE; default: DisplayMessage( MSG_CONV_INVALID_DRIVE_SPEC, ERROR_MESSAGE ); *ExitCode = EXIT_ERROR; return FALSE; } if (!_DosDrive.Strupr()) { DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE ); *ExitCode = EXIT_ERROR; return FALSE; } // // Make sure that drive is valid and is not remote. // switch ( SYSTEM::QueryDriveType( &_DosDrive ) ) { case UnknownDrive: DisplayMessage( MSG_CONV_INVALID_DRIVE_SPEC, ERROR_MESSAGE ); *ExitCode = EXIT_ERROR; return FALSE; case CdRomDrive: DisplayMessage( MSG_CONV_CANT_CDROM, ERROR_MESSAGE ); *ExitCode = EXIT_ERROR; return FALSE; case RemoteDrive: DisplayMessage( MSG_CONV_CANT_NETWORK, ERROR_MESSAGE ); *ExitCode = EXIT_ERROR; return FALSE; default: break; } // // Make sure a target file system was specified. Note that we do not // validate the file system, we accept any string. // if ( _FsName.QueryChCount() == 0 ) { DisplayMessage( MSG_CONV_NO_FILESYSTEM_SPECIFIED, ERROR_MESSAGE ); *ExitCode = EXIT_ERROR; return FALSE; } // // Set other object members. // if ( !IFS_SYSTEM::DosDriveNameToNtDriveName( &_DosDrive, &_NtDrive )) { DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE ); *ExitCode = EXIT_ERROR; return FALSE; } #ifdef DBLSPACE_ENABLED // // If we're to uncompress a dblspace volume, generate the cvf name. // if (_Uncompress) { swprintf(NameBuffer, L"DBLSPACE.%03d", SequenceNumber); if ( _FullPath.GetPathString()->QueryChCount() == 0 ) { DisplayMessage( MSG_CONV_NO_MOUNT_POINT_FOR_GUID_VOLNAME_PATH, ERROR_MESSAGE ); *ExitCode = EXIT_ERROR; return FALSE; } if (!_CvfName.Initialize(NameBuffer)) { DisplayMessage(MSG_CONV_NO_MEMORY, ERROR_MESSAGE); *ExitCode = EXIT_ERROR; return FALSE; } } #endif // DBLSPACE_ENABLED *ExitCode = EXIT_SUCCESS; return TRUE; } BOOLEAN CONVERT::ParseCommandLine ( IN PCWSTRING CommandLine, IN BOOLEAN Interactive ) /*++ Routine Description: Parses the CONVERT (AUTOCONV) command line. The arguments accepted are: drive: Drive to convert /fs:fsname File system to convert to /v Verbose mode /uncompress[:sss] Convert from dblspace /c Compress resulting filesystem /? Help /CVTAREA:filename Filename for convert zone as place holder for the $MFT, $Logfile, and Volume bitmap Arguments: CommandLine - Supplies command line to parse Interactive - Supplies Interactive flag Return Value: BOOLEAN - TRUE if arguments were parsed correctly. --*/ { ARRAY ArgArray; // Array of arguments ARRAY LexArray; // Array of lexemes ARGUMENT_LEXEMIZER ArgLex; // Argument Lexemizer STRING_ARGUMENT DriveArgument; // Drive argument STRING_ARGUMENT ProgramNameArgument; // Program name argument STRING_ARGUMENT FsNameArgument; // Target FS name argument STRING_ARGUMENT ConvertZoneArgument; // Convert Zone file name FLAG_ARGUMENT HelpArgument; // Help flag argument FLAG_ARGUMENT VerboseArgument; // Verbose flag argument FLAG_ARGUMENT NoSecurityArgument; // Skip setting of security argument FLAG_ARGUMENT NoChkdskArgument; // Skip chkdsk argument FLAG_ARGUMENT ForceDismountArgument; // Force a dismount argument #ifdef DBLSPACE_ENABLED FLAG_ARGUMENT UncompressArgument; // Uncompress flag argument FLAG_ARGUMENT CompressArgument; // Compress flag argument LONG_ARGUMENT UncompressNumberArgument;// Sequence number argument #endif // DBLSPACE_ENABLED PWSTRING InvalidArg; // Invalid argument catcher // // Initialize all the argument parsing machinery. // if( !ArgArray.Initialize( 7, 1 ) || !LexArray.Initialize( 7, 1 ) || !ArgLex.Initialize( &LexArray ) || !DriveArgument.Initialize( "*" ) || !HelpArgument.Initialize( "/?" ) || !VerboseArgument.Initialize( "/V" ) || !NoSecurityArgument.Initialize( "/NoSecurity" ) || !NoChkdskArgument.Initialize( "/NoChkdsk" ) || !ForceDismountArgument.Initialize( "/X" ) || #ifdef DBLSPACE_ENABLED !CompressArgument.Initialize( "/C" ) || #endif // DBLSPACE_ENABLED !ProgramNameArgument.Initialize( "*" ) || #ifdef DBLSPACE_ENABLED !UncompressArgument.Initialize( "/UNCOMPRESS" ) || !UncompressNumberArgument.Initialize( "/UNCOMPRESS:*" ) || #endif // DBLSPACE_ENABLED !FsNameArgument.Initialize( "/FS:*" ) || !ConvertZoneArgument.Initialize( "/CVTAREA:*" ) ) { if ( Interactive ) { DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE ); } return FALSE; } // // The conversion utility is case-insensitive // ArgLex.SetCaseSensitive( FALSE ); if( !ArgArray.Put( &ProgramNameArgument ) || !ArgArray.Put( &HelpArgument ) || !ArgArray.Put( &DriveArgument ) || !ArgArray.Put( &VerboseArgument ) || !ArgArray.Put( &NoSecurityArgument ) || !ArgArray.Put( &NoChkdskArgument ) || !ArgArray.Put( &ForceDismountArgument ) || #ifdef DBLSPACE_ENABLED !ArgArray.Put( &CompressArgument ) || #endif // DBLSPACE_ENABLED #ifdef DBLSPACE_ENABLED !ArgArray.Put( &UncompressArgument ) || !ArgArray.Put( &UncompressNumberArgument ) || #endif // DBLSPACE_ENABLED !ArgArray.Put( &FsNameArgument ) || !ArgArray.Put( &ConvertZoneArgument ) ) { if ( Interactive ) { DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE ); } return FALSE; } // // Lexemize the command line. // if ( !ArgLex.PrepareToParse( (PWSTRING)CommandLine ) ) { if ( Interactive ) { DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE ); } return FALSE; } // // Parse the arguments. // if( !ArgLex.DoParsing( &ArgArray ) ) { if ( Interactive ) { DisplayMessage( MSG_CONV_INVALID_PARAMETER, ERROR_MESSAGE, "%W", InvalidArg = ArgLex.QueryInvalidArgument() ); DELETE( InvalidArg ); } return FALSE; } _Help = HelpArgument.QueryFlag(); _Verbose = VerboseArgument.QueryFlag(); _NoSecurity = NoSecurityArgument.QueryFlag(); _NoChkdsk = NoChkdskArgument.QueryFlag(); _ForceDismount = ForceDismountArgument.QueryFlag(); _Restart = FALSE; // obsolete argument #ifdef DBLSPACE_ENABLED _Compress = CompressArgument.QueryFlag(); #endif // DBLSPACE_ENABLED if ( DriveArgument.IsValueSet() ) { if ( !_DosDrive.Initialize( DriveArgument.GetString() ) ) { return FALSE; } } else { if ( !_DosDrive.Initialize( L"" ) ) { return FALSE; } } if ( FsNameArgument.IsValueSet() ) { if ( !_FsName.Initialize( FsNameArgument.GetString() ) ) { return FALSE; } } else { if ( !_FsName.Initialize( L"" ) ) { return FALSE; } } if( ConvertZoneArgument.IsValueSet() ) { if( !_CvtZoneFileName.Initialize( ConvertZoneArgument.GetString() ) ) { return FALSE; } } else { _CvtZoneFileName.Initialize( L"" ); } #ifdef DBLSPACE_ENABLED _SequenceNumber = 0; _Uncompress = FALSE; if (UncompressArgument.IsValueSet()) { _Uncompress = TRUE; } if (UncompressNumberArgument.IsValueSet()) { _SequenceNumber = (UCHAR)UncompressNumberArgument.QueryLong(); _Uncompress = TRUE; } #endif // DBLSPACE_ENABLED return TRUE; } BOOLEAN CONVERT::Schedule ( ) /*++ Routine Description: Schedules AutoConv Arguments: None. Return Value: BOOLEAN - TRUE if AutoConv successfully scheduled. FALSE otherwise --*/ { DSTRING CommandLine; DSTRING Space; DSTRING FileSystem; DSTRING ConvertZoneFlag; DSTRING NoChkdskFlag; DSTRING VerboseFlag; DSTRING NoSecurityFlag; if( !CommandLine.Initialize( (LPWSTR)L"autocheck autoconv " ) || !Space.Initialize( (LPWSTR)L" " ) || !FileSystem.Initialize( (LPWSTR)L"/FS:" ) || !CommandLine.Strcat( &_NtDrive ) || !CommandLine.Strcat( &Space ) || !CommandLine.Strcat( &FileSystem ) || !CommandLine.Strcat( &_FsName ) ) { return FALSE; } if( _CvtZoneFileName.QueryChCount() && ( !CommandLine.Strcat( &Space ) || !ConvertZoneFlag.Initialize( L"/CVTAREA:" ) || !CommandLine.Strcat( &ConvertZoneFlag ) || !CommandLine.Strcat( &_CvtZoneFileName ) ) ) { return FALSE; } if( _NoChkdsk && ( !CommandLine.Strcat( &Space ) || !NoChkdskFlag.Initialize( L"/NoChkdsk" ) || !CommandLine.Strcat( &NoChkdskFlag ) ) ) { return FALSE; } if( _Verbose && ( !CommandLine.Strcat( &Space ) || !VerboseFlag.Initialize( L"/V" ) || !CommandLine.Strcat( &VerboseFlag ) ) ) { return FALSE; } if( _NoSecurity && ( !CommandLine.Strcat( &Space ) || !NoSecurityFlag.Initialize( L"/NoSecurity" ) || !CommandLine.Strcat( &NoSecurityFlag ) ) ) { return FALSE; } // // There is no need to add options that are only for convert.exe // like /x. // return( AUTOREG::AddEntry( &CommandLine ) ); } BOOLEAN CONVERT::ScheduleAutoConv( ) /*++ Routine Description: Schedules AutoConv to be invoked during boot the next time that the machine reboots. Arguments: None Return Value: BOOLEAN - TRUE if AutoConv successfully scheduled. FALSE otherwise --*/ { BOOLEAN Ok; // // Make sure that Autoconv.exe is in the right place. // if ( !(_Autoconv = FindSystemFile( (LPWSTR)AUTOCONV_PROGRAM_NAME )) ) { DisplayMessage( MSG_CONV_CANNOT_SCHEDULE, ERROR_MESSAGE ); return FALSE; } // Remove any previously scheduled conversion // if ( !RemoveScheduledAutoConv( ) ) { DisplayMessage( MSG_CONV_CANNOT_SCHEDULE, ERROR_MESSAGE ); return FALSE; } // // schedule autoconvert // if ( Ok = Schedule( ) ) { DisplayMessage( MSG_CONV_WILL_CONVERT_ON_REBOOT, NORMAL_MESSAGE, "%W", &_DisplayDrive ); } else { DisplayMessage( MSG_CONV_CANNOT_SCHEDULE, ERROR_MESSAGE ); } return Ok; } BOOLEAN CONVERT::RemoveScheduledAutoConv( ) /*++ Routine Description: Remove possibly old entry of autoconv for the specified volume. Arguments: N/A Return Value: TRUE if no error. --*/ { DSTRING CommandLine; DSTRING NtDrive; if (!CommandLine.Initialize( (LPWSTR)L"autocheck autoconv " )) { return FALSE; } if (!AUTOREG::DeleteEntry(&CommandLine, &_NtDrive)) { return FALSE; } if (_DosDrive.Stricmp(&_GuidDrive) == 0) return FALSE; DebugAssert(_DosDrive.QueryChCount() == 2); if (!IFS_SYSTEM::DosDriveNameToNtDriveName(&_GuidDrive, &NtDrive)) { return FALSE; } if (!AUTOREG::DeleteEntry(&CommandLine, &NtDrive)) { return FALSE; } return TRUE; } #if defined(FE_SB) && defined(_X86_) BOOLEAN CONVERT::ChangeBPB1( IN PCWSTRING NtDrive ) /*++ Routine Description: Change Bpb parameters Logical to Physical Arguments: none Return Value: BOOLEAN - TRUE Change OK FALSE otherwise --*/ { USHORT work,work2; ULONG work3; BIG_INT start_sec; ULONG sector_len; PUCHAR Bpb_Buff; HMEM hmem; _NtDrive.Initialize(NtDrive); start_sec=0; sector_len=1; LOG_IO_DP_DRIVE dpdrive; //*** open if (!dpdrive.Initialize( &_NtDrive, &_Message )) { return FALSE; } if (!hmem.Acquire(2048, dpdrive.QueryAlignmentMask())) { return(FALSE); } Bpb_Buff = (PUCHAR)hmem.GetBuf(); //*** read if(!dpdrive.Read( start_sec, sector_len, Bpb_Buff)){ return FALSE; } work=(USHORT)dpdrive.QueryPhysicalSectorSize(); // get physical sector size //*** change to physical from logical *** _BytePerSec = Bpb_Buff[11]+Bpb_Buff[12]*256; _SecPerClus = Bpb_Buff[13]; _Reserved = Bpb_Buff[14]+Bpb_Buff[15]*256; _SectorNum = Bpb_Buff[19]+Bpb_Buff[20]*256; _SecPerFat = Bpb_Buff[22]+Bpb_Buff[23]*256; _LargeSector = Bpb_Buff[32]+Bpb_Buff[33]*256+Bpb_Buff[34]*256*256+Bpb_Buff[35]*256*256*256; if (work != _BytePerSec){ Bpb_Buff[11] = (UCHAR)(work%256); Bpb_Buff[12] = (UCHAR)(work/256); Bpb_Buff[13]*= (UCHAR)(_BytePerSec/work); work2 = _Reserved*_BytePerSec/work; Bpb_Buff[14] = (UCHAR)(work2%256); Bpb_Buff[15] = (UCHAR)(work2/256); work2 = _SecPerFat*_BytePerSec/work; Bpb_Buff[22] = (UCHAR)(work2%256); Bpb_Buff[23] = (UCHAR)(work2/256); if (_SectorNum*(_BytePerSec/work)>0xffff){ Bpb_Buff[19] = 0; Bpb_Buff[20] = 0; work3 = ((long)_SectorNum*(long)(_BytePerSec/work)); Bpb_Buff[32] = (UCHAR)(work3%256L); Bpb_Buff[35] = (UCHAR)(work3/(256L*256L*256L)); Bpb_Buff[34] = (UCHAR)(work3/(256L*256L)-(ULONG)Bpb_Buff[31]*256L); Bpb_Buff[33] = (UCHAR)(work3/256L-(ULONG)Bpb_Buff[31]*256L*256L-(ULONG)Bpb_Buff[30]*256L); } else { work2 = _SectorNum*(_BytePerSec/work); Bpb_Buff[19] = (UCHAR)(work2%256); Bpb_Buff[20] = (UCHAR)(work2/256); work3 = _LargeSector; Bpb_Buff[32] = (UCHAR)(work3%256L); Bpb_Buff[35] = (UCHAR)(work3/(256L*256L*256L)); Bpb_Buff[34] = (UCHAR)(work3/(256L*256L)-(ULONG)Bpb_Buff[31]*256L); Bpb_Buff[33] = (UCHAR)(work3/256L-(ULONG)Bpb_Buff[31]*256L*256L-(ULONG)Bpb_Buff[30]*256L); } start_sec=0; sector_len=1; //*** write if (!dpdrive.Write(start_sec,sector_len,Bpb_Buff)){ //*** close return FALSE; } } else { //*** close return FALSE; } return TRUE; } #endif