mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
439 lines
9.9 KiB
439 lines
9.9 KiB
/*++
|
|
|
|
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: [...] -- just display dirty bit state
|
|
chkntfs /d -- restore default autochk behavior
|
|
chkntfs /x drive: [...] -- exclude drives from default autochk
|
|
chkntfs /c drive: [...] -- schedule autochk to run on drives
|
|
|
|
|
|
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"
|
|
|
|
|
|
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;
|
|
|
|
|
|
PROGRAM::Initialize();
|
|
|
|
ExitStatus = 2;
|
|
|
|
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_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_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;
|
|
}
|
|
|
|
ExitStatus = 0;
|
|
|
|
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();
|
|
|
|
if (_restore_default + _exclude + _schedule_check > 1) {
|
|
|
|
DisplayMessage(MSG_CHKNTFS_ARGS_CONFLICT);
|
|
ExitStatus = 2;
|
|
return FALSE;
|
|
}
|
|
|
|
if (0 == _drive_arguments.QueryPathCount() && !_restore_default) {
|
|
|
|
DisplayMessage(MSG_CHKNTFS_REQUIRES_DRIVE);
|
|
ExitStatus = 2;
|
|
return FALSE;
|
|
}
|
|
|
|
if (_restore_default && _drive_arguments.QueryPathCount() > 0) {
|
|
|
|
DisplayMessage(MSG_CHKNTFS_INVALID_FORMAT);
|
|
ExitStatus = 2;
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
PARRAY drive_array;
|
|
PARRAY_ITERATOR iterator;
|
|
PPATH current_drive;
|
|
PCWSTRING drive_string;
|
|
DSTRING nt_drive_name;
|
|
DSTRING fs_name;
|
|
BOOLEAN is_dirty = 0;
|
|
DSTRING cmd_line;
|
|
ULONG old_error_mode;
|
|
DRIVE_TYPE drive_type;
|
|
|
|
nt_drive_name.Initialize();
|
|
|
|
if (_restore_default) {
|
|
|
|
// Remove previous commands.
|
|
|
|
if (!cmd_line.Initialize("autocheck autochk /k:") ||
|
|
!AUTOREG::DeleteEntry(&cmd_line, TRUE) ||
|
|
!cmd_line.Initialize("autocheck autochk *") ||
|
|
!AUTOREG::DeleteEntry(&cmd_line)) {
|
|
|
|
return FALSE;
|
|
}
|
|
if (!cmd_line.Initialize("autocheck autochk *") ||
|
|
!AUTOREG::AddEntry(&cmd_line)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
drive_array = _drive_arguments.GetPathArray();
|
|
iterator = (PARRAY_ITERATOR)drive_array->QueryIterator();
|
|
|
|
//
|
|
// Run through the arguments here and make sure they're all
|
|
// valid drive names.
|
|
//
|
|
|
|
while (NULL != (current_drive = (PPATH)iterator->GetNext())) {
|
|
|
|
drive_string = current_drive->GetPathString();
|
|
|
|
if (!current_drive->IsDrive()) {
|
|
|
|
DisplayMessage(MSG_CHKNTFS_BAD_ARG, NORMAL_MESSAGE,
|
|
"%W", drive_string);
|
|
return FALSE;
|
|
}
|
|
|
|
old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
drive_type = SYSTEM::QueryDriveType(drive_string);
|
|
|
|
SetErrorMode(old_error_mode);
|
|
|
|
switch (drive_type) {
|
|
case UnknownDrive:
|
|
DisplayMessage(MSG_CHKNTFS_NONEXISTENT_DRIVE, NORMAL_MESSAGE,
|
|
"%W", drive_string);
|
|
return FALSE;
|
|
|
|
case RemoteDrive:
|
|
DisplayMessage(MSG_CHKNTFS_NO_NETWORK, NORMAL_MESSAGE,
|
|
"%W", drive_string);
|
|
return FALSE;
|
|
|
|
case CdRomDrive:
|
|
DisplayMessage(MSG_CHKNTFS_NO_CDROM, NORMAL_MESSAGE,
|
|
"%W", drive_string);
|
|
return FALSE;
|
|
|
|
case RamDiskDrive:
|
|
DisplayMessage(MSG_CHKNTFS_NO_RAMDISK, NORMAL_MESSAGE,
|
|
"%W", drive_string);
|
|
return FALSE;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
iterator->Reset();
|
|
|
|
if (_exclude) {
|
|
|
|
// Remove the previous registry commands, if any.
|
|
|
|
if (!cmd_line.Initialize("autocheck autochk *") ||
|
|
!AUTOREG::DeleteEntry(&cmd_line, TRUE)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!cmd_line.Initialize("autocheck autochk /k:") ||
|
|
!AUTOREG::DeleteEntry(&cmd_line, TRUE)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Collect a list of drives to be excluded and add them to the
|
|
// command line
|
|
//
|
|
|
|
while (NULL != (current_drive = (PPATH)iterator->GetNext())) {
|
|
|
|
drive_string = current_drive->GetPathString();
|
|
|
|
if (!cmd_line.Strcat(drive_string->QueryString(0, 1))) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
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 != (current_drive = (PPATH)iterator->GetNext())) {
|
|
|
|
drive_string = current_drive->GetPathString();
|
|
|
|
if (!IFS_SYSTEM::DosDriveNameToNtDriveName(drive_string,
|
|
&nt_drive_name)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Schedule check: Put a line in the registry like
|
|
// "autocheck autochck \??\X:" for each command-line argument.
|
|
//
|
|
|
|
if (_schedule_check) {
|
|
|
|
|
|
DSTRING cmd_line;
|
|
|
|
if (!cmd_line.Initialize("autocheck autochk /m ") ||
|
|
!cmd_line.Strcat(&nt_drive_name) ||
|
|
!AUTOREG::AddEntry(&cmd_line)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
ExitStatus = 0;
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Default: check to see if the volume is dirty.
|
|
//
|
|
|
|
if (IFS_SYSTEM::QueryFileSystemName(&nt_drive_name, &fs_name, NULL)) {
|
|
|
|
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", drive_string);
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (is_dirty) {
|
|
|
|
DisplayMessage(MSG_CHKNTFS_DIRTY, NORMAL_MESSAGE,
|
|
"%W", drive_string);
|
|
|
|
ExitStatus = 1;
|
|
|
|
} else {
|
|
|
|
DisplayMessage(MSG_CHKNTFS_CLEAN, NORMAL_MESSAGE,
|
|
"%W", drive_string);
|
|
ExitStatus = ExitStatus > 0 ? ExitStatus : 0;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID _CRTAPI1
|
|
main()
|
|
{
|
|
DEFINE_CLASS_DESCRIPTOR(CHKNTFS);
|
|
int r;
|
|
|
|
{
|
|
CHKNTFS ChkNtfs;
|
|
|
|
if (!ChkNtfs.Initialize() ||
|
|
!ChkNtfs.CheckNtfs()) {
|
|
|
|
exit(2);
|
|
}
|
|
|
|
r = ChkNtfs.ExitStatus;
|
|
|
|
}
|
|
|
|
_exit(r);
|
|
}
|