windows-nt/Source/XPSP1/NT/base/fs/utils/chkntfs/chkntfs.cxx

706 lines
20 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
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);
}
}