Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

870 lines
23 KiB

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
autochk.cxx
Abstract:
This is the main program for the autocheck version of chkdsk.
Author:
Norbert P. Kusters (norbertk) 31-May-91
--*/
#include "ulib.hxx"
#include "wstring.hxx"
#include "fatvol.hxx"
#include "untfs.hxx"
#include "ntfsvol.hxx"
#include "spackmsg.hxx"
#include "error.hxx"
#include "ifssys.hxx"
#include "rtmsg.h"
#include "rcache.hxx"
#include "autoreg.hxx"
BOOLEAN
RegistrySosOption(
);
extern "C" BOOLEAN
InitializeUfat(
PVOID DllHandle,
ULONG Reason,
PCONTEXT Context
);
extern "C" BOOLEAN
InitializeUntfs(
PVOID DllHandle,
ULONG Reason,
PCONTEXT Context
);
extern "C" BOOLEAN
InitializeIfsUtil(
PVOID DllHandle,
ULONG Reason,
PCONTEXT Context
);
BOOLEAN
ExtendNtfsVolume(
PCWSTRING DriveName,
PMESSAGE Message
);
BOOLEAN
DeregisterAutochk(
int argc,
char** argv
);
BOOLEAN
QueryNextHardDrive(
PWSTRING DriveName,
PWSTRING FriendlyDriveName
);
int _CRTAPI1
main(
int argc,
char** argv,
char** envp,
ULONG DebugParameter
)
/*++
Routine Description:
This routine is the main program for autocheck FAT chkdsk.
Arguments:
argc, argv - Supplies the fully qualified NT path name of the
the drive to check.
Return Value:
0 - Success.
1 - Failure.
--*/
{
if (!InitializeUlib( NULL, ! DLL_PROCESS_DETACH, NULL ) ||
!InitializeIfsUtil(NULL,0,NULL) ||
!InitializeUfat(NULL,0,NULL) ||
!InitializeUntfs(NULL,0,NULL)) {
return 1;
}
//
// The declarations must come after these initialization functions.
//
DSTRING dos_drive_name;
DSTRING drive_name;
DSTRING skip_list;
PFAT_VOL fatvol = NULL;
PNTFS_VOL ntfsvol = NULL;
AUTOCHECK_MESSAGE *msg;
PVOL_LIODPDRV vol;
DSTRING fsname;
DSTRING fatname;
DSTRING ntfsname;
BOOLEAN onlyifdirty = TRUE;
BOOLEAN recover = FALSE;
BOOLEAN extend = FALSE;
BOOLEAN remove_registry = FALSE;
PREAD_CACHE read_cache;
DSTRING boot_execute_log_file_name;
FSTRING boot_ex_temp;
HMEM logged_message_mem;
ULONG packed_log_length;
ULONG ArgOffset = 1;
BOOLEAN SetupOutput = FALSE;
BOOLEAN SetupSpecialFixLevel = FALSE;
ULONG exit_status = 0;
BOOLEAN SuppressOutput = TRUE; // dots only by default
DSTRING drive_letter;
BOOLEAN resize_logfile = FALSE;
BOOLEAN all_drives = FALSE;
LONG logfile_size = 0;
if (!drive_letter.Initialize() ||
!skip_list.Initialize() ||
!drive_name.Initialize()) {
return 1;
}
// Parse the arguments--the accepted arguments are:
//
// autochk [/s] [/dx:] [/p] [/r] nt-drive-name
// autochk [/dx:] [/p] [/r] [/m] [/l:size] nt-drive-name
// autochk [/s] /x dos-drive-name
// autochk [/k:drives] *
//
// /s - setup: no output
// /d - the drive letter is x:
// /p - check even if not dirty
// /r - recover; implies /p
// /l - resize log file to <size> kilobytes. May not be combined with
// /s because /s explicitly inhibits logfile resizing.
// /x - extend volume
// /k - a list of drive letters to skip
// /m - remove registry entry after running
//
if (argc < 2) {
// Not enough arguments.
return 1;
}
for (ArgOffset = 1; ArgOffset < (ULONG)argc; ++ArgOffset) {
if ((argv[ArgOffset][0] == '/' || argv[ArgOffset][0] == '-') &&
(argv[ArgOffset][1] == 's' || argv[ArgOffset][1] == 'S') &&
(argv[ArgOffset][2] == 0) ) {
//
// Then we're in silent mode
//
SetupOutput = TRUE;
continue;
}
if ((argv[ArgOffset][0] == '/' || argv[ArgOffset][0] == '-') &&
(argv[ArgOffset][1] == 'p' || argv[ArgOffset][1] == 'P') &&
(argv[ArgOffset][2] == 0) ) {
// argv[ArgOffset] is the /p parameter, so argv[ArgOffset+1]
// must be the drive.
onlyifdirty = FALSE;
continue;
}
if( (argv[ArgOffset][0] == '/' || argv[ArgOffset][0] == '-') &&
(argv[ArgOffset][1] == 'r' || argv[ArgOffset][1] == 'R') &&
(argv[ArgOffset][2] == 0) ) {
// Note that /r implies /p.
//
recover = TRUE;
onlyifdirty = FALSE;
continue;
}
if( (argv[ArgOffset][0] == '/' || argv[ArgOffset][0] == '-') &&
(argv[ArgOffset][1] == 'x' || argv[ArgOffset][1] == 'X') &&
(argv[ArgOffset][2] == 0) ) {
// when the /x parameter is specified, we accept a
// DOS name and do a complete check.
//
onlyifdirty = FALSE;
extend = TRUE;
if( !dos_drive_name.Initialize( argv[ArgOffset + 1] ) ||
!IFS_SYSTEM::DosDriveNameToNtDriveName( &dos_drive_name,
&drive_name ) ) {
return 1;
}
ArgOffset++;
continue;
}
if ((argv[ArgOffset][0] == '/' || argv[ArgOffset][0] == '-') &&
(argv[ArgOffset][1] == 'd' || argv[ArgOffset][1] == 'D')) {
//
// A parameter of the form "/dX:" indicates that we are checking
// the volume whose drive letter is X:.
//
if (!drive_letter.Initialize(&argv[ArgOffset][2])) {
return 1;
}
continue;
}
if ((argv[ArgOffset][0] == '/' || argv[ArgOffset][0] == '-') &&
(argv[ArgOffset][1] == 'l' || argv[ArgOffset][1] == 'L')) {
DSTRING number;
// The /l parameter indicates that we're to resize the log file.
// The size should always be specified, and it is in kilobytes.
//
resize_logfile = TRUE;
if (!number.Initialize(&argv[ArgOffset][3]) ||
!number.QueryNumber(&logfile_size) ||
logfile_size < 0) {
return 1;
}
logfile_size *= 1024;
continue;
}
if ((argv[ArgOffset][0] == '/' || argv[ArgOffset][0] == '-') &&
(argv[ArgOffset][1] == 'k' || argv[ArgOffset][1] == 'K')) {
// Skip.
if (!skip_list.Initialize(&argv[ArgOffset][3])) {
return 1;
}
continue;
}
if ((argv[ArgOffset][0] == '/' || argv[ArgOffset][0] == '-') &&
(argv[ArgOffset][1] == 'm' || argv[ArgOffset][1] == 'M')) {
remove_registry = TRUE;
continue;
}
if ((argv[ArgOffset][0] != '/' && argv[ArgOffset][0] != '-')) {
// We've run off the options into the arguments.
break;
}
}
// argv[ArgOffset] is the drive;
if (NULL != argv[ArgOffset]) {
if ('*' == argv[ArgOffset][0]) {
all_drives = TRUE;
if (!drive_name.Initialize("")) {
return 1;
}
} else {
all_drives = FALSE;
if (!drive_name.Initialize(argv[ArgOffset])) {
return 1;
}
}
}
if (!fatname.Initialize("FAT") ||
!ntfsname.Initialize("NTFS")) {
return 1;
}
//
// Determine whether to suppress output or not. If compiled with
// DBG==1, print normal output. Otherwise look in the registry to
// see if the machine has "SOS" in the NTLOADOPTIONS.
//
#if defined _AUTOCHECK_DBG_
SuppressOutput = FALSE;
#else /* _AUTOCHECK_DBG */
if (RegistrySosOption()) {
SuppressOutput = FALSE;
}
#endif /* _AUTOCHECK_DBG_ */
//
// If this is autochk /r or /l, we've been started from an explicit
// registry entry and the dirty bit may not be set. We want to
// deliver interesting output regardless.
//
if (recover || resize_logfile) {
SuppressOutput = FALSE;
}
if (SetupOutput) {
msg = NEW SP_AUTOCHECK_MESSAGE;
} else {
msg = NEW AUTOCHECK_MESSAGE;
}
if (NULL == msg || !msg->Initialize(SuppressOutput)) {
return 1;
}
for (;;) {
if (all_drives && !QueryNextHardDrive(&drive_name, &drive_letter)) {
break;
}
if (skip_list.QueryChCount() > 0 &&
INVALID_CHNUM != skip_list.Strchr(*drive_letter.GetWSTR())) {
// Skip this one.
continue;
}
if (!IFS_SYSTEM::QueryFileSystemName(&drive_name, &fsname)) {
msg->Set( MSG_FS_NOT_DETERMINED );
msg->Display( "%W", &drive_name );
return 1;
}
if (recover) {
msg->Set(MSG_BLANK_LINE);
msg->Display();
msg->SetLoggingEnabled();
} else {
if (all_drives) {
msg->Set(MSG_CHK_RUNNING);
msg->Display("%W", &drive_letter);
}
msg->Set(MSG_FILE_SYSTEM_TYPE);
msg->Display("%W", &fsname);
}
if (fsname == fatname) {
if (!(fatvol = NEW FAT_VOL) ||
!fatvol->Initialize(&drive_name, msg, TRUE)) {
return 1;
}
if ((read_cache = NEW READ_CACHE) &&
read_cache->Initialize(fatvol, 75)) {
fatvol->SetCache(read_cache);
} else {
DELETE(read_cache);
}
vol = fatvol;
} else if (fsname == ntfsname) {
if( extend ) {
// NOTE: this roundabout method is necessary to
// convince NTFS to allow us to access the new
// sectors on the volume.
//
if( !ExtendNtfsVolume( &drive_name, msg ) ) {
return 1;
}
if( !(ntfsvol = NEW NTFS_VOL) ||
!ntfsvol->Initialize( &drive_name, msg ) ) {
return 1;
}
if( !ntfsvol->Lock() ) {
msg->Set( MSG_CANT_LOCK_THE_DRIVE );
msg->Display( "" );
}
} else {
if (!(ntfsvol = NEW NTFS_VOL) ||
!ntfsvol->Initialize(&drive_name, msg, TRUE)) {
return 1;
}
if (SetupOutput) {
//
// SetupSpecialFixLevel will be used for NTFS... it means
// to refrain from resizing the log file.
//
SetupSpecialFixLevel = TRUE;
}
}
// The read cache for NTFS CHKDSK gets set in VerifyAndFix.
vol = ntfsvol;
} else {
msg->Set( MSG_FS_NOT_SUPPORTED );
msg->Display( "%s%W", "AUTOCHK", &fsname );
return 1;
}
// If the /r, /l, or /m switch was supplied, remove the forcing
// entry from the registry before calling Chkdsk, since
// Chkdsk may reboot the system if we are checking the
// boot partition.
//
if (recover || resize_logfile || remove_registry) {
DeregisterAutochk( argc, argv );
}
// Invoke chkdsk. Note that if the /r parameter is supplied,
// we recover both free and allocated space, but if the /x
// parameter is supplied, we only recover free space.
//
if (!vol->ChkDsk(
SetupSpecialFixLevel ? SetupSpecial : TotalFix,
msg,
FALSE,
onlyifdirty,
recover || extend,
recover,
resize_logfile,
(ULONG)logfile_size,
&exit_status,
0 == drive_letter.QueryChCount() ? NULL : &drive_letter)) {
if (!all_drives) {
if (SetupSpecialFixLevel) {
return exit_status;
} else {
return 1;
}
}
}
DELETE( vol );
// Dump the message retained by the message object into a file.
//
if( msg->IsLoggingEnabled() &&
boot_execute_log_file_name.Initialize( &drive_name ) &&
boot_ex_temp.Initialize( L"\\BOOTEX.LOG" ) &&
boot_execute_log_file_name.Strcat( &boot_ex_temp ) &&
logged_message_mem.Initialize() &&
msg->QueryPackedLog( &logged_message_mem, &packed_log_length ) ) {
IFS_SYSTEM::WriteToFile( &boot_execute_log_file_name,
logged_message_mem.GetBuf(),
packed_log_length,
TRUE );
}
//
// If we were checking only a single drive, we're done. Break out
// of this loop and go on to the cleanup code.
//
if (!all_drives) {
break;
}
}
// If the /x switch was supplied, remove the
// forcing entry from the registry, since Chkdsk
// has completed successfully.
//
if (extend) {
DeregisterAutochk( argc, argv );
}
if (SetupSpecialFixLevel) {
return exit_status;
} else {
return 0;
}
}
#define CONTROL_NAME \
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control"
#define VALUE_NAME L"SystemStartOptions"
#define VALUE_BUFFER_SIZE \
(sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 256 * sizeof(WCHAR))
BOOLEAN
RegistrySosOption(
)
/*++
Routine Description:
This function examines the registry to determine whether the
user's NTLOADOPTIONS boot environment variable contains the string
"SOS" or not.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control:SystemStartOptions
Arguments:
None.
Return Value:
TRUE if "SOS" was set. Otherwise FALSE.
--*/
{
NTSTATUS st;
UNICODE_STRING uKeyName, uValueName;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE hKey;
WCHAR ValueBuf[VALUE_BUFFER_SIZE];
PKEY_VALUE_PARTIAL_INFORMATION pKeyValueInfo =
(PKEY_VALUE_PARTIAL_INFORMATION)ValueBuf;
ULONG ValueLength;
RtlInitUnicodeString(&uKeyName, CONTROL_NAME);
InitializeObjectAttributes(&ObjectAttributes, &uKeyName,
OBJ_CASE_INSENSITIVE, NULL, NULL);
st = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes);
if (!NT_SUCCESS(st)) {
DebugPrintf("AUTOCHK: can't open control key: 0x%x\n", st);
return FALSE;
}
RtlInitUnicodeString(&uValueName, VALUE_NAME);
st = NtQueryValueKey(hKey, &uValueName, KeyValuePartialInformation,
(PVOID)pKeyValueInfo, VALUE_BUFFER_SIZE, &ValueLength);
ASSERT(ValueLength < VALUE_BUFFER_SIZE);
NtClose(hKey);
if (!NT_SUCCESS(st)) {
DebugPrintf("AUTOCHK: can't query value key: 0x%x\n", st);
return FALSE;
}
// uValue.Buffer = (PVOID)&pKeyValueInfo->Data;
// uValue.Length = uValue.MaximumLength = (USHORT)pKeyValueInfo->DataLength;
if (NULL != wcsstr((PWCHAR)&pKeyValueInfo->Data, L"SOS") ||
NULL != wcsstr((PWCHAR)&pKeyValueInfo->Data, L"sos")) {
return TRUE;
}
return FALSE;
}
BOOLEAN
ExtendNtfsVolume(
PCWSTRING DriveName,
PMESSAGE Message
)
/*++
Routine Description:
This function changes the count of sectors in sector
zero to agree with the drive object. This is useful
when extending volume sets. Note that it requires that
we be able to lock the volume, and that it should only
be called if we know that the drive in question in an
NTFS volume. This function also copies the boot sector
to the end of the partition, where it's kept as a backup.
Arguments:
DriveName -- Supplies the name of the volume.
Message -- Supplies an output channel for messages.
Return Value:
TRUE upon completion.
--*/
{
LOG_IO_DP_DRIVE Drive;
SECRUN Secrun;
HMEM Mem;
PPACKED_BOOT_SECTOR BootSector;
if( !Drive.Initialize( DriveName, Message ) ||
!Drive.Lock() ||
!Mem.Initialize() ||
!Secrun.Initialize( &Mem, &Drive, 0, 1 ) ||
!Secrun.Read() ) {
return FALSE;
}
BootSector = (PPACKED_BOOT_SECTOR)Secrun.GetBuf();
//
// We leave an extra sector at the end of the volume to contain
// the new replica boot sector.
//
BootSector->NumberSectors.LowPart = Drive.QuerySectors().GetLowPart() - 1;
BootSector->NumberSectors.HighPart = Drive.QuerySectors().GetHighPart();
if (!Secrun.Write()) {
return FALSE;
}
Secrun.Relocate( Drive.QuerySectors() - 2 );
if (!Secrun.Write()) {
DebugPrintf("Error: %x\n", Drive.QueryLastNtStatus());
return FALSE;
}
return TRUE;
}
BOOLEAN
DeregisterAutochk(
int argc,
char** argv
)
/*++
Routine Description:
This function removes the registry entry which triggered
autochk. It is only called if the /x or /r entry is present.
Arguments:
argc -- Supplies the number of arguments given to autochk.
argv -- supplies the arguments given to autochk.
Return Value:
TRUE upon successful completion.
--*/
{
DSTRING CommandLineString1,
CommandLineString2,
CurrentArgString,
OneSpace;
int i;
// Reconstruct the command line and remove it from
// the registry. First, reconstruct the primary
// string, which is "autochk arg1 arg2...".
//
if( !CommandLineString1.Initialize( "autochk" ) ||
!OneSpace.Initialize( " " ) ) {
return FALSE;
}
for( i = 1; i < argc; i++ ) {
if( !CurrentArgString.Initialize(argv[i] ) ||
!CommandLineString1.Strcat( &OneSpace ) ||
!CommandLineString1.Strcat( &CurrentArgString ) ) {
return FALSE;
}
}
// Now construct the secondary string, which is
// "autocheck autochk arg1 arg2..."
//
if( !CommandLineString2.Initialize( "autocheck " ) ||
!CommandLineString2.Strcat( &CommandLineString1 ) ) {
return FALSE;
}
return( AUTOREG::DeleteEntry( &CommandLineString1 ) &&
AUTOREG::DeleteEntry( &CommandLineString2 ) );
}
BOOLEAN
QueryNextHardDrive(
PWSTRING DriveName,
PWSTRING FriendlyDriveName
)
{
static BOOLEAN first_time = TRUE;
static HANDLE dos_devices_object_dir;
static ULONG context = 0;
WCHAR link_target_buffer[MAXIMUM_FILENAME_LENGTH];
POBJECT_DIRECTORY_INFORMATION
dir_info;
OBJECT_ATTRIBUTES object_attributes;
CHAR dir_info_buffer[256];
BOOLEAN restart_scan;
ULONG length;
HANDLE handle;
NTSTATUS status;
UNICODE_STRING link_target;
UNICODE_STRING link_type_name;
UNICODE_STRING link_target_prefix;
UNICODE_STRING u;
link_target.Buffer = link_target_buffer;
dir_info = (POBJECT_DIRECTORY_INFORMATION)dir_info_buffer;
RtlInitUnicodeString(&link_type_name, L"SymbolicLink");
RtlInitUnicodeString(&link_target_prefix, L"\\Device\\Harddisk");
if (first_time) {
first_time = FALSE;
restart_scan = TRUE;
RtlInitUnicodeString(&u, L"\\??");
InitializeObjectAttributes(&object_attributes, &u,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = NtOpenDirectoryObject(&dos_devices_object_dir,
DIRECTORY_ALL_ACCESS,
&object_attributes);
if (!NT_SUCCESS(status)) {
DebugPrintf("AUTOCHK: Unable to open %wZ directory - Status == %lx\n",
&u, status);
return FALSE;
}
} else {
restart_scan = FALSE;
}
for (;;) {
status = NtQueryDirectoryObject(dos_devices_object_dir,
(PVOID)dir_info,
sizeof(dir_info_buffer),
TRUE,
restart_scan,
&context,
&length);
restart_scan = FALSE;
if (!NT_SUCCESS(status)) {
return FALSE;
}
if (status == STATUS_NO_MORE_ENTRIES) {
return FALSE;
}
if (RtlEqualUnicodeString(&dir_info->TypeName, &link_type_name, TRUE) &&
dir_info->Name.Buffer[(dir_info->Name.Length>>1)-1] == L':') {
InitializeObjectAttributes(&object_attributes,
&dir_info->Name,
OBJ_CASE_INSENSITIVE,
dos_devices_object_dir,
NULL);
status = NtOpenSymbolicLinkObject(&handle,
SYMBOLIC_LINK_ALL_ACCESS,
&object_attributes);
if (!NT_SUCCESS(status)) {
return FALSE;
}
link_target.Length = 0;
link_target.MaximumLength = sizeof(link_target_buffer);
status = NtQuerySymbolicLinkObject(handle,
&link_target,
NULL);
NtClose(handle);
if (NT_SUCCESS(status) &&
RtlPrefixUnicodeString(&link_target_prefix, &link_target, TRUE )) {
if (!FriendlyDriveName->Initialize(dir_info->Name.Buffer,
dir_info->Name.Length / 2)) {
return FALSE;
}
if (!DriveName->Initialize(link_target.Buffer,
link_target.Length / 2)) {
return FALSE;
}
return TRUE;
}
}
}
//NOTREACHED
return FALSE;
}