/*++ Copyright (c) 1996 Microsoft Corporation Module Name: chkntfs.cxx Abstract: This utility allows the users to find the state of the dirty bit on NTFS volumes, to schedule autochk for specific drives, and to mofidy the default autochk action for a drive. SYNTAX: chkntfs drive: [...] -- tells if a drive is dirty or chkdsk has been scheduled chkntfs /d -- restore default autochk behavior chkntfs /x drive: [...] -- exclude drives from default autochk chkntfs /c drive: [...] -- schedule autochk to run on drives chkntfs /e drive: [...] -- enable automatic volume upgrades on drives chkntfs /t[:countdowntime] -- display or set autochk countdown time EXIT: 0 -- OK, dirty bit not set on drive or bit not checked 1 -- OK, and dirty bit set on at least one drive 2 -- Error Author: Matthew Bradburn (MattBr) 19-Aug-1996 --*/ #include "ulib.hxx" #include "arg.hxx" #include "array.hxx" #include "path.hxx" #include "wstring.hxx" #include "ifssys.hxx" #include "system.hxx" #include "arrayit.hxx" #include "autoreg.hxx" #include "chkntfs.hxx" #include "mpmap.hxx" #include "volume.hxx" DEFINE_CONSTRUCTOR(CHKNTFS, PROGRAM); BOOLEAN CHKNTFS::Initialize( ) /*++ Routine Description: Initializes an object of class CHKNTFS. Called once when the program starts. Arguments: None. Return Value: BOOLEAN - Indicates whether the initialization succeeded. --*/ { ARGUMENT_LEXEMIZER arg_lex; ARRAY lex_array; ARRAY argument_array; STRING_ARGUMENT program_name_argument; FLAG_ARGUMENT flag_restore_default; FLAG_ARGUMENT flag_exclude; FLAG_ARGUMENT flag_schedule_check; FLAG_ARGUMENT flag_invalid; FLAG_ARGUMENT flag_display_help; FLAG_ARGUMENT flag_count_down_time; LONG_ARGUMENT arg_count_down_time; ExitStatus = 0; PROGRAM::Initialize(); if (!argument_array.Initialize()) { return FALSE; } if (!program_name_argument.Initialize("*") || !flag_restore_default.Initialize("/D") || !flag_exclude.Initialize("/X") || !flag_schedule_check.Initialize("/C") || !flag_count_down_time.Initialize("/T") || !arg_count_down_time.Initialize("/T:*") || !flag_display_help.Initialize("/?") || !flag_invalid.Initialize("/*") || // close comment */ !_drive_arguments.Initialize("*", FALSE, TRUE)) { return FALSE; } if (!argument_array.Put(&program_name_argument) || !argument_array.Put(&flag_display_help) || !argument_array.Put(&flag_restore_default) || !argument_array.Put(&flag_exclude) || !argument_array.Put(&flag_schedule_check) || !argument_array.Put(&flag_count_down_time) || !argument_array.Put(&arg_count_down_time) || !argument_array.Put(&flag_invalid) || !argument_array.Put(&_drive_arguments)) { return FALSE; } if (!lex_array.Initialize() || !arg_lex.Initialize(&lex_array)) { return FALSE; } arg_lex.PutSwitches("/"); arg_lex.PutStartQuotes("\""); arg_lex.PutEndQuotes("\""); arg_lex.PutSeparators(" \"\t"); arg_lex.SetCaseSensitive(FALSE); if (!arg_lex.PrepareToParse()) { DisplayMessage(MSG_CHKNTFS_INVALID_FORMAT); return FALSE; } if (!arg_lex.DoParsing(&argument_array)) { if (flag_invalid.QueryFlag()) { DisplayMessage(MSG_CHKNTFS_INVALID_SWITCH, NORMAL_MESSAGE, "%W", flag_invalid.GetLexeme()); } else { DisplayMessage(MSG_CHKNTFS_INVALID_FORMAT); } return FALSE; } else if (_drive_arguments.WildCardExpansionFailed()) { DisplayMessage(MSG_CHKNTFS_NO_WILDCARDS); return FALSE; } if (flag_invalid.QueryFlag()) { DisplayMessage(MSG_CHKNTFS_INVALID_SWITCH); return FALSE; } if (flag_display_help.QueryFlag()) { DisplayMessage(MSG_CHKNTFS_USAGE); return FALSE; } _restore_default = flag_restore_default.QueryFlag(); _exclude = flag_exclude.QueryFlag(); _schedule_check = flag_schedule_check.QueryFlag(); _display_count_down_time = flag_count_down_time.IsValueSet(); _set_count_down_time = arg_count_down_time.IsValueSet() ? arg_count_down_time.QueryLong() : -1; _count_down_time = _display_count_down_time || arg_count_down_time.IsValueSet(); if (_restore_default + _exclude + _schedule_check + _count_down_time > 1) { DisplayMessage(MSG_CHKNTFS_ARGS_CONFLICT); return FALSE; } if (0 == _drive_arguments.QueryPathCount() && !(_restore_default || _count_down_time)) { DisplayMessage(MSG_CHKNTFS_REQUIRES_DRIVE); return FALSE; } if ((_restore_default || _count_down_time) && _drive_arguments.QueryPathCount() > 0) { DisplayMessage(MSG_CHKNTFS_INVALID_FORMAT); return FALSE; } if (_count_down_time && arg_count_down_time.IsValueSet() && (arg_count_down_time.QueryLong() > MAX_AUTOCHK_TIMEOUT_VALUE || arg_count_down_time.QueryLong() < 0)) { DisplayMessage(MSG_CHKNTFS_INVALID_AUTOCHK_COUNT_DOWN_TIME, NORMAL_MESSAGE, "%d%d", MAX_AUTOCHK_TIMEOUT_VALUE, AUTOCHK_TIMEOUT); return FALSE; } return TRUE; } BOOLEAN CHKNTFS::CheckNtfs( ) /*++ Routine Description: Look at the arguments specified by the user and do what he wants. Arguments: None. Return Value: BOOLEAN -- success or failure. --*/ { DSTRING volume_name; PATH fullpath; DSTRING drive_path_string; PATH_ANALYZE_CODE rst; MOUNT_POINT_MAP drive_array_map; PMOUNT_POINT_TUPLE mptuple; PARRAY drive_array; PARRAY_ITERATOR iterator; PPATH current_drive; PCWSTRING display_drive_string; PCWSTRING dos_volume_name; PCWSTRING dos_drive_name; DSTRING nt_drive_name; DSTRING ntfs_str; DSTRING fat_str; DSTRING fat32_str; DSTRING fs_name; DSTRING fs_name_and_version; BOOLEAN is_dirty = 0; DSTRING cmd_line; ULONG old_error_mode; DRIVE_TYPE drive_type; DP_DRIVE dpdrive; if (_restore_default) { // Remove all autochk commands and insert the // default entry into the registry. if (!cmd_line.Initialize("autocheck autochk") || !AUTOREG::DeleteEntry(&cmd_line, TRUE)) { return FALSE; } if (!cmd_line.Initialize("autocheck autochk *") || !AUTOREG::AddEntry(&cmd_line)) { return FALSE; } return TRUE; } if (_count_down_time) { ULONG timeout; if (_display_count_down_time) { if (!VOL_LIODPDRV::QueryAutochkTimeOut(&timeout)) { timeout = AUTOCHK_TIMEOUT; } DisplayMessage(MSG_CHKNTFS_AUTOCHK_COUNT_DOWN_TIME, NORMAL_MESSAGE, "%d", timeout); return TRUE; } else if (_set_count_down_time >= 0) { if (!VOL_LIODPDRV::SetAutochkTimeOut(_set_count_down_time)) { DisplayMessage(MSG_CHKNTFS_AUTOCHK_SET_COUNT_DOWN_TIME_FAILED, NORMAL_MESSAGE, "%d", _set_count_down_time); } return TRUE; } DebugAssert(FALSE); return FALSE; } if (!ntfs_str.Initialize("NTFS") || !fat_str.Initialize("FAT") || !fat32_str.Initialize("FAT32")) return FALSE; if (!drive_array_map.Initialize()) { DebugPrint("Unable to initialize drive_array_map.\n"); return FALSE; } drive_array = _drive_arguments.GetPathArray(); iterator = (PARRAY_ITERATOR)drive_array->QueryIterator(); iterator->Reset(); // // Run through the arguments here and translate each of // them into their ultimate volume names, and mount points. // Build an array of tuple consists of the mount point, // volume name, and original drive specification tuple. while (NULL != (current_drive = (PPATH)iterator->GetNext())) { display_drive_string = current_drive->GetPathString(); rst = current_drive->AnalyzePath(&volume_name, &fullpath, &drive_path_string); switch (rst) { case PATH_OK: case PATH_COULD_BE_FLOPPY: if (drive_path_string.QueryChCount() != 0) { DisplayMessage(MSG_CHKNTFS_BAD_ARG, NORMAL_MESSAGE, "%W", display_drive_string); DELETE(iterator); return FALSE; } mptuple = (PMOUNT_POINT_TUPLE)NEW MOUNT_POINT_TUPLE; if (mptuple == NULL || !mptuple->_DeviceName.Initialize(display_drive_string) || !mptuple->_VolumeName.Initialize(&volume_name) || !mptuple->_DriveName.Initialize(fullpath.GetPathString())) { DebugPrint("Out of memory.\n"); DELETE(mptuple); DELETE(iterator); return FALSE; } if (!drive_array_map.Put(mptuple)) { DebugPrint("Unable to put away an object.\n"); DELETE(mptuple); DELETE(iterator); return FALSE; } break; case PATH_OUT_OF_MEMORY: DebugPrint("Out of memory.\n"); DELETE(iterator); return FALSE; case PATH_NO_MOUNT_POINT_FOR_VOLUME_NAME_PATH: DisplayMessage(MSG_CHKNTFS_NO_MOUNT_POINT_FOR_GUID_VOLNAME_PATH, NORMAL_MESSAGE, "%W", display_drive_string); DELETE(iterator); return FALSE; default: DisplayMessage(MSG_CHKNTFS_BAD_ARG, NORMAL_MESSAGE, "%W", display_drive_string); DELETE(iterator); return FALSE; } } DELETE(iterator); iterator = (PARRAY_ITERATOR)drive_array_map.QueryIterator(); iterator->Reset(); // // Run through the tuples here and make sure they're all // valid drive names. // while (NULL != (mptuple = (PMOUNT_POINT_TUPLE)iterator->GetNext())) { display_drive_string = &(mptuple->_DeviceName); dos_volume_name = &(mptuple->_VolumeName); old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS); drive_type = SYSTEM::QueryDriveType(dos_volume_name); SetErrorMode(old_error_mode); switch (drive_type) { case UnknownDrive: DisplayMessage(MSG_CHKNTFS_NONEXISTENT_DRIVE, NORMAL_MESSAGE, "%W", display_drive_string); DELETE(iterator); return FALSE; case RemoteDrive: DisplayMessage(MSG_CHKNTFS_NO_NETWORK, NORMAL_MESSAGE, "%W", display_drive_string); DELETE(iterator); return FALSE; case RamDiskDrive: DisplayMessage(MSG_CHKNTFS_NO_RAMDISK, NORMAL_MESSAGE, "%W", display_drive_string); DELETE(iterator); return FALSE; case RemovableDrive: if (!IFS_SYSTEM::DosDriveNameToNtDriveName(dos_volume_name, &nt_drive_name)) { DELETE(iterator); return FALSE; } old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS); if (!dpdrive.Initialize(&nt_drive_name)) { SetErrorMode(old_error_mode); DisplayMessage(MSG_CHKNTFS_CANNOT_CHECK, NORMAL_MESSAGE, "%W", display_drive_string); DELETE(iterator); return FALSE; } SetErrorMode(old_error_mode); if (dpdrive.IsFloppy()) { DisplayMessage(MSG_CHKNTFS_FLOPPY_DRIVE, NORMAL_MESSAGE, "%W", display_drive_string); DELETE(iterator); return FALSE; } default: break; } } iterator->Reset(); if (_exclude) { DSTRING cmd_line2; DSTRING option; // Remove all previous autochk commands, if any. if (!cmd_line.Initialize("autocheck autochk *") || !AUTOREG::DeleteEntry(&cmd_line, TRUE)) { DELETE(iterator); return FALSE; } if (!cmd_line.Initialize("autocheck autochk /k:") || !AUTOREG::DeleteEntry(&cmd_line, TRUE)) { DELETE(iterator); return FALSE; } if (!cmd_line.Initialize("autocheck autochk") || !option.Initialize(" /k:") || !cmd_line2.Initialize("autocheck autochk")) { DELETE(iterator); return FALSE; } // // Collect a list of drives to be excluded and add them to the // command line // while (NULL != (mptuple = (PMOUNT_POINT_TUPLE)iterator->GetNext())) { display_drive_string = &(mptuple->_DeviceName); dos_volume_name = &(mptuple->_VolumeName); dos_drive_name = &(mptuple->_DriveName); // Warn the user if the filesystem is not NTFS. if (!IFS_SYSTEM::DosDriveNameToNtDriveName(dos_volume_name, &nt_drive_name)) { DELETE(iterator); return FALSE; } if (IFS_SYSTEM::QueryFileSystemName(&nt_drive_name, &fs_name, NULL, &fs_name_and_version)) { DisplayMessage(MSG_FILE_SYSTEM_TYPE, NORMAL_MESSAGE, "%W", &fs_name); } if (!AUTOREG::DeleteEntry(&cmd_line2, &nt_drive_name)) { DELETE(iterator); return FALSE; } if (dos_drive_name->QueryChCount() == 2) { if (!IFS_SYSTEM::DosDriveNameToNtDriveName(dos_drive_name, &nt_drive_name)) { DELETE(iterator); return FALSE; } if (!AUTOREG::DeleteEntry(&cmd_line2, &nt_drive_name)) { DELETE(iterator); return FALSE; } if (!cmd_line.Strcat(&option) || !cmd_line.Strcat(dos_drive_name->QueryString(0, 1))) { DELETE(iterator); return FALSE; } } else { if (!cmd_line.Strcat(&option) || !cmd_line.Strcat(dos_volume_name)) { DELETE(iterator); return FALSE; } } } DELETE(iterator); DSTRING star; if (!star.Initialize(" *") || !cmd_line.Strcat(&star)) { return FALSE; } // Add the new command line. return AUTOREG::AddEntry(&cmd_line); } // // This loop handles the "schedule check" and default actions. // while (NULL != (mptuple = (PMOUNT_POINT_TUPLE)iterator->GetNext())) { display_drive_string = &(mptuple->_DeviceName); dos_volume_name = &(mptuple->_VolumeName); dos_drive_name = &(mptuple->_DriveName); if (!IFS_SYSTEM::DosDriveNameToNtDriveName(dos_volume_name, &nt_drive_name)) { DELETE(iterator); return FALSE; } // // Schedule check: Put a line in the registry like // "autocheck autochk \??\X:" for each command-line argument. // if (_schedule_check) { if (!IFS_SYSTEM::QueryFileSystemName(&nt_drive_name, &fs_name, NULL)) { DisplayMessage(MSG_CHKNTFS_CANNOT_CHECK, NORMAL_MESSAGE, "%W", display_drive_string); DELETE(iterator); return FALSE; } if (fs_name == ntfs_str || fs_name == fat_str || fs_name == fat32_str) { if (!cmd_line.Initialize("autocheck autochk") || !AUTOREG::DeleteEntry(&cmd_line, &nt_drive_name)) { DELETE(iterator); return FALSE; } if (dos_drive_name->QueryChCount() == 2) { if (!IFS_SYSTEM::DosDriveNameToNtDriveName(dos_drive_name, &nt_drive_name)) { DELETE(iterator); return FALSE; } if (!cmd_line.Initialize("autocheck autochk") || !AUTOREG::DeleteEntry(&cmd_line, &nt_drive_name)) { DELETE(iterator); return FALSE; } } if (!cmd_line.Initialize("autocheck autochk /m ") || !cmd_line.Strcat(&nt_drive_name) || !AUTOREG::PushEntry(&cmd_line)) { DELETE(iterator); return FALSE; } } else { DisplayMessage(MSG_CHKNTFS_SKIP_DRIVE_RAW, NORMAL_MESSAGE, "%W", display_drive_string); } continue; } // // Default: check to see if the volume is dirty. // if (IFS_SYSTEM::QueryFileSystemName(&nt_drive_name, &fs_name, NULL, &fs_name_and_version)) { DisplayMessage(MSG_FILE_SYSTEM_TYPE, NORMAL_MESSAGE, "%W", &fs_name); } if (!IFS_SYSTEM::IsVolumeDirty(&nt_drive_name, &is_dirty)) { DisplayMessage(MSG_CHKNTFS_CANNOT_CHECK, NORMAL_MESSAGE, "%W", display_drive_string); DELETE(iterator); return FALSE; } if (is_dirty) { DisplayMessage(MSG_CHKNTFS_DIRTY, NORMAL_MESSAGE, "%W", display_drive_string); ExitStatus = 1; } else { DSTRING cmd_line2; if (!cmd_line.Initialize("autocheck autochk") || !cmd_line2.Initialize(" ") || !cmd_line2.Strcat(&nt_drive_name)) { DELETE(iterator); return FALSE; } if (AUTOREG::IsFrontEndPresent(&cmd_line, &nt_drive_name)) { DisplayMessage(MSG_CHKNTFS_CHKDSK_WILL_RUN, NORMAL_MESSAGE, "%W", display_drive_string); continue; } else if (dos_drive_name->QueryChCount() == 2) { if (!IFS_SYSTEM::DosDriveNameToNtDriveName(dos_drive_name, &nt_drive_name)) { DELETE(iterator); return FALSE; } if (AUTOREG::IsFrontEndPresent(&cmd_line, &nt_drive_name)) { DisplayMessage(MSG_CHKNTFS_CHKDSK_WILL_RUN, NORMAL_MESSAGE, "%W", display_drive_string); continue; } } DisplayMessage(MSG_CHKNTFS_CLEAN, NORMAL_MESSAGE, "%W", display_drive_string); } } DELETE(iterator); return TRUE; } VOID __cdecl main() { DEFINE_CLASS_DESCRIPTOR(CHKNTFS); { CHKNTFS ChkNtfs; if (!ChkNtfs.Initialize() || !ChkNtfs.CheckNtfs()) { exit(2); } exit(ChkNtfs.ExitStatus); } }