2447 lines
95 KiB
C
2447 lines
95 KiB
C
/*++
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
FsCtrl.c
|
|
|
|
Abstract:
|
|
This module implements the File System Control routines for Udfs called
|
|
by the Fsd/Fsp dispatch drivers.
|
|
|
|
Author:
|
|
Dan Lovinger [DanLo] 11-Jun-1996
|
|
--*/
|
|
|
|
#include "UdfProcs.h"
|
|
|
|
// The Bug check file id for this module
|
|
#define BugCheckFileId (UDFS_BUG_CHECK_FSCTRL)
|
|
|
|
// The local debug trace level
|
|
#define Dbg (UDFS_DEBUG_LEVEL_FSCTRL)
|
|
|
|
// Local constants
|
|
BOOLEAN UdfDisable = FALSE;
|
|
|
|
|
|
// Local macros
|
|
|
|
|
|
INLINE VOID UdfStoreFileSetDescriptorIfPrevailing(IN OUT PNSR_FSD *StoredFSD, IN OUT PNSR_FSD *NewFSD)
|
|
{
|
|
PNSR_FSD TempFSD;
|
|
|
|
// If we haven't stored a fileset descriptor or the fileset number
|
|
// of the stored descriptor is less than the new descriptor, swap the pointers around.
|
|
if (*StoredFSD == NULL || (*StoredFSD)->FileSet < (*NewFSD)->FileSet) {
|
|
TempFSD = *StoredFSD;
|
|
*StoredFSD = *NewFSD;
|
|
*NewFSD = TempFSD;
|
|
}
|
|
}
|
|
|
|
|
|
// Local support routines
|
|
|
|
|
|
VOID UdfDetermineVolumeBounding(IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PULONG S, IN PULONG N);
|
|
NTSTATUS UdfDismountVolume(IN PIRP_CONTEXT IrpContext, IN PIRP Irp);
|
|
NTSTATUS UdfFindAnchorVolumeDescriptor(IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN OUT PNSR_ANCHOR *AnchorVolumeDescriptor);
|
|
|
|
NTSTATUS UdfFindFileSetDescriptor(IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb,
|
|
IN PLONGAD LongAd,
|
|
IN OUT PNSR_FSD *FileSetDescriptor);
|
|
NTSTATUS UdfFindVolumeDescriptors(IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb,
|
|
IN PEXTENTAD Extent,
|
|
IN OUT PPCB *Pcb,
|
|
IN OUT PNSR_PVD *PrimaryVolumeDescriptor,
|
|
IN OUT PNSR_LVOL *LogicalVolumeDescriptor);
|
|
|
|
NTSTATUS UdfInvalidateVolumes(IN PIRP_CONTEXT IrpContext, IN PIRP Irp);
|
|
NTSTATUS UdfIsPathnameValid(IN PIRP_CONTEXT IrpContext, IN PIRP Irp);
|
|
BOOLEAN UdfIsRemount(IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, OUT PVCB *OldVcb);
|
|
UdfIsVolumeDirty(IN PIRP_CONTEXT IrpContext, IN PIRP Irp);
|
|
NTSTATUS UdfIsVolumeMounted(IN PIRP_CONTEXT IrpContext, IN PIRP Irp);
|
|
NTSTATUS UdfLockVolume(IN PIRP_CONTEXT IrpContext, IN PIRP Irp);
|
|
NTSTATUS UdfMountVolume(IN PIRP_CONTEXT IrpContext, IN PIRP Irp);
|
|
NTSTATUS UdfOplockRequest(IN PIRP_CONTEXT IrpContext, IN PIRP Irp);
|
|
BOOLEAN UdfRecognizeVolume(IN PIRP_CONTEXT IrpContext, IN PDEVICE_OBJECT DeviceObject, IN ULONG SectorSize, IN OUT PBOOLEAN Bridge);
|
|
VOID UdfScanForDismountedVcb(IN PIRP_CONTEXT IrpContext);
|
|
NTSTATUS UdfUnlockVolume(IN PIRP_CONTEXT IrpContext, IN PIRP Irp);
|
|
VOID UdfUpdateVolumeLabel(
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PWCHAR VolumeLabel,
|
|
IN OUT PUSHORT VolumeLabelLength,
|
|
IN PUCHAR Dstring,
|
|
IN UCHAR FieldLength);
|
|
VOID UdfUpdateVolumeSerialNumber(IN PIRP_CONTEXT IrpContext, IN OUT PULONG VolumeSerialNumber, IN PNSR_FSD Fsd);
|
|
NTSTATUS UdfUserFsctl(IN PIRP_CONTEXT IrpContext, IN PIRP Irp);
|
|
NTSTATUS UdfVerifyVolume(IN PIRP_CONTEXT IrpContext, IN PIRP Irp);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, UdfCommonFsControl)
|
|
#pragma alloc_text(PAGE, UdfDetermineVolumeBounding)
|
|
#pragma alloc_text(PAGE, UdfDismountVolume)
|
|
#pragma alloc_text(PAGE, UdfFindAnchorVolumeDescriptor)
|
|
#pragma alloc_text(PAGE, UdfFindFileSetDescriptor)
|
|
#pragma alloc_text(PAGE, UdfFindVolumeDescriptors)
|
|
#pragma alloc_text(PAGE, UdfIsPathnameValid)
|
|
#pragma alloc_text(PAGE, UdfIsRemount)
|
|
#pragma alloc_text(PAGE, UdfIsVolumeDirty)
|
|
#pragma alloc_text(PAGE, UdfIsVolumeMounted)
|
|
#pragma alloc_text(PAGE, UdfLockVolume)
|
|
#pragma alloc_text(PAGE, UdfLockVolumeInternal)
|
|
#pragma alloc_text(PAGE, UdfMountVolume)
|
|
#pragma alloc_text(PAGE, UdfOplockRequest)
|
|
#pragma alloc_text(PAGE, UdfRecognizeVolume)
|
|
#pragma alloc_text(PAGE, UdfScanForDismountedVcb)
|
|
#pragma alloc_text(PAGE, UdfStoreVolumeDescriptorIfPrevailing)
|
|
#pragma alloc_text(PAGE, UdfUnlockVolume)
|
|
#pragma alloc_text(PAGE, UdfUnlockVolumeInternal)
|
|
#pragma alloc_text(PAGE, UdfUpdateVolumeLabel)
|
|
#pragma alloc_text(PAGE, UdfUpdateVolumeSerialNumber)
|
|
#pragma alloc_text(PAGE, UdfUserFsctl)
|
|
#pragma alloc_text(PAGE, UdfVerifyVolume)
|
|
#endif
|
|
|
|
|
|
VOID UdfStoreVolumeDescriptorIfPrevailing(IN OUT PNSR_VD_GENERIC *StoredVD, IN OUT PNSR_VD_GENERIC NewVD)
|
|
/*++
|
|
Routine Description:
|
|
This routine updates Volume Descriptor if the new descriptor
|
|
is more prevailing than the one currently stored.
|
|
Arguments:
|
|
StoredVD - pointer to a currently stored descriptor
|
|
NewVD - pointer to a candidate descriptor
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
PNSR_VD_GENERIC TempVD;
|
|
|
|
// If we haven't stored a volume descriptor or the sequence number
|
|
// of the stored descriptor is less than the new descriptor, make a copy of it and store it.
|
|
if ((NULL == *StoredVD) || ((*StoredVD)->Sequence < NewVD->Sequence)) {
|
|
if (NULL == *StoredVD) {
|
|
*StoredVD = (PNSR_VD_GENERIC)FsRtlAllocatePoolWithTag(UdfNonPagedPool, sizeof(NSR_VD_GENERIC), TAG_NSR_VDSD);
|
|
}
|
|
|
|
RtlCopyMemory(*StoredVD, NewVD, sizeof(NSR_VD_GENERIC));
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS UdfCommonFsControl(IN PIRP_CONTEXT IrpContext, IN PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This is the common routine for doing FileSystem control operations called by both the fsd and fsp threads
|
|
Arguments:
|
|
Irp - Supplies the Irp to process
|
|
Return Value:
|
|
NTSTATUS - The return status for the operation
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
PAGED_CODE();
|
|
|
|
// Check the input parameters
|
|
ASSERT_IRP_CONTEXT(IrpContext);
|
|
ASSERT_IRP(Irp);
|
|
|
|
// Get a pointer to the current Irp stack location
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
// We know this is a file system control so we'll case on the
|
|
// minor function, and call a internal worker routine to complete the irp.
|
|
switch (IrpSp->MinorFunction) {
|
|
case IRP_MN_MOUNT_VOLUME:
|
|
Status = UdfMountVolume(IrpContext, Irp);
|
|
break;
|
|
case IRP_MN_VERIFY_VOLUME:
|
|
Status = UdfVerifyVolume(IrpContext, Irp);
|
|
break;
|
|
case IRP_MN_USER_FS_REQUEST:
|
|
Status = UdfUserFsctl(IrpContext, Irp);
|
|
break;
|
|
default:
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST);
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
NTSTATUS UdfUserFsctl(IN PIRP_CONTEXT IrpContext, IN PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This is the common routine for implementing the user's requests made through NtFsControlFile.
|
|
Arguments:
|
|
Irp - Supplies the Irp being processed
|
|
Return Value:
|
|
NTSTATUS - The return status for the operation
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
PAGED_CODE();
|
|
|
|
// Case on the control code.
|
|
switch (IrpSp->Parameters.FileSystemControl.FsControlCode) {
|
|
case FSCTL_REQUEST_OPLOCK_LEVEL_1:
|
|
case FSCTL_REQUEST_OPLOCK_LEVEL_2:
|
|
case FSCTL_REQUEST_BATCH_OPLOCK:
|
|
case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
|
|
case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
|
|
case FSCTL_OPLOCK_BREAK_NOTIFY:
|
|
case FSCTL_OPLOCK_BREAK_ACK_NO_2:
|
|
case FSCTL_REQUEST_FILTER_OPLOCK:
|
|
Status = UdfOplockRequest(IrpContext, Irp);
|
|
break;
|
|
case FSCTL_LOCK_VOLUME:
|
|
Status = UdfLockVolume(IrpContext, Irp);
|
|
break;
|
|
case FSCTL_UNLOCK_VOLUME:
|
|
Status = UdfUnlockVolume(IrpContext, Irp);
|
|
break;
|
|
case FSCTL_DISMOUNT_VOLUME:
|
|
Status = UdfDismountVolume(IrpContext, Irp);
|
|
break;
|
|
case FSCTL_IS_VOLUME_DIRTY:
|
|
Status = UdfIsVolumeDirty(IrpContext, Irp);
|
|
break;
|
|
case FSCTL_IS_VOLUME_MOUNTED:
|
|
Status = UdfIsVolumeMounted(IrpContext, Irp);
|
|
break;
|
|
case FSCTL_IS_PATHNAME_VALID:
|
|
Status = UdfIsPathnameValid(IrpContext, Irp);
|
|
break;
|
|
case FSCTL_INVALIDATE_VOLUMES:
|
|
Status = UdfInvalidateVolumes(IrpContext, Irp);
|
|
break;
|
|
|
|
// We don't support any of the known or unknown requests.
|
|
case FSCTL_MARK_VOLUME_DIRTY:
|
|
case FSCTL_QUERY_RETRIEVAL_POINTERS:
|
|
case FSCTL_GET_COMPRESSION:
|
|
case FSCTL_SET_COMPRESSION:
|
|
case FSCTL_MARK_AS_SYSTEM_HIVE:
|
|
case FSCTL_QUERY_FAT_BPB:
|
|
default:
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST);
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
NTSTATUS UdfOplockRequest(IN PIRP_CONTEXT IrpContext, IN PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This is the common routine to handle oplock requests made via the NtFsControlFile call.
|
|
Arguments:
|
|
Irp - Supplies the Irp being processed
|
|
Return Value:
|
|
NTSTATUS - The return status for the operation
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PFCB Fcb;
|
|
PCCB Ccb;
|
|
ULONG OplockCount = 0;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
PAGED_CODE();
|
|
|
|
// We only permit oplock requests on files.
|
|
if (UdfDecodeFileObject(IrpSp->FileObject, &Fcb, &Ccb) != UserFileOpen) {
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_INVALID_PARAMETER);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Make this a waitable Irpcontext so we don't fail to acquire the resources.
|
|
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
|
|
ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST);
|
|
|
|
// Switch on the function control code. We grab the Fcb exclusively
|
|
// for oplock requests, shared for oplock break acknowledgement.
|
|
switch (IrpSp->Parameters.FileSystemControl.FsControlCode) {
|
|
case FSCTL_REQUEST_OPLOCK_LEVEL_1:
|
|
case FSCTL_REQUEST_OPLOCK_LEVEL_2:
|
|
case FSCTL_REQUEST_BATCH_OPLOCK:
|
|
case FSCTL_REQUEST_FILTER_OPLOCK:
|
|
UdfAcquireFcbExclusive(IrpContext, Fcb, FALSE);
|
|
if (IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2) {
|
|
if (Fcb->FileLock != NULL) {
|
|
OplockCount = (ULONG)FsRtlAreThereCurrentFileLocks(Fcb->FileLock);
|
|
}
|
|
} else {
|
|
OplockCount = Fcb->FcbCleanup;
|
|
}
|
|
|
|
break;
|
|
case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
|
|
case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
|
|
case FSCTL_OPLOCK_BREAK_NOTIFY:
|
|
case FSCTL_OPLOCK_BREAK_ACK_NO_2:
|
|
UdfAcquireFcbShared(IrpContext, Fcb, FALSE);
|
|
break;
|
|
default:
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_INVALID_PARAMETER);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Use a try finally to free the Fcb.
|
|
try {
|
|
// Verify the Fcb.
|
|
UdfVerifyFcbOperation(IrpContext, Fcb);
|
|
|
|
// Call the FsRtl routine to grant/acknowledge oplock.
|
|
Status = FsRtlOplockFsctrl(&Fcb->Oplock, Irp, OplockCount);
|
|
|
|
// Set the flag indicating if Fast I/O is possible
|
|
UdfLockFcb(IrpContext, Fcb);
|
|
Fcb->IsFastIoPossible = UdfIsFastIoPossible(Fcb);
|
|
UdfUnlockFcb(IrpContext, Fcb);
|
|
|
|
// The oplock package will complete the Irp.
|
|
Irp = NULL;
|
|
} finally{
|
|
// Release all of our resources
|
|
UdfReleaseFcb(IrpContext, Fcb);
|
|
}
|
|
|
|
// Complete the request if there was no exception.
|
|
UdfCompleteRequest(IrpContext, Irp, Status);
|
|
return Status;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
NTSTATUS UdfLockVolumeInternal(IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_OBJECT FileObject OPTIONAL)
|
|
/*++
|
|
Routine Description:
|
|
This routine performs the actual lock volume operation. It will be called
|
|
by anyone wishing to try to protect the volume for a long duration. PNP operations are such a user.
|
|
|
|
The volume must be held exclusive by the caller.
|
|
Arguments:
|
|
Vcb - The volume being locked.
|
|
FileObject - File corresponding to the handle locking the volume. If this is not specified, a system lock is assumed.
|
|
Return Value:
|
|
NTSTATUS - The return status for the operation
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
NTSTATUS FinalStatus = (FileObject ? STATUS_ACCESS_DENIED : STATUS_DEVICE_BUSY);
|
|
ULONG RemainingUserReferences = (FileObject ? 1 : 0);
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_EXCLUSIVE_VCB(Vcb);
|
|
|
|
// If the volume is already locked then complete with success if this file
|
|
// object has the volume locked, fail otherwise.
|
|
if (FlagOn(Vcb->VcbState, VCB_STATE_LOCKED)) {
|
|
if (FileObject && Vcb->VolumeLockFileObject == FileObject) {
|
|
FinalStatus = STATUS_SUCCESS;
|
|
}
|
|
} else if (Vcb->VcbCleanup == RemainingUserReferences) {
|
|
// The cleanup count for the volume only reflects the fileobject that
|
|
// will lock the volume. Otherwise, we must fail the request.
|
|
|
|
// Since the only cleanup is for the provided fileobject, we will try
|
|
// to get rid of all of the other user references. If there is only one
|
|
// remaining after the purge then we can allow the volume to be locked.
|
|
UdfPurgeVolume(IrpContext, Vcb, FALSE);
|
|
|
|
// Now back out of our synchronization and wait for the lazy writer
|
|
// to finish off any lazy closes that could have been outstanding.
|
|
|
|
// Since we purged, we know that the lazy writer will issue all
|
|
// possible lazy closes in the next tick - if we hadn't, an otherwise
|
|
// unopened file with a large amount of dirty data could have hung
|
|
// around for a while as the data trickled out to the disk.
|
|
|
|
// This is even more important now since we send notification to
|
|
// alert other folks that this style of check is about to happen so
|
|
// that they can close their handles. We don't want to enter a fast
|
|
// race with the lazy writer tearing down his references to the file.
|
|
|
|
UdfReleaseVcb(IrpContext, Vcb);
|
|
|
|
Status = CcWaitForCurrentLazyWriterActivity();
|
|
|
|
// This is intentional. If we were able to get the Vcb before, just
|
|
// wait for it and take advantage of knowing that it is OK to leave the flag up.
|
|
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
|
|
UdfAcquireVcbExclusive(IrpContext, Vcb, FALSE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
UdfFspClose(Vcb);
|
|
|
|
if (Vcb->VcbUserReference == Vcb->VcbResidualUserReference + RemainingUserReferences) {
|
|
SetFlag(Vcb->VcbState, VCB_STATE_LOCKED);
|
|
Vcb->VolumeLockFileObject = FileObject;
|
|
FinalStatus = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return FinalStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS UdfUnlockVolumeInternal(IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_OBJECT FileObject OPTIONAL)
|
|
/*++
|
|
Routine Description:
|
|
This routine performs the actual unlock volume operation.
|
|
The volume must be held exclusive by the caller.
|
|
Arguments:
|
|
Vcb - The volume being locked.
|
|
FileObject - File corresponding to the handle locking the volume. If this is not specified, a system lock is assumed.
|
|
Return Value:
|
|
NTSTATUS - The return status for the operation
|
|
Attempting to remove a system lock that did not exist is OK.
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
|
|
|
if (FlagOn(Vcb->VcbState, VCB_STATE_LOCKED) && FileObject == Vcb->VolumeLockFileObject) {
|
|
ClearFlag(Vcb->VcbState, VCB_STATE_LOCKED);
|
|
Vcb->VolumeLockFileObject = NULL;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
NTSTATUS UdfLockVolume(IN PIRP_CONTEXT IrpContext, IN PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This routine performs the lock volume operation. It is responsible for
|
|
either completing of enqueuing the input Irp.
|
|
Arguments:
|
|
Irp - Supplies the Irp to process
|
|
Return Value:
|
|
NTSTATUS - The return status for the operation
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PVCB Vcb;
|
|
PFCB Fcb;
|
|
PCCB Ccb;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Decode the file object, the only type of opens we accept are user volume opens.
|
|
if (UdfDecodeFileObject(IrpSp->FileObject, &Fcb, &Ccb) != UserVolumeOpen) {
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_INVALID_PARAMETER);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Send our notification so that folks that like to hold handles on volumes can get out of the way.
|
|
FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_LOCK);
|
|
|
|
// Acquire exclusive access to the Vcb.
|
|
Vcb = Fcb->Vcb;
|
|
UdfAcquireVcbExclusive(IrpContext, Vcb, FALSE);
|
|
|
|
try {
|
|
// Verify the Vcb.
|
|
UdfVerifyVcb(IrpContext, Vcb);
|
|
|
|
Status = UdfLockVolumeInternal(IrpContext, Vcb, IrpSp->FileObject);
|
|
} finally{
|
|
// Release the Vcb.
|
|
UdfReleaseVcb(IrpContext, Vcb);
|
|
|
|
if (AbnormalTermination() || !NT_SUCCESS(Status)) {
|
|
FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_LOCK_FAILED);
|
|
}
|
|
}
|
|
|
|
// Complete the request if there haven't been any exceptions.
|
|
UdfCompleteRequest(IrpContext, Irp, Status);
|
|
return Status;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
NTSTATUS UdfUnlockVolume(IN PIRP_CONTEXT IrpContext, IN PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This routine performs the unlock volume operation. It is responsible for
|
|
either completing of enqueuing the input Irp.
|
|
Arguments:
|
|
Irp - Supplies the Irp to process
|
|
Return Value:
|
|
NTSTATUS - The return status for the operation
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PVCB Vcb;
|
|
PFCB Fcb;
|
|
PCCB Ccb;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Decode the file object, the only type of opens we accept are user volume opens.
|
|
if (UdfDecodeFileObject(IrpSp->FileObject, &Fcb, &Ccb) != UserVolumeOpen) {
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_INVALID_PARAMETER);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Acquire exclusive access to the Vcb.
|
|
Vcb = Fcb->Vcb;
|
|
|
|
UdfAcquireVcbExclusive(IrpContext, Vcb, FALSE);
|
|
|
|
// We won't check for a valid Vcb for this request. An unlock will always
|
|
// succeed on a locked volume.
|
|
Status = UdfUnlockVolumeInternal(IrpContext, Vcb, IrpSp->FileObject);
|
|
|
|
// Release all of our resources
|
|
UdfReleaseVcb(IrpContext, Vcb);
|
|
|
|
// Send notification that the volume is avaliable.
|
|
if (NT_SUCCESS(Status)) {
|
|
FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_UNLOCK);
|
|
}
|
|
|
|
// Complete the request if there haven't been any exceptions.
|
|
UdfCompleteRequest(IrpContext, Irp, Status);
|
|
return Status;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
NTSTATUS UdfDismountVolume(IN PIRP_CONTEXT IrpContext, IN PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This routine performs the dismount volume operation. It is responsible for
|
|
either completing of enqueuing the input Irp. We only dismount a volume which
|
|
has been locked. The intent here is that someone has locked the volume (they are the
|
|
only remaining handle). We set the volume state to invalid so that it will be torn down quickly.
|
|
Arguments:
|
|
Irp - Supplies the Irp to process
|
|
Return Value:
|
|
NTSTATUS - The return status for the operation
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PVCB Vcb;
|
|
PFCB Fcb;
|
|
PCCB Ccb;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (UdfDecodeFileObject(IrpSp->FileObject, &Fcb, &Ccb) != UserVolumeOpen) {
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_INVALID_PARAMETER);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Send notification.
|
|
FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_DISMOUNT);
|
|
|
|
// Acquire exclusive access to the Vcb.
|
|
Vcb = Fcb->Vcb;
|
|
|
|
UdfAcquireVcbExclusive(IrpContext, Vcb, FALSE);
|
|
|
|
// Mark the volume as invalid, but only do it if the vcb is locked
|
|
// by this handle and the volume is currently mounted. No more
|
|
// operations will occur on this vcb except cleanup/close.
|
|
if ((Vcb->VcbCondition != VcbMounted) && (Vcb->VolumeLockFileObject != IrpSp->FileObject)) {
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
} else {
|
|
SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);
|
|
SetFlag(Vcb->VcbState, VCB_STATE_NOTIFY_REMOUNT);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
// Release all of our resources
|
|
UdfReleaseVcb(IrpContext, Vcb);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_DISMOUNT_FAILED);
|
|
}
|
|
|
|
// Complete the request if there haven't been any exceptions.
|
|
UdfCompleteRequest(IrpContext, Irp, Status);
|
|
return Status;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
UdfIsVolumeDirty(IN PIRP_CONTEXT IrpContext, IN PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This routine determines if a volume is currently dirty.
|
|
Arguments:
|
|
Irp - Supplies the Irp to process
|
|
Return Value:
|
|
NTSTATUS - The return status for the operation
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
TYPE_OF_OPEN TypeOfOpen;
|
|
PVCB Vcb;
|
|
PFCB Fcb;
|
|
PCCB Ccb;
|
|
PULONG VolumeState;
|
|
|
|
// Get the current stack location and extract the output buffer information.
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
// Get a pointer to the output buffer. Look at the system buffer field in the
|
|
// irp first. Then the Irp Mdl.
|
|
if (Irp->AssociatedIrp.SystemBuffer != NULL) {
|
|
VolumeState = Irp->AssociatedIrp.SystemBuffer;
|
|
} else if (Irp->MdlAddress != NULL) {
|
|
VolumeState = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
|
|
if (NULL == VolumeState) {
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_INSUFFICIENT_RESOURCES);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
} else {
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_INVALID_USER_BUFFER);
|
|
return STATUS_INVALID_USER_BUFFER;
|
|
}
|
|
|
|
// Make sure the output buffer is large enough and then initialize
|
|
// the answer to be that the volume isn't dirty.
|
|
if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG)) {
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_INVALID_PARAMETER);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
*VolumeState = 0;
|
|
|
|
// Decode the file object
|
|
TypeOfOpen = UdfDecodeFileObject(IrpSp->FileObject, &Fcb, &Ccb);
|
|
if (TypeOfOpen != UserVolumeOpen) {
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_INVALID_PARAMETER);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (Fcb->Vcb->VcbCondition != VcbMounted) {
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_VOLUME_DISMOUNTED);
|
|
return STATUS_VOLUME_DISMOUNTED;
|
|
}
|
|
|
|
// Now set up to return the clean state. If we paid attention to the dirty
|
|
// state of the media we could be more accurate, but since this is a readonly
|
|
// implementation at the moment we think it is clean all of the time.
|
|
Irp->IoStatus.Information = sizeof(ULONG);
|
|
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_SUCCESS);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
NTSTATUS UdfIsVolumeMounted(IN PIRP_CONTEXT IrpContext, IN PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This routine determines if a volume is currently mounted.
|
|
Arguments:
|
|
Irp - Supplies the Irp to process
|
|
Return Value:
|
|
NTSTATUS - The return status for the operation
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
PFCB Fcb;
|
|
PCCB Ccb;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Decode the file object.
|
|
UdfDecodeFileObject(IrpSp->FileObject, &Fcb, &Ccb);
|
|
|
|
if (Fcb != NULL) {
|
|
// Disable PopUps, we want to return any error.
|
|
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS);
|
|
|
|
// Verify the Vcb. This will raise in the error condition.
|
|
UdfVerifyVcb(IrpContext, Fcb->Vcb);
|
|
}
|
|
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_SUCCESS);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
NTSTATUS UdfIsPathnameValid(IN PIRP_CONTEXT IrpContext, IN PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This routine determines if pathname is a valid UDFS pathname.
|
|
We always succeed this request.
|
|
Arguments:
|
|
Irp - Supplies the Irp to process.
|
|
Return Value:
|
|
None
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_SUCCESS);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
NTSTATUS UdfInvalidateVolumes(IN PIRP_CONTEXT IrpContext, IN PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This routine searches for all the volumes mounted on the same real device
|
|
of the current DASD handle, and marks them all bad. The only operation that can be done on such handles is cleanup and close.
|
|
Arguments:
|
|
Irp - Supplies the Irp to process
|
|
Return Value:
|
|
NTSTATUS - The return status for the operation
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
KIRQL SavedIrql;
|
|
LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0};
|
|
HANDLE Handle;
|
|
PVPB NewVpb;
|
|
PVCB Vcb;
|
|
PLIST_ENTRY Links;
|
|
PFILE_OBJECT FileToMarkBad;
|
|
PDEVICE_OBJECT DeviceToMarkBad;
|
|
|
|
// Check for the correct security access.
|
|
// The caller must have the SeTcbPrivilege.
|
|
if (!SeSinglePrivilegeCheck(TcbPrivilege, Irp->RequestorMode)) {
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_PRIVILEGE_NOT_HELD);
|
|
return STATUS_PRIVILEGE_NOT_HELD;
|
|
}
|
|
|
|
// Try to get a pointer to the device object from the handle passed in.
|
|
if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE)) {
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_INVALID_PARAMETER);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Handle = *((PHANDLE)Irp->AssociatedIrp.SystemBuffer);
|
|
|
|
Status = ObReferenceObjectByHandle(Handle, 0, *IoFileObjectType, KernelMode, &FileToMarkBad, NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
UdfCompleteRequest(IrpContext, Irp, Status);
|
|
return Status;
|
|
}
|
|
|
|
// Grab the DeviceObject from the FileObject.
|
|
DeviceToMarkBad = FileToMarkBad->DeviceObject;
|
|
|
|
// We only needed the device object involved, not a reference to the file.
|
|
ObDereferenceObject(FileToMarkBad);
|
|
|
|
// Create a new Vpb for this device so that any new opens will mount a new volume.
|
|
NewVpb = ExAllocatePoolWithTag(NonPagedPoolMustSucceed, sizeof(VPB), TAG_VPB);
|
|
RtlZeroMemory(NewVpb, sizeof(VPB));
|
|
|
|
NewVpb->Type = IO_TYPE_VPB;
|
|
NewVpb->Size = sizeof(VPB);
|
|
NewVpb->RealDevice = DeviceToMarkBad;
|
|
NewVpb->Flags = FlagOn(DeviceToMarkBad->Vpb->Flags, VPB_REMOVE_PENDING);
|
|
|
|
// Make sure this request can wait.
|
|
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
|
|
ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST);
|
|
|
|
UdfAcquireUdfData(IrpContext);
|
|
|
|
// Nothing can go wrong now.
|
|
IoAcquireVpbSpinLock(&SavedIrql);
|
|
DeviceToMarkBad->Vpb = NewVpb;
|
|
IoReleaseVpbSpinLock(SavedIrql);
|
|
|
|
// Now walk through all the mounted Vcb's looking for candidates to mark invalid.
|
|
|
|
// On volumes we mark invalid, check for dismount possibility (which is why we have to get the next link so early).
|
|
Links = UdfData.VcbQueue.Flink;
|
|
|
|
while (Links != &UdfData.VcbQueue) {
|
|
Vcb = CONTAINING_RECORD(Links, VCB, VcbLinks);
|
|
Links = Links->Flink;
|
|
|
|
// If we get a match, mark the volume Bad, and also check to see if the volume should go away.
|
|
UdfLockVcb(IrpContext, Vcb);
|
|
|
|
if (Vcb->Vpb->RealDevice == DeviceToMarkBad) {
|
|
if (Vcb->VcbCondition != VcbDismountInProgress) {
|
|
Vcb->VcbCondition = VcbInvalid;
|
|
}
|
|
|
|
UdfUnlockVcb(IrpContext, Vcb);
|
|
|
|
UdfPurgeVolume(IrpContext, Vcb, FALSE);
|
|
UdfCheckForDismount(IrpContext, Vcb, FALSE);
|
|
} else {
|
|
UdfUnlockVcb(IrpContext, Vcb);
|
|
}
|
|
}
|
|
|
|
UdfReleaseUdfData(IrpContext);
|
|
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_SUCCESS);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
NTSTATUS UdfMountVolume(IN PIRP_CONTEXT IrpContext, IN PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This routine performs the mount volume operation.
|
|
It is responsible for either completing of enqueuing the input Irp.
|
|
|
|
Its job is to verify that the volume denoted in the IRP is a UDF volume,
|
|
and create the VCB and root directory FCB structures. The algorithm it uses is essentially as follows:
|
|
1. Create a new Vcb Structure, and initialize it enough to do I/O through the on-disk volume descriptors.
|
|
2. Read the disk and check if it is a UDF volume.
|
|
3. If it is not a UDF volume then delete the Vcb and complete the IRP with STATUS_UNRECOGNIZED_VOLUME
|
|
4. Check if the volume was previously mounted and if it was then do a
|
|
remount operation. This involves deleting the VCB, hook in the old VCB, and complete the IRP.
|
|
5. Otherwise create a Vcb and root directory FCB
|
|
Arguments:
|
|
Irp - Supplies the Irp to process
|
|
Return Value:
|
|
NTSTATUS - The return status for the operation
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PVOLUME_DEVICE_OBJECT VolDo = NULL;
|
|
PVCB Vcb = NULL;
|
|
PVCB OldVcb = NULL;
|
|
PPCB Pcb = NULL;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PDEVICE_OBJECT DeviceObjectWeTalkTo = IrpSp->Parameters.MountVolume.DeviceObject;
|
|
PVPB Vpb = IrpSp->Parameters.MountVolume.Vpb;
|
|
PFILE_OBJECT FileObjectToNotify = NULL;
|
|
ULONG MediaChangeCount = 0;
|
|
DISK_GEOMETRY DiskGeometry;
|
|
PNSR_ANCHOR AnchorVolumeDescriptor = NULL;
|
|
PNSR_PVD PrimaryVolumeDescriptor = NULL;
|
|
PNSR_LVOL LogicalVolumeDescriptor = NULL;
|
|
PNSR_FSD FileSetDescriptor = NULL;
|
|
BOOLEAN BridgeMedia;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Check the input parameters
|
|
ASSERT_IRP_CONTEXT(IrpContext);
|
|
ASSERT_IRP(Irp);
|
|
|
|
// Check that we are talking to a Cdrom or Disk device. This request should always be waitable.
|
|
ASSERT(Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM || Vpb->RealDevice->DeviceType == FILE_DEVICE_DISK);
|
|
ASSERT(FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT));
|
|
|
|
DebugTrace((+1, Dbg, "UdfMountVolume\n"));
|
|
|
|
// Update the real device in the IrpContext from the Vpb.
|
|
// There was no available file object when the IrpContext was created.
|
|
IrpContext->RealDevice = Vpb->RealDevice;
|
|
|
|
// Check if we have disabled the mount process.
|
|
if (UdfDisable) {
|
|
UdfCompleteRequest(IrpContext, Irp, STATUS_UNRECOGNIZED_VOLUME);
|
|
DebugTrace((0, Dbg, "UdfMountVolume, disabled\n"));
|
|
DebugTrace((-1, Dbg, "UdfMountVolume -> STATUS_UNRECOGNIZED_VOLUME\n"));
|
|
return STATUS_UNRECOGNIZED_VOLUME;
|
|
}
|
|
|
|
// Do a CheckVerify here to lift the MediaChange ticker from the driver
|
|
Status = UdfPerformDevIoCtrl(IrpContext,
|
|
(Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM ? IOCTL_CDROM_CHECK_VERIFY : IOCTL_DISK_CHECK_VERIFY),
|
|
DeviceObjectWeTalkTo,
|
|
&MediaChangeCount,
|
|
sizeof(ULONG),
|
|
FALSE,
|
|
TRUE,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
UdfCompleteRequest(IrpContext, Irp, Status);
|
|
DebugTrace((0, Dbg, "UdfMountVolume, CHECK_VERIFY handed back status %08x (so don't continue)\n", Status));
|
|
DebugTrace((-1, Dbg, "UdfMountVolume -> %08x\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
// Now let's make Jeff delirious and call to get the disk geometry. This
|
|
// will fix the case where the first change line is swallowed.
|
|
|
|
// This IOCTL does not have a generic STORAGE equivalent, so we must figure
|
|
// our which variant to pass down from the real underlying device object (as
|
|
// opposed to the top of the driver filter stack we will really be attaching on top of).
|
|
|
|
Status = UdfPerformDevIoCtrl(IrpContext,
|
|
(Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM ? IOCTL_CDROM_GET_DRIVE_GEOMETRY : IOCTL_DISK_GET_DRIVE_GEOMETRY),
|
|
DeviceObjectWeTalkTo,
|
|
&DiskGeometry,
|
|
sizeof(DISK_GEOMETRY),
|
|
FALSE,
|
|
TRUE,
|
|
NULL);
|
|
|
|
// If this call failed, we might be able to get away with a heuristic guess as to
|
|
// what the sector size is (per CDFS), but that is playing with fire. Nearly every
|
|
// failure here will be a permanent problem of some form.
|
|
if (!NT_SUCCESS(Status)) {
|
|
UdfCompleteRequest(IrpContext, Irp, Status);
|
|
DebugTrace((0, Dbg, "UdfMountVolume, GET_DRIVE_GEOMETRY failed\n"));
|
|
DebugTrace((-1, Dbg, "UdfMountVolume -> %08x\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
// Acquire the global resource to do mount operations.
|
|
UdfAcquireUdfData(IrpContext);
|
|
|
|
// Use a try-finally to facilitate cleanup.
|
|
try {
|
|
// Do a quick check to see if there any Vcb's which can be removed.
|
|
UdfScanForDismountedVcb(IrpContext);
|
|
|
|
// Make sure that the driver/drive is not screwing up underneath of us by
|
|
// feeding us garbage for the sector size.
|
|
if (DiskGeometry.BytesPerSector == 0 ||
|
|
(DiskGeometry.BytesPerSector & ~(1 << UdfHighBit(DiskGeometry.BytesPerSector))) != 0) {
|
|
DebugTrace((0, 0,
|
|
"UdfMountVolume, bad DiskGeometry (%08x) .BytesPerSector == %08x\n",
|
|
&DiskGeometry,
|
|
DiskGeometry.BytesPerSector));
|
|
|
|
ASSERT(FALSE);
|
|
|
|
try_leave(Status = STATUS_DRIVER_INTERNAL_ERROR);
|
|
}
|
|
|
|
// Now go confirm that this volume may be a UDF image by looking for a
|
|
// valid ISO 13346 Volume Recognition Sequence.
|
|
if (!UdfRecognizeVolume(IrpContext, DeviceObjectWeTalkTo, DiskGeometry.BytesPerSector, &BridgeMedia)) {
|
|
DebugTrace((0, Dbg, "UdfMountVolume, recognition failed so not mounting\n"));
|
|
try_leave(Status = STATUS_UNRECOGNIZED_VOLUME);
|
|
}
|
|
|
|
// Create the DeviceObject for this mount attempt
|
|
Status = IoCreateDevice(UdfData.DriverObject,
|
|
sizeof(VOLUME_DEVICE_OBJECT) - sizeof(DEVICE_OBJECT),
|
|
NULL,
|
|
FILE_DEVICE_CD_ROM_FILE_SYSTEM,
|
|
0,
|
|
FALSE,
|
|
(PDEVICE_OBJECT *)&VolDo);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugTrace((0, Dbg, "UdfMountVolume, couldn't get voldo! (%08x)\n", Status));
|
|
try_leave(Status);
|
|
}
|
|
|
|
// Our alignment requirement is the larger of the processor alignment requirement
|
|
// already in the volume device object and that in the DeviceObjectWeTalkTo
|
|
if (DeviceObjectWeTalkTo->AlignmentRequirement > VolDo->DeviceObject.AlignmentRequirement) {
|
|
VolDo->DeviceObject.AlignmentRequirement = DeviceObjectWeTalkTo->AlignmentRequirement;
|
|
}
|
|
|
|
ClearFlag(VolDo->DeviceObject.Flags, DO_DEVICE_INITIALIZING);
|
|
|
|
// Initialize the overflow queue for the volume
|
|
|
|
VolDo->OverflowQueueCount = 0;
|
|
InitializeListHead(&VolDo->OverflowQueue);
|
|
|
|
VolDo->PostedRequestCount = 0;
|
|
KeInitializeSpinLock(&VolDo->OverflowQueueSpinLock);
|
|
|
|
// Now before we can initialize the Vcb we need to set up the
|
|
// device object field in the VPB to point to our new volume device object.
|
|
Vpb->DeviceObject = (PDEVICE_OBJECT)VolDo;
|
|
|
|
// Initialize the Vcb. This routine will raise on an allocation failure.
|
|
UdfInitializeVcb(IrpContext, &VolDo->Vcb, DeviceObjectWeTalkTo, Vpb, &DiskGeometry, MediaChangeCount);
|
|
|
|
// We must initialize the stack size in our device object before
|
|
// the following reads, because the I/O system has not done it yet.
|
|
((PDEVICE_OBJECT)VolDo)->StackSize = (CCHAR)(DeviceObjectWeTalkTo->StackSize + 1);
|
|
|
|
// Pick up a local pointer to the new Vcb. Here is where we start
|
|
// thinking about cleanup of structures if the mount is failed.
|
|
Vcb = &VolDo->Vcb;
|
|
Vpb = NULL;
|
|
VolDo = NULL;
|
|
|
|
// Store the Vcb in the IrpContext as we didn't have one before.
|
|
IrpContext->Vcb = Vcb;
|
|
|
|
UdfAcquireVcbExclusive(IrpContext, Vcb, FALSE);
|
|
|
|
// Let's reference the Vpb to make sure we are the one to have the last dereference.
|
|
Vcb->Vpb->ReferenceCount += 1;
|
|
|
|
// Clear the verify bit for the start of mount.
|
|
ClearFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);
|
|
|
|
// Now find the multi-session bounds on this media.
|
|
UdfDetermineVolumeBounding(IrpContext, Vcb, &Vcb->BoundS, &Vcb->BoundN);
|
|
|
|
// Now find the Anchor Volume Descriptor so we can discover the Volume Set Descriptor Sequence extent.
|
|
Status = UdfFindAnchorVolumeDescriptor(IrpContext, Vcb, &AnchorVolumeDescriptor);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugTrace((0, Dbg, "UdfMountVolume, couldn't find anchor descriptors\n"));
|
|
try_leave(Status);
|
|
}
|
|
|
|
// Now search for the prevailing copies of the PVD, LVD, and related PD in the extents indicated by the AVD.
|
|
Status = UdfFindVolumeDescriptors(IrpContext,
|
|
Vcb,
|
|
&AnchorVolumeDescriptor->Main,
|
|
&Pcb,
|
|
&PrimaryVolumeDescriptor,
|
|
&LogicalVolumeDescriptor);
|
|
|
|
// If we discovered invalid structures on the main extent, we may still
|
|
// be able to use the reserve extent. By definition the two extents
|
|
// must be logically equal, so just plow into it on any error.
|
|
if (!NT_SUCCESS(Status)) {
|
|
Status = UdfFindVolumeDescriptors(IrpContext,
|
|
Vcb,
|
|
&AnchorVolumeDescriptor->Reserve,
|
|
&Pcb,
|
|
&PrimaryVolumeDescriptor,
|
|
&LogicalVolumeDescriptor);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugTrace((0, Dbg, "UdfMountVolume, couldn't find good VSD descriptors (PVD/LVD/PD)\n"));
|
|
try_leave(Status);
|
|
}
|
|
|
|
// Now go complete initialization of the Pcb. After this point, we can perform
|
|
// physical partition mappings and know that the partition table is good.
|
|
Status = UdfCompletePcb(IrpContext, Vcb, Pcb);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugTrace((0, Dbg, "UdfMountVolume, Pcb completion failed\n"));
|
|
try_leave(Status);
|
|
}
|
|
|
|
Vcb->Pcb = Pcb;
|
|
Pcb = NULL;
|
|
|
|
// Set up all the support we need to do reads into the volume.
|
|
UdfUpdateVcbPhase0(IrpContext, Vcb);
|
|
|
|
// Now go get the fileset descriptor that will finally reveal the location
|
|
// of the root directory on this volume.
|
|
Status = UdfFindFileSetDescriptor(IrpContext, Vcb, &LogicalVolumeDescriptor->FSD, &FileSetDescriptor);
|
|
if (!NT_SUCCESS(Status)) {
|
|
try_leave(NOTHING);
|
|
}
|
|
|
|
// Now that we have everything together, update the Vpb with identification of this volume.
|
|
UdfUpdateVolumeLabel(IrpContext,
|
|
Vcb->Vpb->VolumeLabel,
|
|
&Vcb->Vpb->VolumeLabelLength,
|
|
LogicalVolumeDescriptor->VolumeID,
|
|
sizeof(LogicalVolumeDescriptor->VolumeID));
|
|
|
|
UdfUpdateVolumeSerialNumber(IrpContext, &Vcb->Vpb->SerialNumber, FileSetDescriptor);
|
|
|
|
// Check if this is a remount operation. If so then clean up
|
|
// the data structures passed in and created here.
|
|
if (UdfIsRemount(IrpContext, Vcb, &OldVcb)) {
|
|
// Link the old Vcb to point to the new device object that we should be talking to, dereferencing the previous.
|
|
ObDereferenceObject(OldVcb->TargetDeviceObject);
|
|
|
|
Vcb->Vpb->RealDevice->Vpb = OldVcb->Vpb;
|
|
|
|
OldVcb->Vpb->RealDevice = Vcb->Vpb->RealDevice;
|
|
OldVcb->TargetDeviceObject = DeviceObjectWeTalkTo;
|
|
OldVcb->VcbCondition = VcbMounted;
|
|
OldVcb->MediaChangeCount = Vcb->MediaChangeCount;
|
|
|
|
// Push the state of the method 2 bit across. In changing the device,
|
|
// we may now be on one with a different requirement.
|
|
ClearFlag(OldVcb->VcbState, VCB_STATE_METHOD_2_FIXUP);
|
|
SetFlag(OldVcb->VcbState, FlagOn(Vcb->VcbState, VCB_STATE_METHOD_2_FIXUP));
|
|
|
|
// See if we will need to provide notification of the remount. This is the readonly
|
|
// filesystem's form of dismount/mount notification - we promise that whenever a
|
|
// volume is "dismounted", that a mount notification will occur when it is revalidated.
|
|
// Note that we do not send mount on normal remounts - that would duplicate the media
|
|
// arrival notification of the device driver.
|
|
if (FlagOn(OldVcb->VcbState, VCB_STATE_NOTIFY_REMOUNT)) {
|
|
ClearFlag(OldVcb->VcbState, VCB_STATE_NOTIFY_REMOUNT);
|
|
FileObjectToNotify = OldVcb->RootIndexFcb->FileObject;
|
|
ObReferenceObject(FileObjectToNotify);
|
|
}
|
|
|
|
DebugTrace((0, Dbg, "UdfMountVolume, remounted old Vcb %08x\n", OldVcb));
|
|
|
|
try_leave(Status = STATUS_SUCCESS);
|
|
}
|
|
|
|
// Initialize the Vcb and associated structures from our volume descriptors
|
|
UdfUpdateVcbPhase1(IrpContext, Vcb, FileSetDescriptor);
|
|
|
|
// Drop an extra reference on the root dir file so we'll be able to send notification.
|
|
if (Vcb->RootIndexFcb) {
|
|
FileObjectToNotify = Vcb->RootIndexFcb->FileObject;
|
|
ObReferenceObject(FileObjectToNotify);
|
|
}
|
|
|
|
// The new mount is complete. Remove the additional references on this
|
|
// Vcb since, at this point, we have added the real references this volume
|
|
// will have during its lifetime. We also need to drop the additional
|
|
// reference on the device we mounted.
|
|
Vcb->VcbReference -= Vcb->VcbResidualReference;
|
|
ASSERT(Vcb->VcbReference == Vcb->VcbResidualReference);
|
|
|
|
ObDereferenceObject(Vcb->TargetDeviceObject);
|
|
|
|
Vcb->VcbCondition = VcbMounted;
|
|
|
|
UdfReleaseVcb(IrpContext, Vcb);
|
|
Vcb = NULL;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
} finally{
|
|
DebugUnwind("UdfMountVolume");
|
|
|
|
// If we didn't complete the mount then cleanup any remaining structures.
|
|
|
|
if (Vpb != NULL) {
|
|
Vpb->DeviceObject = NULL;
|
|
}
|
|
|
|
if (Pcb != NULL) {
|
|
UdfDeletePcb(Pcb);
|
|
}
|
|
|
|
if (Vcb != NULL) {
|
|
// Make sure there is no Vcb in the IrpContext since it could go away
|
|
IrpContext->Vcb = NULL;
|
|
|
|
Vcb->VcbReference -= Vcb->VcbResidualReference;
|
|
|
|
if (UdfDismountVcb(IrpContext, Vcb)) {
|
|
UdfReleaseVcb(IrpContext, Vcb);
|
|
}
|
|
} else if (VolDo != NULL) {
|
|
IoDeleteDevice((PDEVICE_OBJECT)VolDo);
|
|
Vpb->DeviceObject = NULL;
|
|
}
|
|
|
|
// Release the global resource.
|
|
UdfReleaseUdfData(IrpContext);
|
|
|
|
// Free any structures we may have been allocated
|
|
UdfFreePool(&AnchorVolumeDescriptor);
|
|
UdfFreePool(&PrimaryVolumeDescriptor);
|
|
UdfFreePool(&LogicalVolumeDescriptor);
|
|
UdfFreePool(&FileSetDescriptor);
|
|
}
|
|
|
|
// Now send mount notification.
|
|
if (FileObjectToNotify) {
|
|
FsRtlNotifyVolumeEvent(FileObjectToNotify, FSRTL_VOLUME_MOUNT);
|
|
ObDereferenceObject(FileObjectToNotify);
|
|
}
|
|
|
|
// Complete the request if no exception.
|
|
UdfCompleteRequest(IrpContext, Irp, Status);
|
|
DebugTrace((-1, Dbg, "UdfMountVolume -> %08x\n", Status));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
NTSTATUS UdfVerifyVolume(IN PIRP_CONTEXT IrpContext, IN PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This routine performs the verify volume operation.
|
|
It is responsible for either completing of enqueuing the input Irp.
|
|
Arguments:
|
|
Irp - Supplies the Irp to process
|
|
Return Value:
|
|
NTSTATUS - The return status for the operation
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PVPB Vpb = IrpSp->Parameters.VerifyVolume.Vpb;
|
|
PVCB Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->Parameters.VerifyVolume.DeviceObject)->Vcb;
|
|
PPCB Pcb = NULL;
|
|
PNSR_ANCHOR AnchorVolumeDescriptor = NULL;
|
|
PNSR_PVD PrimaryVolumeDescriptor = NULL;
|
|
PNSR_LVOL LogicalVolumeDescriptor = NULL;
|
|
PNSR_FSD FileSetDescriptor = NULL;
|
|
ULONG MediaChangeCount = 0;
|
|
ULONG Index;
|
|
PFILE_OBJECT FileObjectToNotify = NULL;
|
|
BOOLEAN ReturnError;
|
|
BOOLEAN ReleaseVcb;
|
|
IO_STATUS_BLOCK Iosb;
|
|
WCHAR VolumeLabel[MAXIMUM_VOLUME_LABEL_LENGTH / sizeof(WCHAR)];
|
|
USHORT VolumeLabelLength;
|
|
ULONG VolumeSerialNumber;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Check input.
|
|
ASSERT_IRP_CONTEXT(IrpContext);
|
|
|
|
// Check that we are talking to a Cdrom or Disk device. This request should always be waitable.
|
|
ASSERT(Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM || Vpb->RealDevice->DeviceType == FILE_DEVICE_DISK);
|
|
|
|
ASSERT_VCB(Vcb);
|
|
|
|
// Update the real device in the IrpContext from the Vpb. There was no available
|
|
// file object when the IrpContext was created.
|
|
IrpContext->RealDevice = Vpb->RealDevice;
|
|
|
|
// Acquire shared global access, the termination handler for the
|
|
// following try statement will free the access.
|
|
UdfAcquireUdfData(IrpContext);
|
|
UdfAcquireVcbExclusive(IrpContext, Vcb, FALSE);
|
|
ReleaseVcb = TRUE;
|
|
|
|
DebugTrace((+1, Dbg, "UdfVerifyVolume, Vcb %08x\n", Vcb));
|
|
|
|
try {
|
|
// Check if the real device still needs to be verified. If it doesn't
|
|
// then obviously someone beat us here and already did the work
|
|
// so complete the verify irp with success. Otherwise reenable
|
|
// the real device and get to work.
|
|
if (!FlagOn(Vpb->RealDevice->Flags, DO_VERIFY_VOLUME)) {
|
|
DebugTrace((0, Dbg, "UdfVerifyVolume, verify bit was cleared out ahead of us\n"));
|
|
MediaChangeCount = Vcb->MediaChangeCount;
|
|
try_leave(Status = STATUS_SUCCESS);
|
|
}
|
|
|
|
// Verify that there is a disk here.
|
|
Status = UdfPerformDevIoCtrl(IrpContext,
|
|
(Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM ? IOCTL_CDROM_CHECK_VERIFY : IOCTL_DISK_CHECK_VERIFY),
|
|
Vcb->TargetDeviceObject,
|
|
&MediaChangeCount,
|
|
sizeof(ULONG),
|
|
FALSE,
|
|
TRUE,
|
|
&Iosb);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugTrace((0, Dbg, "UdfVerifyVolume, CHECK_VERIFY failed\n"));
|
|
|
|
// If we will allow a raw mount then return WRONG_VOLUME to allow the volume to be mounted by raw.
|
|
if (FlagOn(IrpSp->Flags, SL_ALLOW_RAW_MOUNT)) {
|
|
DebugTrace((0, Dbg, "UdfVerifyVolume, ... allowing raw mount\n"));
|
|
Status = STATUS_WRONG_VOLUME;
|
|
}
|
|
|
|
try_leave(Status);
|
|
}
|
|
|
|
if (Iosb.Information != sizeof(ULONG)) {
|
|
// Be safe about the count in case the driver didn't fill it in
|
|
MediaChangeCount = 0;
|
|
}
|
|
|
|
// Verify that the device actually saw a change. If the driver does not
|
|
// support the MCC, then we must verify the volume in any case.
|
|
if (MediaChangeCount == 0 || Vcb->MediaChangeCount != MediaChangeCount) {
|
|
// Now we need to navigate the disc to find the relavent decriptors. This is
|
|
// much the same as the mount process.
|
|
|
|
// Find the AVD.
|
|
Status = UdfFindAnchorVolumeDescriptor(IrpContext, Vcb, &AnchorVolumeDescriptor);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugTrace((0, Dbg, "UdfVerifyVolume, No AVD visible\n"));
|
|
try_leave(Status = STATUS_WRONG_VOLUME);
|
|
}
|
|
|
|
// Get the prevailing descriptors out of the VDS, building a fresh Pcb.
|
|
Status = UdfFindVolumeDescriptors(IrpContext,
|
|
Vcb,
|
|
&AnchorVolumeDescriptor->Main,
|
|
&Pcb,
|
|
&PrimaryVolumeDescriptor,
|
|
&LogicalVolumeDescriptor);
|
|
|
|
// Try the reserve sequence in case of error.
|
|
if (Status == STATUS_DISK_CORRUPT_ERROR) {
|
|
Status = UdfFindVolumeDescriptors(IrpContext,
|
|
Vcb,
|
|
&AnchorVolumeDescriptor->Reserve,
|
|
&Pcb,
|
|
&PrimaryVolumeDescriptor,
|
|
&LogicalVolumeDescriptor);
|
|
}
|
|
|
|
// If we're totally unable to find a VDS, give up.
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugTrace((0, Dbg, "UdfVerifyVolume, PVD/LVD/PD pickup failed\n"));
|
|
try_leave(Status = STATUS_WRONG_VOLUME);
|
|
}
|
|
|
|
// Now go complete initialization of the Pcb so we can compare it.
|
|
Status = UdfCompletePcb(IrpContext, Vcb, Pcb);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugTrace((0, Dbg, "UdfVerifyVolume, Pcb completion failed\n"));
|
|
try_leave(Status = STATUS_WRONG_VOLUME);
|
|
}
|
|
|
|
// Now let's compare this new Pcb to the previous Vcb's Pcb to see if they appear to be equivalent.
|
|
if (!UdfEquivalentPcb(IrpContext, Pcb, Vcb->Pcb)) {
|
|
DebugTrace((0, Dbg, "UdfVerifyVolume, Pcbs are not equivalent\n"));
|
|
try_leave(Status = STATUS_WRONG_VOLUME);
|
|
}
|
|
|
|
// At this point we know that the Vcb's Pcb is OK for mapping to find the fileset
|
|
// descriptor, so we can drop the new one we built for comparison purposes.
|
|
UdfDeletePcb(Pcb);
|
|
Pcb = NULL;
|
|
|
|
// Go pick up the fileset descriptor.
|
|
Status = UdfFindFileSetDescriptor(IrpContext,
|
|
Vcb,
|
|
&LogicalVolumeDescriptor->FSD,
|
|
&FileSetDescriptor);
|
|
if (!NT_SUCCESS(Status)) {
|
|
try_leave(Status = STATUS_WRONG_VOLUME);
|
|
}
|
|
|
|
// Now that everything is in place, build a volume label and serial number from these
|
|
// descriptors and perform the final check that this Vcb is (or is not) the right one
|
|
// for the media now in the drive.
|
|
UdfUpdateVolumeLabel(IrpContext,
|
|
VolumeLabel,
|
|
&VolumeLabelLength,
|
|
LogicalVolumeDescriptor->VolumeID,
|
|
sizeof(LogicalVolumeDescriptor->VolumeID));
|
|
|
|
UdfUpdateVolumeSerialNumber(IrpContext, &VolumeSerialNumber, FileSetDescriptor);
|
|
|
|
if (Vcb->Vpb->SerialNumber != VolumeSerialNumber ||
|
|
Vcb->Vpb->VolumeLabelLength != VolumeLabelLength ||
|
|
RtlCompareMemory(Vcb->Vpb->VolumeLabel, VolumeLabel, VolumeLabelLength)) {
|
|
DebugTrace((0, Dbg, "UdfVerifyVolume, volume label/sn mismatch\n"));
|
|
try_leave(Status = STATUS_WRONG_VOLUME);
|
|
}
|
|
}
|
|
|
|
// The volume is OK, clear the verify bit.
|
|
DebugTrace((0, Dbg, "UdfVerifyVolume, looks like the same volume\n"));
|
|
|
|
Vcb->VcbCondition = VcbMounted;
|
|
|
|
ClearFlag(Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);
|
|
|
|
// See if we will need to provide notification of the remount. This is the readonly
|
|
// filesystem's form of dismount/mount notification.
|
|
if (FlagOn(Vcb->VcbState, VCB_STATE_NOTIFY_REMOUNT)) {
|
|
ClearFlag(Vcb->VcbState, VCB_STATE_NOTIFY_REMOUNT);
|
|
FileObjectToNotify = Vcb->RootIndexFcb->FileObject;
|
|
ObReferenceObject(FileObjectToNotify);
|
|
}
|
|
} finally{
|
|
// If we did not raise an exception, update the current Vcb.
|
|
if (!AbnormalTermination()) {
|
|
// Update the media change count to note that we have verified the volume at this value
|
|
Vcb->MediaChangeCount = MediaChangeCount;
|
|
|
|
// Mark the Vcb as not mounted.
|
|
if (Status == STATUS_WRONG_VOLUME) {
|
|
Vcb->VcbCondition = VcbNotMounted;
|
|
|
|
// Now, if there are no user handles to the volume, try to spark teardown by purging the volume.
|
|
if (Vcb->VcbCleanup == 0) {
|
|
if (NT_SUCCESS(UdfPurgeVolume(IrpContext, Vcb, FALSE))) {
|
|
ReleaseVcb = UdfCheckForDismount(IrpContext, Vcb, FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DebugTrace((-1, Dbg, "UdfVerifyVolume -> %08x\n", Status));
|
|
|
|
if (ReleaseVcb) {
|
|
UdfReleaseVcb(IrpContext, Vcb);
|
|
}
|
|
|
|
UdfReleaseUdfData(IrpContext);
|
|
|
|
// Delete the Pcb if built.
|
|
if (Pcb != NULL) {
|
|
UdfDeletePcb(Pcb);
|
|
}
|
|
|
|
UdfFreePool(&AnchorVolumeDescriptor);
|
|
UdfFreePool(&PrimaryVolumeDescriptor);
|
|
UdfFreePool(&LogicalVolumeDescriptor);
|
|
UdfFreePool(&FileSetDescriptor);
|
|
}
|
|
|
|
// Now send mount notification.
|
|
if (FileObjectToNotify) {
|
|
FsRtlNotifyVolumeEvent(FileObjectToNotify, FSRTL_VOLUME_MOUNT);
|
|
ObDereferenceObject(FileObjectToNotify);
|
|
}
|
|
|
|
// Complete the request if no exception.
|
|
UdfCompleteRequest(IrpContext, Irp, Status);
|
|
return Status;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
BOOLEAN UdfIsRemount(IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, OUT PVCB *OldVcb)
|
|
/*++
|
|
Routine Description:
|
|
This routine walks through the links of the Vcb chain in the global
|
|
data structure. The remount condition is met when the following conditions are all met:
|
|
1 - The 32 serial for this VPB matches that in a previous VPB.
|
|
2 - The volume label for this VPB matches that in the previous VPB.
|
|
3 - The system pointer to the real device object in the current VPB matches that in the same previous VPB.
|
|
4 - Finally the previous Vcb cannot be invalid or have a dismount underway.
|
|
If a VPB is found which matches these conditions, then the address of the Vcb for that VPB is returned via the pointer OldVcb.
|
|
|
|
Skip over the current Vcb.
|
|
|
|
Arguments:
|
|
Vcb - This is the Vcb we are checking for a remount.
|
|
OldVcb - A pointer to the address to store the address for the Vcb
|
|
for the volume if this is a remount. (This is a pointer to a pointer)
|
|
Return Value:
|
|
BOOLEAN - TRUE if this is in fact a remount, FALSE otherwise.
|
|
--*/
|
|
{
|
|
PLIST_ENTRY Link;
|
|
PVPB Vpb = Vcb->Vpb;
|
|
PVPB OldVpb;
|
|
BOOLEAN Remount = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Check input.
|
|
ASSERT_IRP_CONTEXT(IrpContext);
|
|
ASSERT_VCB(Vcb);
|
|
|
|
DebugTrace((+1, Dbg, "UdfIsRemount, Vcb %08x\n", Vcb));
|
|
|
|
for (Link = UdfData.VcbQueue.Flink; Link != &UdfData.VcbQueue; Link = Link->Flink) {
|
|
*OldVcb = CONTAINING_RECORD(Link, VCB, VcbLinks);
|
|
|
|
// Skip ourselves.
|
|
if (Vcb == *OldVcb) {
|
|
continue;
|
|
}
|
|
|
|
// Look at the Vpb and state of the previous Vcb.
|
|
OldVpb = (*OldVcb)->Vpb;
|
|
|
|
if ((OldVpb != Vpb) && (OldVpb->RealDevice == Vpb->RealDevice) && ((*OldVcb)->VcbCondition == VcbNotMounted)) {
|
|
// Go ahead and compare serial numbers and volume label.
|
|
if ((OldVpb->SerialNumber == Vpb->SerialNumber) &&
|
|
(Vpb->VolumeLabelLength == OldVpb->VolumeLabelLength) &&
|
|
(RtlEqualMemory(OldVpb->VolumeLabel, Vpb->VolumeLabel, Vpb->VolumeLabelLength))) {
|
|
// Got it.
|
|
DebugTrace((0, Dbg, "UdfIsRemount, matched OldVcb %08x\n", *OldVcb));
|
|
|
|
Remount = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
DebugTrace((-1, Dbg, "UdfIsRemount -> %c\n", (Remount ? 'T' : 'F')));
|
|
|
|
return Remount;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
NTSTATUS UdfFindFileSetDescriptor(IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb,
|
|
IN PLONGAD LongAd,
|
|
IN OUT PNSR_FSD *FileSetDescriptor)
|
|
/*++
|
|
Routine Description:
|
|
This routine walks a Fileset Descriptor Sequence looking for the default
|
|
descriptor. This will reveal the location of the root directory on the volume.
|
|
Arguments:
|
|
Vcb - Vcb of volume to search
|
|
LongAd - Long allocation descriptor describing the start of the sequence
|
|
FileSetDescriptor - Address of caller's pointer to an FSD
|
|
Return Value:
|
|
STATUS_SUCCESS if all descriptors are found, read, and are valid.
|
|
STATUS_DISK_CORRUPT_ERROR if corrupt/bad descriptors are found (may be raised)
|
|
--*/
|
|
{
|
|
PNSR_FSD FSD = NULL;
|
|
ULONGLONG Offset;
|
|
ULONG Lbn, Len;
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Check inputs
|
|
ASSERT_IRP_CONTEXT(IrpContext);
|
|
ASSERT(*FileSetDescriptor == NULL);
|
|
|
|
DebugTrace((+1, Dbg,
|
|
"UdfFindFileSetDescriptor, Vcb %08x, LongAd %08x %x/%08x +%08x (type %x)\n",
|
|
Vcb,
|
|
LongAd,
|
|
LongAd->Start.Partition,
|
|
LongAd->Start.Lbn,
|
|
LongAd->Length.Length,
|
|
LongAd->Length.Type));
|
|
|
|
// If the extent we begin from is not a whole number of recorded logical blocks, we can't continue.
|
|
|
|
#ifndef UDF_SUPPORT_NONSTANDARD_ALLSTOR
|
|
// Disable checking the sanity of the longad here.
|
|
// Reason: first drop of Allstor media recorded the type as unrecorded (!)
|
|
if (LongAd->Length.Length == 0 || LongAd->Length.Type != NSRLENGTH_TYPE_RECORDED || BlockOffset(Vcb, LongAd->Length.Length)) {
|
|
DebugTrace((+0, Dbg, "UdfFindFileSetDescriptor, bad longad length\n"));
|
|
DebugTrace((-1, Dbg, "UdfFindFileSetDescriptor -> STATUS_DISK_CORRUPT_ERROR\n"));
|
|
return STATUS_DISK_CORRUPT_ERROR;
|
|
}
|
|
#endif
|
|
|
|
// Use a try-finally for cleanup
|
|
try {
|
|
try {
|
|
for ( //
|
|
// Home ourselves in the search and make a pass through the sequence.
|
|
Len = LongAd->Length.Length,
|
|
Lbn = LongAd->Start.Lbn,
|
|
Offset = LlBytesFromSectors(Vcb, UdfLookupPsnOfExtent(IrpContext, Vcb, LongAd->Start.Partition, Lbn, Len));
|
|
|
|
Len;
|
|
|
|
// Advance to the next descriptor offset in the sequence.
|
|
Len -= BlockSize(Vcb),
|
|
Lbn++,
|
|
Offset += BlockSize(Vcb)) {
|
|
// Allocate a buffer to read fileset descriptors.
|
|
if (FSD == NULL) {
|
|
FSD = FsRtlAllocatePoolWithTag(UdfNonPagedPool, UdfRawBufferSize(Vcb, sizeof(NSR_FSD)), TAG_NSR_FSD);
|
|
}
|
|
|
|
Status = UdfReadSectors(IrpContext,
|
|
Offset,
|
|
UdfRawReadSize(Vcb, sizeof(NSR_FSD)),
|
|
TRUE,
|
|
FSD,
|
|
Vcb->TargetDeviceObject);
|
|
|
|
if (!NT_SUCCESS(Status) || FSD->Destag.Ident == DESTAG_ID_NOTSPEC) {
|
|
// These are both an excellent sign that this is an unrecorded sector, which
|
|
// is defined to terminate the sequence. (3/8.4.2)
|
|
break;
|
|
}
|
|
|
|
if ((FSD->Destag.Ident != DESTAG_ID_NSR_FSD &&
|
|
FSD->Destag.Ident != DESTAG_ID_NSR_TERM) ||
|
|
!UdfVerifyDescriptor(IrpContext, &FSD->Destag, FSD->Destag.Ident, sizeof(NSR_FSD), Lbn, TRUE)) {
|
|
// If we spot an illegal descriptor type in the stream, there is no reasonable
|
|
// way to guess that we can continue (the disc may be trash beyond this point).
|
|
// Clearly, we also cannot trust the next extent pointed to by a corrupt descriptor.
|
|
try_leave(Status = STATUS_DISK_CORRUPT_ERROR);
|
|
}
|
|
|
|
if (FSD->Destag.Ident == DESTAG_ID_NSR_TERM) {
|
|
// This is a way to terminate the sequence.
|
|
break;
|
|
}
|
|
|
|
// Reset the pointers to the possible next extent
|
|
LongAd = &FSD->NextExtent;
|
|
|
|
if (LongAd->Length.Length) {
|
|
// A fileset descriptor containing a nonzero next extent pointer also
|
|
// terminates this extent of the FSD sequence. (4/8.3.1)
|
|
|
|
// If the extent referred to is not fully recorded, this will terminate the sequence.
|
|
if (LongAd->Length.Type != NSRLENGTH_TYPE_RECORDED) {
|
|
break;
|
|
}
|
|
|
|
Len = LongAd->Length.Length;
|
|
|
|
// The extent must be a multiple of a block size.
|
|
if (BlockOffset(Vcb, Len)) {
|
|
DebugTrace((+0, Dbg, "UdfFindFileSetDescriptor, interior extent not blocksize in length\n"));
|
|
try_leave(Status = STATUS_DISK_CORRUPT_ERROR);
|
|
}
|
|
|
|
Lbn = LongAd->Start.Lbn;
|
|
|
|
Offset = LlBytesFromBlocks(Vcb, UdfLookupPsnOfExtent(IrpContext, Vcb, LongAd->Start.Partition, Lbn, Len));
|
|
}
|
|
|
|
UdfStoreFileSetDescriptorIfPrevailing(FileSetDescriptor, &FSD);
|
|
}
|
|
} finally{
|
|
// Free up the buffer space we may have allocated
|
|
UdfFreePool(&FSD);
|
|
}
|
|
} except(UdfExceptionFilter(IrpContext, GetExceptionInformation()))
|
|
{
|
|
// Transmute raised apparent file corruption to disk corruption - we are not
|
|
// yet touching the visible filesystem.
|
|
|
|
Status = IrpContext->ExceptionStatus;
|
|
|
|
DebugTrace((+0, Dbg, "UdfFindFileSetDescriptor, exception %08x thrown\n", Status));
|
|
|
|
if (Status == STATUS_FILE_CORRUPT_ERROR) {
|
|
DebugTrace((+0, Dbg, "UdfFindFileSetDescriptor, translating file corrupt to disk corrupt\n"));
|
|
Status = STATUS_DISK_CORRUPT_ERROR;
|
|
}
|
|
}
|
|
|
|
// Success is when we've really found something. If we failed to find the
|
|
// descriptor, commute whatever intermediate status was involved and clean up.
|
|
|
|
if (*FileSetDescriptor == NULL) {
|
|
Status = STATUS_UNRECOGNIZED_VOLUME;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
UdfFreePool(FileSetDescriptor);
|
|
}
|
|
|
|
DebugTrace((-1, Dbg, "UdfFindFileSetDescriptor -> %08x\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
NTSTATUS UdfFindVolumeDescriptors(IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb,
|
|
IN PEXTENTAD Extent,
|
|
IN OUT PPCB *Pcb,
|
|
IN OUT PNSR_PVD *PrimaryVolumeDescriptor,
|
|
IN OUT PNSR_LVOL *LogicalVolumeDescriptor)
|
|
/*++
|
|
Routine Description:
|
|
This routine walks the indicated Volume Descriptor Sequence searching for the
|
|
active descriptors for this volume and generates an initializing Pcb from the
|
|
referenced partitions. No updating of the Vcb occurs.
|
|
Arguments:
|
|
Vcb - Vcb of volume to search
|
|
Extent - Extent to search
|
|
Pcb - Address of a caller's pointer to a Pcb
|
|
PrimaryVolumeDescriptor - Address of caller's pointer to a PVD
|
|
LogicalVolumeDescriptor - Address of caller's pointer to an LVD
|
|
Return Value:
|
|
STATUS_SUCCESS if all descriptors are found, read, and are valid.
|
|
STATUS_DISK_CORRUPT_ERROR if corrupt descriptors are found.
|
|
STATUS_UNRECOGNIZED_VOLUME if noncompliant descriptors are found.
|
|
Descriptors are only returned on success.
|
|
--*/
|
|
{
|
|
PNSR_VD_GENERIC GenericVD = NULL;
|
|
ULONGLONG Offset;
|
|
ULONG Len;
|
|
ULONG UnitSize = UdfRawReadSize(Vcb, sizeof(NSR_VD_GENERIC));
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG ThisPass = 1;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Check the input parameters
|
|
ASSERT_IRP_CONTEXT(IrpContext);
|
|
ASSERT_VCB(Vcb);
|
|
ASSERT_OPTIONAL_PCB(*Pcb);
|
|
|
|
DebugTrace((+1, Dbg, "UdfFindVolumeDescriptors, Vcb %08x, Extent %08x +%08x\n", Vcb, Extent->Lsn, Extent->Len));
|
|
|
|
// If the extent we begin from is not at least the size of an aligned descriptor
|
|
// or is sized in base units other than aligned descriptors, we can't continue.
|
|
if (Extent->Len < UnitSize || Extent->Len % UnitSize) {
|
|
DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, Base extent length %08x is mismatched with read size %08x\n", Extent->Len, UnitSize));
|
|
DebugTrace((-1, Dbg, "UdfFindVolumeDescriptors -> STATUS_DISK_CORRUPT_ERROR\n"));
|
|
return STATUS_DISK_CORRUPT_ERROR;
|
|
}
|
|
|
|
// Use a try-finally to facilitate cleanup.
|
|
try {
|
|
DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, starting pass 1, find LVD/PVD\n"));
|
|
|
|
// We will make at least one pass through the Volume Descriptor Sequence to find
|
|
// the prevailing versions of the two controlling descriptors - the PVD and LVD.
|
|
// In order to avoid picking up partition descriptors that aren't actually going
|
|
// to be referenced by the LVD, we will pick them up in a second pass if we find
|
|
// a PVD and LVD that look reasonable and then stick them in a Pcb.
|
|
for (ThisPass = 1; ThisPass <= 2; ThisPass++) {
|
|
for ( //
|
|
// Home ourselves in the search and make a pass through the sequence.
|
|
Offset = LlBytesFromSectors(Vcb, Extent->Lsn),
|
|
Len = Extent->Len;
|
|
|
|
// If we have reached the end of the extent's indicated valid
|
|
// length, we are done. This usually will not happen.
|
|
Len;
|
|
|
|
// Advance to the next descriptor offset in the sequence.
|
|
Offset += UnitSize,
|
|
Len -= UnitSize) {
|
|
// Allocate a buffer to read generic volume descriptors.
|
|
if (GenericVD == NULL) {
|
|
GenericVD = (PNSR_VD_GENERIC)FsRtlAllocatePoolWithTag(UdfNonPagedPool, UdfRawBufferSize(Vcb, sizeof(NSR_VD_GENERIC)), TAG_NSR_VDSD);
|
|
}
|
|
|
|
Status = UdfReadSectors(IrpContext, Offset, UnitSize, TRUE, GenericVD, Vcb->TargetDeviceObject);
|
|
|
|
// Thise is a decent sign that this is an unrecorded sector and is defined to terminate the sequence.
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
if (GenericVD->Destag.Ident > DESTAG_ID_MAXIMUM_PART3 ||
|
|
!UdfVerifyDescriptor(IrpContext,
|
|
&GenericVD->Destag,
|
|
GenericVD->Destag.Ident,
|
|
sizeof(NSR_VD_GENERIC),
|
|
(ULONG)SectorsFromBytes(Vcb, Offset),
|
|
TRUE)) {
|
|
// If we spot an illegal descriptor type in the stream, there is no reasonable
|
|
// way to guess that we can continue (the disc may be trash beyond this point).
|
|
// Likewise, even if we have a single corrupt descriptor we cannot continue because
|
|
// this may be corruption of a descriptor we may have otherwise required for operation
|
|
// (i.e., one of the prevailing descriptors).
|
|
|
|
DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, descriptor didn't verify\n"));
|
|
try_leave(Status = STATUS_DISK_CORRUPT_ERROR);
|
|
}
|
|
|
|
if (GenericVD->Destag.Ident == DESTAG_ID_NSR_TERM) {
|
|
// The Terminating Descriptor (3/10.9) is the usual way to stop a search.
|
|
break;
|
|
}
|
|
|
|
if (GenericVD->Destag.Ident == DESTAG_ID_NSR_VDP) {
|
|
// Follow a Volume Desciptor Pointer (3/10.3) to the next extent of the sequence.
|
|
Offset = LlBytesFromSectors(Vcb, ((PNSR_VDP)GenericVD)->Next.Lsn);
|
|
Len = ((PNSR_VDP)GenericVD)->Next.Len;
|
|
|
|
// We cannot do anything if the extent is invalid
|
|
if (Len < UnitSize || Len % UnitSize) {
|
|
DebugTrace((0, Dbg,
|
|
"UdfFindVolumeDescriptors, following extent length %08x is mismatched with read size %08x\n",
|
|
Extent->Len,
|
|
UnitSize));
|
|
|
|
try_leave(Status = STATUS_DISK_CORRUPT_ERROR);
|
|
}
|
|
}
|
|
|
|
DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, descriptor tag %08x\n", GenericVD->Destag.Ident));
|
|
|
|
if (ThisPass == 1) {
|
|
// Our first pass is to find prevailing LVD and PVD.
|
|
switch (GenericVD->Destag.Ident) {
|
|
case DESTAG_ID_NSR_PVD:
|
|
UdfStoreVolumeDescriptorIfPrevailing((PNSR_VD_GENERIC *)PrimaryVolumeDescriptor, GenericVD);
|
|
break;
|
|
case DESTAG_ID_NSR_LVOL:
|
|
UdfStoreVolumeDescriptorIfPrevailing((PNSR_VD_GENERIC *)LogicalVolumeDescriptor, GenericVD);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
PNSR_PART PartitionDescriptor = (PNSR_PART)GenericVD;
|
|
|
|
// Our second pass is to pick up all relavent NSR02 PD
|
|
if (PartitionDescriptor->Destag.Ident != DESTAG_ID_NSR_PART ||
|
|
!UdfEqualEntityId(&PartitionDescriptor->ContentsID, &UdfNSR02Identifier, NULL)) {
|
|
continue;
|
|
}
|
|
|
|
UdfAddToPcb(*Pcb, (PNSR_PART)GenericVD);
|
|
}
|
|
}
|
|
|
|
// Now that a pass through the VDS has been completed, analyze the results.
|
|
if (ThisPass == 1) {
|
|
PNSR_PVD PVD;
|
|
PNSR_LVOL LVD;
|
|
|
|
// Reference the descriptors for ease of use
|
|
PVD = *PrimaryVolumeDescriptor;
|
|
LVD = *LogicalVolumeDescriptor;
|
|
|
|
// Check that the descriptors indicate a logical volume which appears to be a valid UDF volume.
|
|
if ((PVD == NULL && DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, don't have a PVD\n"))) ||
|
|
(LVD == NULL && DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, don't have an LVD\n"))) ||
|
|
// Now check the PVD
|
|
|
|
// The Volume Set Sequence fields indicates how many volumes form
|
|
// the volume set and what number this volume is in that sequence.
|
|
// We are a level 2 implementation, meaning that the volumes we read consist of a single volume. (3/11)
|
|
(PVD->VolSetSeq > 1 && DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, PVD VolSetSeq %08x - not volume 1 of a volume set\n", PVD->VolSetSeq))) ||
|
|
(PVD->VolSetSeqMax > 1 && DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, PVD VolSetSeqMax %08x - volume in a non-unit volume set\n", PVD->VolSetSeqMax))) ||
|
|
|
|
#ifndef UDF_SUPPORT_NONSTANDARD_ALLSTOR
|
|
// Disable checking of character set lists.
|
|
// Reason: first drop of Allstor media recorded these fields as 0x0.
|
|
|
|
// Insure that Character Set Lists conform to UDF
|
|
(PVD->CharSetList != UDF_CHARSETLIST &&
|
|
DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, PVD CharSetList %08x != CS0 only\n", PVD->CharSetList))) ||
|
|
(PVD->CharSetListMax != UDF_CHARSETLIST &&
|
|
DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, PVD CharSetListMax %08x != CS0 only\n", PVD->CharSetListMax))) ||
|
|
|
|
// Disable checking of character set lists.
|
|
// Reason: first drop of Allstor media misspelled "Compressed" as "Copmressed"
|
|
|
|
// The two character sets must be UDF CS0. CS0 is a "by convention"
|
|
// character set in ISO 13346, which UDF specifies for our domain.
|
|
(!UdfEqualCharspec(&PVD->CharsetDesc, &UdfCS0Identifier, CHARSPEC_T_CS0) &&
|
|
DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, PVD CharsetDesc != CS0 only\n"))) ||
|
|
(!UdfEqualCharspec(&PVD->CharsetExplan, &UdfCS0Identifier, CHARSPEC_T_CS0) &&
|
|
DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, PVD CharsetExplan != CS0 only\n"))) ||
|
|
#endif
|
|
|
|
// Now check the LVD
|
|
|
|
// The LVD is a variant sized structure. Check that the claimed size fits in a single
|
|
// logical sector. Although an LVD may legally exceed a single sector, we will never
|
|
// want to deal with such a volume.
|
|
(ISONsrLvolSize(LVD) > SectorSize(Vcb) &&
|
|
DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, LVD is bigger than a sector\n"))) ||
|
|
|
|
#ifndef UDF_SUPPORT_NONSTANDARD_ALLSTOR
|
|
// Disable checking of character set lists.
|
|
// Reason: first drop of Allstor media recorded these fields as 0x0.
|
|
|
|
// The character set used in the LVD must be UDF CS0 as well.
|
|
|
|
(!UdfEqualCharspec(&LVD->Charset, &UdfCS0Identifier, CHARSPEC_T_CS0) &&
|
|
DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, LVD Charset != CS0 only\n"))) ||
|
|
#endif
|
|
|
|
// The specified block size must equal the physical sector size.
|
|
(LVD->BlockSize != SectorSize(Vcb) &&
|
|
DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, LVD BlockSize %08x != SectorSize %08x\n"))) ||
|
|
|
|
// The domain must be within the version we read
|
|
(!UdfDomainIdentifierContained(&LVD->DomainID, &UdfDomainIdentifier, UDF_VERSION_MINIMUM, UDF_VERSION_RECOGNIZED) &&
|
|
DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, domain ID indicates unreadable volume\n"))) ||
|
|
|
|
// Although we can handle any number of partitions, UDF only specifies
|
|
// a single partition or special dual partition formats.
|
|
(LVD->MapTableCount > 2 &&
|
|
DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, LVD MapTableCount %08x greater than allowed (2)\n", LVD->MapTableCount)))
|
|
) {
|
|
DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, ... so returning STATUS_UNRECOGNIZED_VOLUME\n"));
|
|
try_leave(Status = STATUS_UNRECOGNIZED_VOLUME);
|
|
}
|
|
|
|
// Now that we have performed the simple field checks, build a Pcb.
|
|
Status = UdfInitializePcb(IrpContext, Vcb, Pcb, LVD);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, Pcb intialization failed (!)\n"));
|
|
try_leave(Status);
|
|
}
|
|
}
|
|
|
|
// Go onto Pass 2 to find the Partition Descriptors
|
|
DebugTrace((0, Dbg, "UdfFindVolumeDescriptors, starting pass 2, find associated PD\n"));
|
|
}
|
|
} finally{
|
|
DebugUnwind("UdfFindVolumeDescriptors");
|
|
|
|
// Free up the buffer space we may have allocated
|
|
UdfFreePool(&GenericVD);
|
|
}
|
|
|
|
DebugTrace((-1, Dbg, "UdfFindVolumeDescriptors -> %08x\n", Status));
|
|
|
|
// Success is when we've really found something. If we failed to find both
|
|
// descriptors, commute whatever intermediate status was involved and clean up.
|
|
if (*PrimaryVolumeDescriptor == NULL || *LogicalVolumeDescriptor == NULL) {
|
|
Status = STATUS_UNRECOGNIZED_VOLUME;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
UdfFreePool(PrimaryVolumeDescriptor);
|
|
UdfFreePool(LogicalVolumeDescriptor);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
NTSTATUS UdfFindAnchorVolumeDescriptor(IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN OUT PNSR_ANCHOR *AnchorVolumeDescriptor)
|
|
/*++
|
|
Routine Description:
|
|
This routine will find the Anchor Volume Descriptor for a piece of media
|
|
Arguments:
|
|
Vcb - Vcb of volume to search
|
|
AnchorVolumeDescriptor - Caller's pointer to an AVD
|
|
Return Value:
|
|
Boolean TRUE if AVD is discovered, FALSE otherwise.
|
|
--*/
|
|
{
|
|
ULONG ThisPass;
|
|
ULONG ReadLsn;
|
|
ULONG Lsn;
|
|
BOOLEAN Found = FALSE;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Check the input parameters
|
|
ASSERT_IRP_CONTEXT(IrpContext);
|
|
ASSERT_VCB(Vcb);
|
|
ASSERT(*AnchorVolumeDescriptor == NULL);
|
|
|
|
// Discover the Anchor Volume Descriptor, which will point towards the
|
|
// Volume Set Descriptor Sequence. The AVD may exist at sector 256 or
|
|
// in the last sector of the volume.
|
|
*AnchorVolumeDescriptor = (PNSR_ANCHOR)FsRtlAllocatePoolWithTag(UdfNonPagedPool, UdfRawBufferSize(Vcb, sizeof(NSR_ANCHOR)), TAG_NSR_VDSD);
|
|
|
|
// Search the three possible locations for an AVD to exist on the volume, plus check for the possibility of a method 2 fixup requirement.
|
|
for (ThisPass = 1; ThisPass <= 4; ThisPass++) {
|
|
if (ThisPass == 1) {
|
|
ReadLsn = Lsn = ANCHOR_SECTOR + Vcb->BoundS;
|
|
} else if (ThisPass == 2) {
|
|
// It is so unlikely that we will get a disk that doesn't have
|
|
// an anchor at 256 that this is a pretty good indication we
|
|
// have a CD-RW here and the drive is method 2 goofy. Take a shot.
|
|
ReadLsn = UdfMethod2TransformSector(Vcb, ANCHOR_SECTOR);
|
|
Lsn = ANCHOR_SECTOR;
|
|
} else if (ThisPass == 3) {
|
|
// Our remaining two chances depend on being able to determine
|
|
// the last recorded sector for the volume. If we were unable to do this, stop.
|
|
if (!Vcb->BoundN) {
|
|
break;
|
|
}
|
|
|
|
ReadLsn = Lsn = Vcb->BoundN;
|
|
} else if (ThisPass == 4) {
|
|
ReadLsn = Lsn = Vcb->BoundN - ANCHOR_SECTOR;
|
|
}
|
|
|
|
// We may have more chances to succeed if failure occurs.
|
|
Status = UdfReadSectors(IrpContext,
|
|
LlBytesFromSectors(Vcb, ReadLsn),
|
|
UdfRawReadSize(Vcb, sizeof(NSR_ANCHOR)),
|
|
TRUE,
|
|
*AnchorVolumeDescriptor,
|
|
Vcb->TargetDeviceObject);
|
|
if (!NT_SUCCESS(Status)) {
|
|
continue;
|
|
}
|
|
|
|
if (!UdfVerifyDescriptor(IrpContext, &(*AnchorVolumeDescriptor)->Destag, DESTAG_ID_NSR_ANCHOR, sizeof(NSR_ANCHOR), Lsn, TRUE)) {
|
|
continue;
|
|
}
|
|
|
|
// Got one! Set the method 2 fixup appropriately.
|
|
if (ThisPass == 2) {
|
|
DebugTrace((0, Dbg, "********\n"));
|
|
DebugTrace((0, Dbg, "METHOD 2 FIXUPS ACTIVATED FOR Vcb @ %08x\n", Vcb));
|
|
DebugTrace((0, Dbg, "********\n"));
|
|
SetFlag(Vcb->VcbState, VCB_STATE_METHOD_2_FIXUP);
|
|
} else {
|
|
ClearFlag(Vcb->VcbState, VCB_STATE_METHOD_2_FIXUP);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
return STATUS_UNRECOGNIZED_VOLUME;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
BOOLEAN UdfRecognizeVolume(IN PIRP_CONTEXT IrpContext, IN PDEVICE_OBJECT DeviceObject, IN ULONG SectorSize, IN OUT PBOOLEAN Bridge)
|
|
/*++
|
|
Routine Description:
|
|
This routine walks the Volume Recognition Sequence to determine
|
|
whether this volume contains an NSR02 (ISO 13346 Section 4) image.
|
|
Arguments:
|
|
DeviceObject - device we are checking
|
|
SectorSize - size of a physical sector on this device
|
|
Bridge - will return whether there appear to be ISO 9660 structures on the media
|
|
Return Value:
|
|
Boolean TRUE if we found NSR02, FALSE otherwise.
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN FoundBEA = FALSE;
|
|
BOOLEAN FoundNSR = FALSE;
|
|
BOOLEAN Resolved = FALSE;
|
|
PVSD_GENERIC VolumeStructureDescriptor;
|
|
ULONGLONG Offset = SectorAlignN(SectorSize, VRA_BOUNDARY_LOCATION);
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_IRP_CONTEXT(IrpContext);// Check the input parameters
|
|
|
|
VolumeStructureDescriptor = (PVSD_GENERIC)FsRtlAllocatePoolWithTag(UdfNonPagedPool, UdfRawBufferSizeN(SectorSize, sizeof(VSD_GENERIC)), TAG_NSR_VSD);
|
|
|
|
DebugTrace((+1, Dbg, "UdfRecognizeVolume, DevObj %08x SectorSize %08x\n", DeviceObject, SectorSize));
|
|
|
|
// Use try-finally to facilitate cleanup
|
|
try {
|
|
#ifdef UDF_SUPPORT_NONSTANDARD_ADAPTEC
|
|
// Disable checking the recognition area.
|
|
// Reasons:
|
|
// ADAPTEC - early CDUDF did not bound NSR02 with BEA/TEA, instead sticking it in the middle of the ISO 9660 sequence.
|
|
Resolved = TRUE;
|
|
#endif
|
|
|
|
while (!Resolved) {
|
|
Status = UdfReadSectors(IrpContext,
|
|
Offset,
|
|
UdfRawReadSizeN(SectorSize, sizeof(VSD_GENERIC)),
|
|
FALSE,
|
|
VolumeStructureDescriptor,
|
|
DeviceObject);
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
// Now check the type of the descriptor. All ISO 13346 VSDs are
|
|
// of Type 0, 9660 PVDs are Type 1, 9660 SVDs are Type 2, and 9660
|
|
// terminating descriptors are Type 255.
|
|
if (VolumeStructureDescriptor->Type == 0) {
|
|
// In order to properly recognize the volume, we must know all of the
|
|
// Structure identifiers in ISO 13346 so that we can terminate if a
|
|
// badly formatted (or, shockingly, non 13346) volume is presented to us.
|
|
switch (UdfFindInParseTable(VsdIdentParseTable, VolumeStructureDescriptor->Ident, VSD_LENGTH_IDENT)) {
|
|
case VsdIdentBEA01:
|
|
// Only one BEA may exist and its version must be 1 (2/9.2.3)
|
|
DebugTrace((0, Dbg, "UdfRecognizeVolume, got a BEA01\n"));
|
|
|
|
if ((FoundBEA &&
|
|
DebugTrace((0, Dbg, "UdfRecognizeVolume, ... but it is a duplicate!\n"))) ||
|
|
(VolumeStructureDescriptor->Version != 1 &&
|
|
DebugTrace((0, Dbg, "UdfRecognizeVolume, ... but it has a wacky version number %02x != 1!\n", VolumeStructureDescriptor->Version)))) {
|
|
Resolved = TRUE;
|
|
break;
|
|
}
|
|
|
|
FoundBEA = TRUE;
|
|
break;
|
|
case VsdIdentTEA01:
|
|
// If we reach the TEA it must be the case that we don't recognize
|
|
DebugTrace((0, Dbg, "UdfRecognizeVolume, got a TEA01\n"));
|
|
Resolved = TRUE;
|
|
break;
|
|
case VsdIdentNSR02:
|
|
// We recognize NSR02 version 1 embedded after a BEA (3/9.1.3). For
|
|
// simplicity we will not bother being a complete nitpick and check
|
|
// for a bounding TEA, although we will be optimistic in the case where we fail to match the version.
|
|
DebugTrace((0, Dbg, "UdfRecognizeVolume, got an NSR02\n"));
|
|
|
|
if ((FoundBEA ||
|
|
!DebugTrace((0, Dbg, "UdfRecognizeVolume, ... but we haven't seen a BEA01 yet!\n"))) &&
|
|
(VolumeStructureDescriptor->Version == 1 ||
|
|
!DebugTrace((0, Dbg, "UdfRecognizeVolume, ... but it has a wacky version number %02x != 1\n", VolumeStructureDescriptor->Version)))) {
|
|
FoundNSR = Resolved = TRUE;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
case VsdIdentCD001:
|
|
case VsdIdentCDW01:
|
|
case VsdIdentNSR01:
|
|
case VsdIdentCDW02:
|
|
case VsdIdentBOOT2:
|
|
DebugTrace((0, Dbg, "UdfRecognizeVolume, got a valid but uninteresting 13346 descriptor\n"));
|
|
|
|
// Valid but uninteresting (to us) descriptors
|
|
break;
|
|
default:
|
|
DebugTrace((0, Dbg, "UdfRecognizeVolume, got an invalid 13346 descriptor\n"));
|
|
|
|
// Stumbling across something we don't know, it must be that this is not a valid 13346 image
|
|
Resolved = TRUE;
|
|
break;
|
|
}
|
|
} else if (!FoundBEA && (VolumeStructureDescriptor->Type < 3 || VolumeStructureDescriptor->Type == 255)) {
|
|
DebugTrace((0, Dbg, "UdfRecognizeVolume, got a 9660 descriptor\n"));
|
|
|
|
// Only HSG (CDROM) and 9660 (CD001) are possible, and they are only legal before the ISO 13346 BEA/TEA extent.
|
|
// By design, an ISO 13346 VSD precisely overlaps a 9660 PVD/SVD in the appropriate fields.
|
|
|
|
// Note that we aren't being strict about the structure of the 9660 descriptors
|
|
// since that really isn't very interesting. We care more about the 13346.
|
|
switch (UdfFindInParseTable(VsdIdentParseTable, VolumeStructureDescriptor->Ident, VSD_LENGTH_IDENT)) {
|
|
case VsdIdentCDROM:
|
|
case VsdIdentCD001:
|
|
DebugTrace((0, Dbg, "UdfRecognizeVolume, ... seems we have 9660 here\n"));
|
|
|
|
// Note to our caller that we seem to have ISO 9660 here
|
|
*Bridge = TRUE;
|
|
break;
|
|
default:
|
|
DebugTrace((0, Dbg, "UdfRecognizeVolume, ... but it looks wacky\n"));
|
|
|
|
// This probably was a false alert, but in any case there is nothing on this volume for us.
|
|
Resolved = TRUE;
|
|
break;
|
|
}
|
|
} else {
|
|
// Something else must be recorded on this volume.
|
|
DebugTrace((0, Dbg, "UdfRecognizeVolume, got an unrecognizeable descriptor, probably not 13346/9660\n"));
|
|
break;
|
|
}
|
|
|
|
// Align our next read with the sector following the current descriptor
|
|
Offset += SectorAlignN(SectorSize, sizeof(VSD_GENERIC));
|
|
}
|
|
} finally{
|
|
DebugUnwind("UdfRecognizeVolume");
|
|
|
|
UdfFreePool(&VolumeStructureDescriptor);// Free up our temporary buffer
|
|
|
|
if (AbnormalTermination()) {
|
|
// Commute a status we raised for empty devices so that other filesystems can have a crack at this.
|
|
if (UdfIsRawDevice(IrpContext, IrpContext->ExceptionStatus)) {
|
|
IrpContext->ExceptionStatus = STATUS_UNRECOGNIZED_VOLUME;
|
|
}
|
|
}
|
|
}
|
|
|
|
DebugTrace((-1, Dbg, "UdfRecognizeVolume -> %u\n", FoundNSR));
|
|
|
|
return FoundNSR;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
VOID UdfScanForDismountedVcb(IN PIRP_CONTEXT IrpContext)
|
|
/*++
|
|
Routine Description:
|
|
This routine walks through the list of Vcb's looking for any which may
|
|
now be deleted. They may have been left on the list because there were outstanding references.
|
|
--*/
|
|
{
|
|
PVCB Vcb;
|
|
PLIST_ENTRY Links;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Check input.
|
|
ASSERT_IRP_CONTEXT(IrpContext);
|
|
ASSERT_EXCLUSIVE_UDFDATA;
|
|
|
|
// Walk through all of the Vcb's attached to the global data.
|
|
Links = UdfData.VcbQueue.Flink;
|
|
while (Links != &UdfData.VcbQueue) {
|
|
Vcb = CONTAINING_RECORD(Links, VCB, VcbLinks);
|
|
|
|
// Move to the next link now since the current Vcb may be deleted.
|
|
Links = Links->Flink;
|
|
|
|
// If dismount is already underway then check if this Vcb can go away.
|
|
if ((Vcb->VcbCondition == VcbDismountInProgress) ||
|
|
(Vcb->VcbCondition == VcbInvalid) ||
|
|
((Vcb->VcbCondition == VcbNotMounted) && (Vcb->VcbReference <= Vcb->VcbResidualReference))) {
|
|
UdfCheckForDismount(IrpContext, Vcb, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID UdfDetermineVolumeBounding(IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PULONG S, IN PULONG N)
|
|
/*++
|
|
Routine Description:
|
|
This routine will figure out where the base offset to discover volume descriptors
|
|
lies and where the end of the disc is. In the case where this is a non-CD media,
|
|
this will tend to not to set the end bound since there is no uniform way to figure
|
|
that piece of information out.
|
|
|
|
The bounding information is used to start the hunt for CD-UDF (UDF 1.5) volumes.
|
|
Anyone who puts CD-UDF on non-CD media deserves what they get.
|
|
Arguments:
|
|
Vcb - the volume we are operating on
|
|
S - an address to store the start of the volume for the purposes of finding descriptors
|
|
N - an address to store the end of the volume for the purposes of finding descriptors
|
|
Return Value:
|
|
None.
|
|
|
|
Benign inability find the S/N information will result in 0/0 being returned.
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PCDROM_TOC CdromToc;
|
|
PTRACK_DATA TrackData;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Check input.
|
|
ASSERT_IRP_CONTEXT(IrpContext);
|
|
ASSERT_VCB(Vcb);
|
|
|
|
// Allocate a buffer for the last session information.
|
|
CdromToc = FsRtlAllocatePoolWithTag(UdfPagedPool, sizeof(CDROM_TOC), TAG_CDROM_TOC);
|
|
RtlZeroMemory(CdromToc, sizeof(CDROM_TOC));
|
|
DebugTrace((+1, Dbg, "UdfDetermineVolumeBounding, Vcb %08x S %08x N %08x\n", Vcb, S, N));
|
|
|
|
// Whack the inputs to the benign state.
|
|
*S = *N = 0;
|
|
|
|
// Try to retrieve the CDROM last session information.
|
|
try {
|
|
// Pull up the TOC. The information for track AA (start of leadout)
|
|
// will get us the end of disc within some tolerance dependent on how
|
|
// much the device manufacturer paid attention to specifications.
|
|
// (-152, -150, -2, and 0 are possible offsets to the real end).
|
|
|
|
Status = UdfPerformDevIoCtrl(IrpContext,
|
|
IOCTL_CDROM_READ_TOC,
|
|
Vcb->TargetDeviceObject,
|
|
CdromToc,
|
|
sizeof(CDROM_TOC),
|
|
FALSE,
|
|
TRUE,
|
|
NULL);
|
|
|
|
// Raise an exception if there was an allocation failure.
|
|
if (Status == STATUS_INSUFFICIENT_RESOURCES) {
|
|
DebugTrace((0, Dbg, "UdfDetermineVolumeBounding, READ_TOC failed INSUFFICIENT_RESOURCES\n"));
|
|
UdfRaiseStatus(IrpContext, Status);
|
|
}
|
|
|
|
// For other errors, just fail. Perhaps this will turn out to be benign, in any case
|
|
// the mount will rapidly and correctly fail if it really was dependant on this work.
|
|
if (!NT_SUCCESS(Status)) {
|
|
try_leave(NOTHING);
|
|
}
|
|
|
|
// Sanity chck that the TOC is well-bounded.
|
|
if (CdromToc->LastTrack - CdromToc->FirstTrack >= MAXIMUM_NUMBER_TRACKS) {
|
|
DebugTrace((0, Dbg, "UdfDetermineVolumeBounding, TOC malf (too many tracks)\n"));
|
|
try_leave(NOTHING);
|
|
}
|
|
|
|
TrackData = &CdromToc->TrackData[(CdromToc->LastTrack - CdromToc->FirstTrack + 1)];
|
|
|
|
#if 0
|
|
// Better be AA ...
|
|
if (TrackData->TrackNumber != 0xaa) {
|
|
DebugTrace((0, Dbg, "UdfDetermineVolumeBounding, TOC malf (aa not last)\n"));
|
|
try_leave(NOTHING);
|
|
}
|
|
#endif
|
|
|
|
// Now, find the AA info and convert MSF to a logical block address. 75 frames/sectors
|
|
// per second, 60 seconds per minute. The MSF address is stored LSB (the F byte) high in the word.
|
|
|
|
// NOTE: MSF is only capable of representing 256*(256+256*60)*75 = 0x11ce20 sectors.
|
|
// This is 2.3gb, much less than the size of DVD media, which will respond to CDROM_TOC.
|
|
// Caveat user.
|
|
|
|
*N = (TrackData->Address[3] + (TrackData->Address[2] + TrackData->Address[1] * 60) * 75) - 1;
|
|
|
|
// We must bias back by 0/2/0 MSF since that is the defined location of sector 0. This
|
|
// works out to 150 sectors.
|
|
|
|
if (*N <= 150) {
|
|
*N = 0;
|
|
try_leave(NOTHING);
|
|
}
|
|
|
|
*N -= 150;
|
|
|
|
// Query the last session information from the driver.
|
|
Status = UdfPerformDevIoCtrl(IrpContext,
|
|
IOCTL_CDROM_GET_LAST_SESSION,
|
|
Vcb->TargetDeviceObject,
|
|
CdromToc,
|
|
sizeof(CDROM_TOC),
|
|
FALSE,
|
|
TRUE,
|
|
NULL);
|
|
|
|
// Raise an exception if there was an allocation failure.
|
|
if (Status == STATUS_INSUFFICIENT_RESOURCES) {
|
|
DebugTrace((0, Dbg, "UdfDetermineVolumeBounding, GET_LAST_SESSION failed INSUFFICIENT_RESOURCES\n"));
|
|
UdfRaiseStatus(IrpContext, Status);
|
|
}
|
|
|
|
// Now, if we got anything interesting out of this try, return it. If this
|
|
// failed for any other reason, we don't really care - it just means that
|
|
// if this was CDUDF media, we're gonna fail to figure it out pretty quickly.
|
|
|
|
// Life is tough.
|
|
|
|
if (NT_SUCCESS(Status) && CdromToc->FirstTrack != CdromToc->LastTrack) {
|
|
// The 0 entry in TrackData tells us about the start of the session as a logical block address.
|
|
SwapCopyUchar4(S, &CdromToc->TrackData[0].Address);
|
|
|
|
// Save grief if the session info is screwed up.
|
|
if (*N <= *S) {
|
|
DebugTrace((0, Dbg, "UdfDetermineVolumeBounding, N before S, whacking both back!\n"));
|
|
*S = *N = 0;
|
|
}
|
|
}
|
|
|
|
DebugTrace((0, Dbg, "UdfDetermineVolumeBounding, S %08x N %08x\n", *S, *N));
|
|
} finally{
|
|
DebugUnwind("UdfDetermineVolumeBounding");
|
|
if (CdromToc != NULL) {
|
|
UdfFreePool(&CdromToc);
|
|
}
|
|
}
|
|
|
|
DebugTrace((-1, Dbg, "UdfDetermineVolumeBounding -> VOID\n"));
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
VOID UdfUpdateVolumeLabel(IN PIRP_CONTEXT IrpContext,
|
|
IN PWCHAR VolumeLabel,
|
|
IN OUT PUSHORT VolumeLabelLength,
|
|
IN PUCHAR Dstring,
|
|
IN UCHAR FieldLength)
|
|
/*++
|
|
Routine Description:
|
|
This routine will retrieve an NT volume label from a logical volume descriptor.
|
|
Arguments:
|
|
VolumeLabel - a volume label to fill in.
|
|
VolumeLabelLength - returns the length of the returned volume label.
|
|
Dstring - the dstring field containing the volume id.
|
|
FieldLength - the length of the dstring field.
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
BOOLEAN Result;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_IRP_CONTEXT(IrpContext);// Check inputs.
|
|
|
|
DebugTrace((+1, Dbg,
|
|
"UdfUpdateVolumeLabel, Label %08x, Dstring %08x FieldLength %02x\n",
|
|
VolumeLabel,
|
|
Dstring,
|
|
FieldLength));
|
|
|
|
// Check that the dstring is usable as a volume identification.
|
|
Result = UdfCheckLegalCS0Dstring(IrpContext, Dstring, 0, FieldLength, TRUE);
|
|
|
|
// Update the label directly if the dstring is good.
|
|
if (Result) {
|
|
UNICODE_STRING TemporaryUnicodeString;
|
|
|
|
TemporaryUnicodeString.Buffer = VolumeLabel;
|
|
TemporaryUnicodeString.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH;
|
|
TemporaryUnicodeString.Length = 0;
|
|
|
|
UdfConvertCS0DstringToUnicode(IrpContext, Dstring, 0, FieldLength, &TemporaryUnicodeString);
|
|
|
|
// Now retrieve the name for return to the caller.
|
|
RtlCopyMemory(VolumeLabel, TemporaryUnicodeString.Buffer, TemporaryUnicodeString.Length);
|
|
*VolumeLabelLength = TemporaryUnicodeString.Length;
|
|
|
|
DebugTrace((0, Dbg, "UdfUpdateVolumeLabel, Labeled as \"%wZ\"\n", &TemporaryUnicodeString));
|
|
|
|
// Treat as label.
|
|
} else {
|
|
*VolumeLabelLength = 0;
|
|
|
|
DebugTrace((0, Dbg, "UdfUpdateVolumeLabel, invalid label.\n"));
|
|
}
|
|
|
|
DebugTrace((-1, Dbg, "UdfUpdateVolumeLabel -> VOID\n"));
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
VOID UdfUpdateVolumeSerialNumber(IN PIRP_CONTEXT IrpContext, IN OUT PULONG VolumeSerialNumber, IN PNSR_FSD Fsd)
|
|
/*++
|
|
Routine Description:
|
|
This routine will compute the volume serial number for a set of descriptors.
|
|
Arguments:
|
|
VolumeSerialNumber - returns the volume serial number corresponding to these descriptors.
|
|
Fsd - the fileset descriptor to examine.
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
ULONG VsnLe;
|
|
PAGED_CODE();
|
|
|
|
ASSERT_IRP_CONTEXT(IrpContext);// Check input.
|
|
|
|
// The serial number is just off of the FSD. This matches Win9x.
|
|
VsnLe = UdfSerial32((PCHAR)Fsd, sizeof(NSR_FSD));
|
|
SwapCopyUchar4(VolumeSerialNumber, &VsnLe);
|
|
}
|