NT4/private/utils/fdisk/commit.c
2020-09-30 17:12:29 +02:00

1525 lines
41 KiB
C

/*++
Copyright (c) 1993-1994 Microsoft Corporation
Module Name:
commit.c
Abstract:
This module contains the set of routines that support the commitment
of changes to disk without rebooting.
Author:
Bob Rinne (bobri) 11/15/93
Environment:
User process.
Notes:
Revision History:
--*/
#include "fdisk.h"
#include "shellapi.h"
#include <winbase.h>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include "scsi.h"
#include <ntddcdrm.h>
#include <ntddscsi.h>
// Lock list chain head for deleted partitions.
PDRIVE_LOCKLIST DriveLockListHead = NULL;
// Commit flag for case where a partition is deleted that has not drive letter
extern BOOLEAN CommitDueToDelete;
extern BOOLEAN CommitDueToMirror;
extern BOOLEAN CommitDueToExtended;
extern ULONG UpdateMbrOnDisk;
extern HWND InitDlg;
// List head for new drive letter assignment on commit.
typedef struct _ASSIGN_LIST {
struct _ASSIGN_LIST *Next;
ULONG DiskNumber;
BOOLEAN MoveLetter;
UCHAR OriginalLetter;
UCHAR DriveLetter;
} ASSIGN_LIST, *PASSIGN_LIST;
PASSIGN_LIST AssignDriveLetterListHead = NULL;
VOID
CommitToAssignLetterList(
IN PREGION_DESCRIPTOR RegionDescriptor,
IN BOOL MoveLetter
)
/*++
Routine Description:
Remember this region for assigning a drive letter to it upon
commit.
Arguments:
RegionDescriptor - the region to watch
MoveLetter - indicate that the region letter is already
assigned to a different partition, therefore
it must be "moved".
Return Value:
None
--*/
{
PASSIGN_LIST newListEntry;
PPERSISTENT_REGION_DATA regionData;
newListEntry = (PASSIGN_LIST) Malloc(sizeof(ASSIGN_LIST));
if (newListEntry) {
// Save this region
regionData = PERSISTENT_DATA(RegionDescriptor);
newListEntry->OriginalLetter =
newListEntry->DriveLetter = regionData->DriveLetter;
newListEntry->DiskNumber = RegionDescriptor->Disk;
newListEntry->MoveLetter = MoveLetter;
// place it at the front of the chain.
newListEntry->Next = AssignDriveLetterListHead;
AssignDriveLetterListHead = newListEntry;
}
}
VOID
CommitAssignLetterList(
VOID
)
/*++
Routine Description:
Walk the assign drive letter list and make all drive letter assignments
expected. The regions data structures are moved around, so no pointer
can be maintained to look at them. To determine the partition number
for a new partition in this list, the Disks[] structure must be searched
to find a match on the partition for the drive letter. Then the partition
number will be known.
Arguments:
None
Return Value:
None
--*/
{
PREGION_DESCRIPTOR regionDescriptor;
PPERSISTENT_REGION_DATA regionData;
PDISKSTATE diskp;
PASSIGN_LIST assignList,
prevEntry;
TCHAR newName[4];
WCHAR targetPath[100];
LONG partitionNumber;
ULONG index;
assignList = AssignDriveLetterListHead;
while (assignList) {
if ((assignList->DriveLetter != NO_DRIVE_LETTER_YET) && (assignList->DriveLetter != NO_DRIVE_LETTER_EVER)) {
diskp = Disks[assignList->DiskNumber];
partitionNumber = 0;
for (index = 0; index < diskp->RegionCount; index++) {
regionDescriptor = &diskp->RegionArray[index];
if (DmSignificantRegion(regionDescriptor)) {
// If the region has a drive letter, use the drive letter
// to get the info via the Windows API. Otherwise we'll
// have to use the NT API.
regionData = PERSISTENT_DATA(regionDescriptor);
if (regionData) {
if (regionData->DriveLetter == assignList->DriveLetter) {
partitionNumber = regionDescriptor->Reserved->Partition->PartitionNumber;
regionDescriptor->PartitionNumber = partitionNumber;
break;
}
}
}
}
if (partitionNumber) {
HANDLE handle;
ULONG status;
// set up the new NT path.
wsprintf((LPTSTR) targetPath,
"%s\\Partition%d",
GetDiskName(assignList->DiskNumber),
partitionNumber);
// Set up the DOS name.
newName[1] = (TCHAR)':';
newName[2] = 0;
if (assignList->MoveLetter) {
// The letter must be removed before it
// can be assigned.
newName[0] = (TCHAR)assignList->OriginalLetter;
NetworkRemoveShare((LPCTSTR) newName);
DefineDosDevice(DDD_REMOVE_DEFINITION, (LPCTSTR) newName, (LPCTSTR) NULL);
newName[0] = (TCHAR)assignList->DriveLetter;
} else {
newName[0] = (TCHAR)assignList->DriveLetter;
}
// Assign the name - don't worry about errors for now.
DefineDosDevice(DDD_RAW_TARGET_PATH, (LPCTSTR) newName, (LPCTSTR) targetPath);
NetworkShare((LPCTSTR) newName);
// Some of the file systems do not actually dismount
// when requested. Instead, they set a verification
// bit in the device object. Due to dynamic partitioning
// this bit may get cleared by the process of the
// repartitioning and the file system will then
// assume it is still mounted on a new access.
// To get around this problem, new drive letters
// are always locked and dismounted on creation.
status = LowOpenDriveLetter(assignList->DriveLetter,
&handle);
if (NT_SUCCESS(status)) {
// Lock the drive to insure that no other access is occurring
// to the volume.
status = LowLockDrive(handle);
if (NT_SUCCESS(status)) {
LowUnlockDrive(handle);
}
LowCloseDisk(handle);
}
} else {
ErrorDialog(MSG_INTERNAL_LETTER_ASSIGN_ERROR);
}
}
prevEntry = assignList;
assignList = assignList->Next;
Free(prevEntry);
}
AssignDriveLetterListHead = NULL;
}
LONG
CommitInternalLockDriveLetter(
IN PDRIVE_LOCKLIST LockListEntry
)
/*++
Routine Description:
Support routine to perform the locking of a drive letter based on
the locklist entry given.
Arguments:
LockListEntry - The information about what to lock.
Return Values:
zero - success
non-zero failure
--*/
{
ULONG status;
// Lock the disk and save the handle.
status = LowOpenDriveLetter(LockListEntry->DriveLetter,
&LockListEntry->LockHandle);
if (!NT_SUCCESS(status)) {
return 1;
}
// Lock the drive to insure that no other access is occurring
// to the volume.
status = LowLockDrive(LockListEntry->LockHandle);
if (!NT_SUCCESS(status)) {
LowCloseDisk(LockListEntry->LockHandle);
return 1;
}
LockListEntry->CurrentlyLocked = TRUE;
return 0;
}
LONG
CommitToLockList(
IN PREGION_DESCRIPTOR RegionDescriptor,
IN BOOL RemoveDriveLetter,
IN BOOL LockNow,
IN BOOL FailOk
)
/*++
Routine Description:
This routine adds the given drive into the lock list for processing
when a commit occurs. If the LockNow flag is set it indicates that
the drive letter is to be immediately locked if it is to go in the
lock letter list. If this locking fails an error is returned.
Arguments:
RegionDescriptor - the region for the drive to lock.
RemoveDriveLetter - remove the letter when performing the unlock.
LockNow - If the letter is inserted in the list - lock it now.
FailOk - It is ok to fail the lock - used for disabled FT sets.
Return Values:
non-zero - failure to add to list.
--*/
{
PPERSISTENT_REGION_DATA regionData = PERSISTENT_DATA(RegionDescriptor);
PDRIVE_LOCKLIST lockListEntry;
UCHAR driveLetter;
ULONG diskNumber;
if (!regionData) {
// without region data there is no need to be on the lock list.
return 0;
}
// See if this drive letter is already in the lock list.
driveLetter = regionData->DriveLetter;
if ((driveLetter == NO_DRIVE_LETTER_YET) || (driveLetter == NO_DRIVE_LETTER_EVER)) {
// There is no drive letter to lock.
CommitDueToDelete = RemoveDriveLetter;
return 0;
}
if (!regionData->VolumeExists) {
PASSIGN_LIST assignList,
prevEntry;
// This item has never been created so no need to put it in the
// lock list. But it does need to be removed from the assign
// letter list.
prevEntry = NULL;
assignList = AssignDriveLetterListHead;
while (assignList) {
// If a match is found remove it from the list.
if (assignList->DriveLetter == driveLetter) {
if (prevEntry) {
prevEntry->Next = assignList->Next;
} else {
AssignDriveLetterListHead = assignList->Next;
}
Free(assignList);
assignList = NULL;
} else {
prevEntry = assignList;
assignList = assignList->Next;
}
}
return 0;
}
diskNumber = RegionDescriptor->Disk;
lockListEntry = DriveLockListHead;
while (lockListEntry) {
if (lockListEntry->DriveLetter == driveLetter) {
// Already in the list -- update when to lock and unlock
if (diskNumber < lockListEntry->LockOnDiskNumber) {
lockListEntry->LockOnDiskNumber = diskNumber;
}
if (diskNumber > lockListEntry->UnlockOnDiskNumber) {
lockListEntry->UnlockOnDiskNumber = diskNumber;
}
// Already in the lock list and information for locking set up.
// Check to see if this should be a LockNow request.
if (LockNow) {
if (!lockListEntry->CurrentlyLocked) {
// Need to perform the lock.
if (CommitInternalLockDriveLetter(lockListEntry)) {
// Leave the element in the list
return 1;
}
}
}
return 0;
}
lockListEntry = lockListEntry->Next;
}
lockListEntry = (PDRIVE_LOCKLIST) Malloc(sizeof(DRIVE_LOCKLIST));
if (!lockListEntry) {
return 1;
}
// set up the lock list entry.
lockListEntry->LockHandle = NULL;
lockListEntry->PartitionNumber = RegionDescriptor->PartitionNumber;
lockListEntry->DriveLetter = driveLetter;
lockListEntry->RemoveOnUnlock = RemoveDriveLetter;
lockListEntry->CurrentlyLocked = FALSE;
lockListEntry->FailOk = FailOk;
lockListEntry->DiskNumber = lockListEntry->UnlockOnDiskNumber =
lockListEntry->LockOnDiskNumber = diskNumber;
if (LockNow) {
if (CommitInternalLockDriveLetter(lockListEntry)) {
// Do not add this to the list.
Free(lockListEntry);
return 1;
}
}
// place it at the front of the chain.
lockListEntry->Next = DriveLockListHead;
DriveLockListHead = lockListEntry;
return 0;
}
LONG
CommitLockVolumes(
IN ULONG Disk
)
/*++
Routine Description:
This routine will go through any drive letters inserted in the lock list
for the given disk number and attempt to lock the volumes. Currently,
this routine locks all of the drives letters in the lock list when
called the first time (i.e. when Disk == 0).
Arguments:
Disk - the index into the disk table.
Return Values:
non-zero - failure to lock the items in the list.
--*/
{
PDRIVE_LOCKLIST lockListEntry;
if (Disk) {
return 0;
}
for (lockListEntry = DriveLockListHead; lockListEntry; lockListEntry = lockListEntry->Next) {
// Lock the disk. Return on any failure if that is the
// requested action for the entry. It is the responsibility
// of the caller to release any successful locks.
if (!lockListEntry->CurrentlyLocked) {
if (CommitInternalLockDriveLetter(lockListEntry)) {
if (!lockListEntry->FailOk) {
return 1;
}
}
}
}
return 0;
}
LONG
CommitUnlockVolumes(
IN ULONG Disk,
IN BOOLEAN FreeList
)
/*++
Routine Description:
Go through and unlock any locked volumes in the locked list for the
given disk. Currently this routine waits until the last disk has
been processed, then unlocks all disks.
Arguments:
Disk - the index into the disk table.
FreeList - Clean up the list as unlocks are performed or don't
Return Values:
non-zero - failure to lock the items in the list.
--*/
{
PDRIVE_LOCKLIST lockListEntry,
previousLockListEntry;
TCHAR name[4];
if (Disk != GetDiskCount()) {
return 0;
}
lockListEntry = DriveLockListHead;
if (FreeList) {
DriveLockListHead = NULL;
}
while (lockListEntry) {
// Unlock the disk.
if (lockListEntry->CurrentlyLocked) {
if (FreeList && lockListEntry->RemoveOnUnlock) {
// set up the new dos name and NT path.
name[0] = (TCHAR)lockListEntry->DriveLetter;
name[1] = (TCHAR)':';
name[2] = 0;
NetworkRemoveShare((LPCTSTR) name);
if (!DefineDosDevice(DDD_REMOVE_DEFINITION, (LPCTSTR) name, (LPCTSTR) NULL)) {
// could not remove name!!?
}
}
LowUnlockDrive(lockListEntry->LockHandle);
LowCloseDisk(lockListEntry->LockHandle);
}
// Move to the next entry. If requested free this entry.
previousLockListEntry = lockListEntry;
lockListEntry = lockListEntry->Next;
if (FreeList) {
Free(previousLockListEntry);
}
}
return 0;
}
LETTER_ASSIGNMENT_RESULT
CommitDriveLetter(
IN PREGION_DESCRIPTOR RegionDescriptor,
IN CHAR OldDrive,
IN CHAR NewDrive
)
/*++
Routine Description:
This routine will update the drive letter information in the registry and
(if the update works) it will attempt to move the current drive letter
to the new one via DefineDosDevice()
Arguments:
RegionDescriptor - the region that should get the letter.
NewDrive - the new drive letter for the volume.
Return Value:
0 - the assignment failed.
1 - if the assigning of the letter occurred interactively.
2 - must reboot to do the letter.
--*/
{
PPERSISTENT_REGION_DATA regionData;
PDRIVE_LOCKLIST lockListEntry;
PASSIGN_LIST assignList;
HANDLE handle;
TCHAR newName[4];
WCHAR targetPath[100];
int doIt;
STATUS_CODE status = ERROR_SEVERITY_ERROR;
LETTER_ASSIGNMENT_RESULT result = Failure;
regionData = PERSISTENT_DATA(RegionDescriptor);
// check the assign letter list for a match.
// If the letter is there, then just update the list
// otherwise continue on with the action.
assignList = AssignDriveLetterListHead;
while (assignList) {
if (assignList->DriveLetter == (UCHAR)OldDrive) {
assignList->DriveLetter = (UCHAR)NewDrive;
return Complete;
}
assignList = assignList->Next;
}
// Search to see if the drive is currently locked.
for (lockListEntry = DriveLockListHead;
lockListEntry;
lockListEntry = lockListEntry->Next) {
if ((lockListEntry->DiskNumber == RegionDescriptor->Disk) &&
(lockListEntry->PartitionNumber == RegionDescriptor->PartitionNumber)) {
if (lockListEntry->CurrentlyLocked) {
status = 0;
}
// found the match no need to continue searching.
break;
}
}
if (!NT_SUCCESS(status)) {
// See if the drive can be locked.
status = LowOpenPartition(GetDiskName(RegionDescriptor->Disk),
RegionDescriptor->PartitionNumber,
&handle);
if (!NT_SUCCESS(status)) {
return Failure;
}
// Lock the drive to insure that no other access is occurring
// to the volume.
status = LowLockDrive(handle);
if (!NT_SUCCESS(status)) {
if (IsPagefileOnDrive(OldDrive)) {
ErrorDialog(MSG_CANNOT_LOCK_PAGEFILE);
} else {
ErrorDialog(MSG_CANNOT_LOCK_TRY_AGAIN);
}
doIt = ConfirmationDialog(MSG_SCHEDULE_REBOOT, MB_ICONQUESTION | MB_YESNO);
LowCloseDisk(handle);
if (doIt == IDYES) {
RegistryChanged = TRUE;
RestartRequired = TRUE;
return MustReboot;
}
return Failure;
}
} else {
// This drive was found in the lock list and is already
// in the locked state. It is safe to continue with
// the drive letter assignment.
}
doIt = ConfirmationDialog(MSG_DRIVE_RENAME_WARNING, MB_ICONQUESTION | MB_YESNOCANCEL);
if (doIt != IDYES) {
LowUnlockDrive(handle);
LowCloseDisk(handle);
return Failure;
}
// Update the registry first. This way if something goes wrong
// the new letter will arrive on reboot.
if (!DiskRegistryAssignDriveLetter(Disks[RegionDescriptor->Disk]->Signature,
FdGetExactOffset(RegionDescriptor),
FdGetExactSize(RegionDescriptor, FALSE),
(UCHAR)((NewDrive == NO_DRIVE_LETTER_EVER) ? (UCHAR)' ' : (UCHAR)NewDrive))) {
// Registry update failed.
return Failure;
}
// It is safe to change the drive letter. First, remove the
// existing letter.
newName[0] = (TCHAR)OldDrive;
newName[1] = (TCHAR)':';
newName[2] = 0;
NetworkRemoveShare((LPCTSTR) newName);
if (!DefineDosDevice(DDD_REMOVE_DEFINITION, (LPCTSTR) newName, (LPCTSTR) NULL)) {
LowUnlockDrive(handle);
LowCloseDisk(handle);
RegistryChanged = TRUE;
return Failure;
}
if (NewDrive != NO_DRIVE_LETTER_EVER) {
// set up the new dos name and NT path.
newName[0] = (TCHAR)NewDrive;
newName[1] = (TCHAR)':';
newName[2] = 0;
wsprintf((LPTSTR) targetPath,
"%s\\Partition%d",
GetDiskName(RegionDescriptor->Disk),
RegionDescriptor->PartitionNumber);
if (DefineDosDevice(DDD_RAW_TARGET_PATH, (LPCTSTR) newName, (LPCTSTR) targetPath)) {
result = Complete;
} else {
RegistryChanged = TRUE;
}
NetworkShare((LPCTSTR) newName);
} else {
result = Complete;
}
// Force the file system to dismount
LowUnlockDrive(handle);
LowCloseDisk(handle);
return result;
}
VOID
CommitUpdateRegionStructures(
VOID
)
/*++
Routine Description:
This routine is called ONLY after a successful commit of a new partitioning
scheme for the system. Its is responsible for walking through the
region arrays for each of the disks and updating the regions to indicate
their transition from being "desired" to being actually committed
to disk
Arguments:
None
Return Values:
None
--*/
{
PDISKSTATE diskState;
PREGION_DESCRIPTOR regionDescriptor;
PPERSISTENT_REGION_DATA regionData;
ULONG regionNumber,
diskNumber;
// search through all disks in the system.
for (diskNumber = 0, diskState = Disks[0]; diskNumber < DiskCount; diskState = Disks[++diskNumber]) {
// Look at every region array entry and update the values
// to indicate that this region now exists.
for (regionNumber = 0; regionNumber < diskState->RegionCount; regionNumber++) {
regionDescriptor = &diskState->RegionArray[regionNumber];
if (regionDescriptor->Reserved) {
if (regionDescriptor->Reserved->Partition) {
regionDescriptor->Reserved->Partition->CommitMirrorBreakNeeded = FALSE;
}
}
regionData = PERSISTENT_DATA(regionDescriptor);
if ((regionData) && (!regionData->VolumeExists)) {
// By definition and assumption of this routine,
// this region has just been committed to disk.
regionData->VolumeExists = TRUE;
if (regionData->TypeName) {
Free(regionData->TypeName);
}
regionData->TypeName = Malloc((lstrlenW(wszUnformatted)+1)*sizeof(WCHAR));
lstrcpyW(regionData->TypeName, wszUnformatted);
}
}
}
}
VOID
CommitAllChanges(
IN PVOID Param
)
/*++
Routine Description:
This routine will go through all of the region descriptors and commit
any changes that have occurred to disk. Then it "re-initializes"
Disk Administrator and start the display/work process over again.
Arguments:
Param - undefined for now
Return Value:
None
--*/
{
DWORD action,
errorCode;
ULONG diskCount,
temp;
BOOL profileWritten,
changesMade,
mustReboot,
configureFt;
SetCursor(hcurWait);
diskCount = GetDiskCount();
// Determine whether any disks have been changed, and whether
// the system must be rebooted. The system must be rebooted
// if the registry has changed, if any non-removable disk has
// changed, or if any removable disk that was not originally
// unpartitioned has changed.
changesMade = configureFt = FALSE;
mustReboot = RestartRequired;
for (temp=0; temp<diskCount; temp++) {
if (HavePartitionsBeenChanged(temp)) {
changesMade = TRUE;
break;
}
}
profileWritten = FALSE;
// Determine if the commit can be done without a reboot.
// If FT is in the system then it must be notified to
// reconfigure if a reboot is not performed. If it is
// not in the system, but the new disk information requires
// it, then a reboot must be forced.
if (FtInstalled()) {
configureFt = TRUE;
}
if (NewConfigurationRequiresFt()) {
if (!configureFt) {
// The FT driver is not loaded currently.
mustReboot = TRUE;
} else {
// If the system is going to be rebooted, don't
// have FT reconfigure prior to shutdown.
if (mustReboot) {
configureFt = FALSE;
}
}
}
if (RegistryChanged | changesMade | RestartRequired) {
if (RestartRequired) {
action = IDYES;
} else {
action = ConfirmationDialog(MSG_CONFIRM_EXIT, MB_ICONQUESTION | MB_YESNOCANCEL);
}
if (action == IDYES) {
errorCode = CommitLockVolumes(0);
if (errorCode) {
// could not lock all volumes
SetCursor(hcurNormal);
ErrorDialog(MSG_CANNOT_LOCK_FOR_COMMIT);
CommitUnlockVolumes(diskCount, FALSE);
return;
}
if (mustReboot) {
SetCursor(hcurNormal);
if (RestartRequired) {
action = IDYES;
} else {
action = ConfirmationDialog(MSG_REQUIRE_REBOOT, MB_ICONQUESTION | MB_YESNO);
}
if (action != IDYES) {
CommitUnlockVolumes(diskCount, FALSE);
return;
}
}
SetCursor(hcurWait);
errorCode = CommitChanges();
CommitUnlockVolumes(diskCount, TRUE);
SetCursor(hcurNormal);
if (errorCode != NO_ERROR) {
ErrorDialog(MSG_BAD_CONFIG_SET);
PostQuitMessage(0);
} else {
ULONG OldBootPartitionNumber,
NewBootPartitionNumber;
CHAR OldNumberString[8],
NewNumberString[8];
DWORD MsgCode;
// Update the configuration registry
errorCode = SaveFt();
// Check if FTDISK drive should reconfigure.
if (configureFt) {
// Issue device control to ftdisk driver to reconfigure.
FtConfigure();
}
// Register autochk to fix up file systems
// in newly extended volume sets, if necessary
if (RegisterFileSystemExtend()) {
mustReboot = TRUE;
}
// Determine if the FT driver must be enabled.
if (DiskRegistryRequiresFt() == TRUE) {
if (!FtInstalled()) {
mustReboot = TRUE;
}
DiskRegistryEnableFt();
} else {
DiskRegistryDisableFt();
}
if (errorCode == NO_ERROR) {
InfoDialog(MSG_OK_COMMIT);
} else {
ErrorDialog(MSG_BAD_CONFIG_SET);
}
// Has the partition number of the boot
// partition changed?
if (BootPartitionNumberChanged( &OldBootPartitionNumber,&NewBootPartitionNumber)) {
#if i386
MsgCode = MSG_BOOT_PARTITION_CHANGED_X86;
#else
MsgCode = MSG_BOOT_PARTITION_CHANGED_ARC;
#endif
sprintf(OldNumberString, "%d", OldBootPartitionNumber);
sprintf(NewNumberString, "%d", NewBootPartitionNumber);
InfoDialog(MsgCode, OldNumberString, NewNumberString);
}
ClearCommittedDiskInformation();
if (UpdateMbrOnDisk) {
UpdateMasterBootCode(UpdateMbrOnDisk);
UpdateMbrOnDisk = 0;
}
// Reboot if necessary.
if (mustReboot) {
SetCursor(hcurWait);
Sleep(5000);
SetCursor(hcurNormal);
FdShutdownTheSystem();
profileWritten = TRUE;
}
CommitAssignLetterList();
CommitUpdateRegionStructures();
RegistryChanged = FALSE;
CommitDueToDelete = CommitDueToMirror = FALSE;
TotalRedrawAndRepaint();
AdjustMenuAndStatus();
}
} else if (action == IDCANCEL) {
return; // don't exit
} else {
FDASSERT(action == IDNO);
}
}
}
VOID
FtConfigure(
VOID
)
/*++
Routine Description:
This routine calls the FTDISK driver to ask it to reconfigure as changes
have been made in the registry.
Arguments:
None
Return Value:
None
--*/
{
OBJECT_ATTRIBUTES objectAttributes;
STRING ntFtName;
IO_STATUS_BLOCK statusBlock;
UNICODE_STRING unicodeDeviceName;
NTSTATUS status;
HANDLE handle;
// Open ft control object.
RtlInitString(&ntFtName,
"\\Device\\FtControl");
RtlAnsiStringToUnicodeString(&unicodeDeviceName,
&ntFtName,
TRUE);
InitializeObjectAttributes(&objectAttributes,
&unicodeDeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = DmOpenFile(&handle,
SYNCHRONIZE | FILE_ANY_ACCESS,
&objectAttributes,
&statusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_ALERT );
RtlFreeUnicodeString(&unicodeDeviceName);
if (!NT_SUCCESS(status)) {
return;
}
// Issue device control to reconfigure FT.
NtDeviceIoControlFile(handle,
NULL,
NULL,
NULL,
&statusBlock,
FT_CONFIGURE,
NULL,
0L,
NULL,
0L);
DmClose(handle);
return;
}
BOOL
CommitAllowed(
VOID
)
/*++
Routine Description:
Determine if it is ok to perform a commit.
Arguments:
None
Return Value:
TRUE if it is ok to commit and there is something to commit
FALSE otherwise
--*/
{
if (DriveLockListHead ||
AssignDriveLetterListHead ||
CommitDueToDelete ||
CommitDueToMirror ||
CommitDueToExtended) {
return TRUE;
}
return FALSE;
}
VOID
RescanDevices(
VOID
)
/*++
Routine Description:
This routine performs all actions necessary to dynamically rescan
device buses (i.e. SCSI) and get the appropriate driver support loaded.
Arguments:
None
Return Value:
None
--*/
{
PSCSI_ADAPTER_BUS_INFO adapterInfo;
PSCSI_BUS_DATA busData;
PSCSI_INQUIRY_DATA inquiryData;
TCHAR physicalName[32];
TCHAR driveName[32];
BYTE driveBuffer[32];
BYTE physicalBuffer[32];
HANDLE volumeHandle;
STRING string;
UNICODE_STRING unicodeString;
UNICODE_STRING physicalString;
OBJECT_ATTRIBUTES objectAttributes;
NTSTATUS ntStatus;
IO_STATUS_BLOCK statusBlock;
BOOLEAN diskFound,
cdromFound;
ULONG bytesTransferred,
i,
j,
deviceNumber,
currentPort,
numberOfPorts,
percentComplete,
portNumber;
diskFound = FALSE;
cdromFound = FALSE;
// Determine how many buses there are
portNumber = numberOfPorts = percentComplete = 0;
while (TRUE) {
memset(driveBuffer, 0, sizeof(driveBuffer));
sprintf(driveBuffer, "\\\\.\\Scsi%d:", portNumber);
// Open the SCSI port with the DOS name.
volumeHandle = CreateFile(driveBuffer,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
0);
if (volumeHandle == INVALID_HANDLE_VALUE) {
break;
}
CloseHandle(volumeHandle);
numberOfPorts++;
portNumber++;
}
currentPort = 1;
portNumber = 0;
// Perform the scsi bus rescan.
while (TRUE) {
memset(driveBuffer, 0, sizeof(driveBuffer));
sprintf(driveBuffer, "\\\\.\\Scsi%d:", portNumber);
// Open the SCSI port with the DOS name.
volumeHandle = CreateFile(driveBuffer,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
0);
if (volumeHandle == INVALID_HANDLE_VALUE) {
break;
}
// Issue rescan device control.
if (!DeviceIoControl(volumeHandle,
IOCTL_SCSI_RESCAN_BUS,
NULL,
0,
NULL,
0,
&bytesTransferred,
NULL)) {
CloseHandle(volumeHandle);
break;
}
percentComplete = (currentPort * 100) / numberOfPorts;
if (percentComplete < 100) {
PostMessage(InitDlg,
WM_USER,
percentComplete,
0);
}
currentPort++;
// Get a big chuck of memory to store the SCSI bus data.
adapterInfo = malloc(0x4000);
if (adapterInfo == NULL) {
CloseHandle(volumeHandle);
goto finish;
}
// Issue device control to get configuration information.
if (!DeviceIoControl(volumeHandle,
IOCTL_SCSI_GET_INQUIRY_DATA,
NULL,
0,
adapterInfo,
0x4000,
&bytesTransferred,
NULL)) {
CloseHandle(volumeHandle);
goto finish;
}
for (i = 0; i < adapterInfo->NumberOfBuses; i++) {
busData = &adapterInfo->BusData[i];
inquiryData =
(PSCSI_INQUIRY_DATA)((PUCHAR)adapterInfo + busData->InquiryDataOffset);
for (j = 0; j < busData->NumberOfLogicalUnits; j++) {
// Check if device is claimed.
if (!inquiryData->DeviceClaimed) {
// Determine the perpherial type.
switch (inquiryData->InquiryData[0] & 0x1f) {
case DIRECT_ACCESS_DEVICE:
diskFound = TRUE;
break;
case READ_ONLY_DIRECT_ACCESS_DEVICE:
cdromFound = TRUE;
break;
case OPTICAL_DEVICE:
diskFound = TRUE;
break;
}
}
// Get next device data.
inquiryData =
(PSCSI_INQUIRY_DATA)((PUCHAR)adapterInfo + inquiryData->NextInquiryDataOffset);
}
}
free(adapterInfo);
CloseHandle(volumeHandle);
portNumber++;
}
if (diskFound) {
// Send IOCTL_DISK_FIND_NEW_DEVICES commands to each existing disk.
deviceNumber = 0;
while (TRUE) {
memset(driveBuffer, 0, sizeof(driveBuffer));
sprintf(driveBuffer, "\\Device\\Harddisk%d\\Partition0", deviceNumber);
RtlInitString(&string, driveBuffer);
ntStatus = RtlAnsiStringToUnicodeString(&unicodeString,
&string,
TRUE);
if (!NT_SUCCESS(ntStatus)) {
break;
}
InitializeObjectAttributes(&objectAttributes,
&unicodeString,
0,
NULL,
NULL);
ntStatus = DmOpenFile(&volumeHandle,
FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE,
&objectAttributes,
&statusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_ALERT);
if (!NT_SUCCESS(ntStatus)) {
RtlFreeUnicodeString(&unicodeString);
break;
}
// Issue find device device control.
if (!DeviceIoControl(volumeHandle,
IOCTL_DISK_FIND_NEW_DEVICES,
NULL,
0,
NULL,
0,
&bytesTransferred,
NULL)) {
}
DmClose(volumeHandle);
// see if the physicaldrive# symbolic link is present
sprintf(physicalBuffer, "\\DosDevices\\PhysicalDrive%d", deviceNumber);
deviceNumber++;
RtlInitString(&string, physicalBuffer);
ntStatus = RtlAnsiStringToUnicodeString(&physicalString,
&string,
TRUE);
if (!NT_SUCCESS(ntStatus)) {
continue;
}
InitializeObjectAttributes(&objectAttributes,
&physicalString,
0,
NULL,
NULL);
ntStatus = DmOpenFile(&volumeHandle,
FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE,
&objectAttributes,
&statusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_ALERT);
if (!NT_SUCCESS(ntStatus)) {
ULONG index;
ULONG dest;
// Name is not there - create it. This copying
// is done in case this code should ever become
// unicode and the types for the two strings would
// actually be different.
//
// Copy only the portion of the physical name
// that is in the \dosdevices\ directory
for (dest = 0, index = 12; TRUE; index++, dest++) {
physicalName[dest] = (TCHAR)physicalBuffer[index];
if (!physicalName[dest]) {
break;
}
}
// Copy all of the NT namespace name.
for (index = 0; TRUE; index++) {
driveName[index] = (TCHAR) driveBuffer[index];
if (!driveName[index]) {
break;
}
}
DefineDosDevice(DDD_RAW_TARGET_PATH,
(LPCTSTR) physicalName,
(LPCTSTR) driveName);
} else {
DmClose(volumeHandle);
}
// free allocated memory for unicode string.
RtlFreeUnicodeString(&unicodeString);
RtlFreeUnicodeString(&physicalString);
}
}
if (cdromFound) {
// Send IOCTL_CDROM_FIND_NEW_DEVICES commands to each existing cdrom.
deviceNumber = 0;
while (TRUE) {
memset(driveBuffer, 0, sizeof(driveBuffer));
sprintf(driveBuffer, "\\Device\\Cdrom%d", deviceNumber);
RtlInitString(&string, driveBuffer);
ntStatus = RtlAnsiStringToUnicodeString(&unicodeString,
&string,
TRUE);
if (!NT_SUCCESS(ntStatus)) {
break;
}
InitializeObjectAttributes(&objectAttributes,
&unicodeString,
0,
NULL,
NULL);
ntStatus = DmOpenFile(&volumeHandle,
FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE,
&objectAttributes,
&statusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_ALERT);
if (!NT_SUCCESS(ntStatus)) {
break;
}
// Issue find device device control.
if (!DeviceIoControl(volumeHandle,
IOCTL_CDROM_FIND_NEW_DEVICES,
NULL,
0,
NULL,
0,
&bytesTransferred,
NULL)) {
}
CloseHandle(volumeHandle);
deviceNumber++;
}
}
finish:
PostMessage(InitDlg,
WM_USER,
100,
0);
return;
}