4921 lines
134 KiB
C
4921 lines
134 KiB
C
/*++
|
||
|
||
Copyright (c) 1996-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
FsCtrl.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the File System Control routines for Udfs called
|
||
by the Fsd/Fsp dispatch drivers.
|
||
|
||
// @@BEGIN_DDKSPLIT
|
||
|
||
Author:
|
||
|
||
Dan Lovinger [DanLo] 11-Jun-1996
|
||
|
||
Revision History:
|
||
|
||
Tom Jolly [TomJolly] 1-March-2000 UDF 2.01 support
|
||
|
||
// @@END_DDKSPLIT
|
||
|
||
--*/
|
||
|
||
#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;
|
||
|
||
//
|
||
// CRC of the PVD on Disney's Snow White title, so we can
|
||
// ignore the volsetseqmax on that disc only.
|
||
//
|
||
|
||
#define UDF_SNOW_WHITE_PVD_CRC ((USHORT)0x1d05)
|
||
#define UDF_SNOW_WHITE_PVD_CRC_VARIANT_2 ((USHORT)0x534e)
|
||
|
||
//
|
||
// 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 PDEVICE_OBJECT TargetDeviceObject,
|
||
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
|
||
);
|
||
|
||
VOID
|
||
UdfRemountOldVcb(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB OldVcb,
|
||
IN PVCB NewVcb,
|
||
IN PDEVICE_OBJECT DeviceObjectWeTalkTo
|
||
);
|
||
|
||
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 PULONG BoundS,
|
||
IN OUT PBOOLEAN Bridge,
|
||
OUT PUSHORT NSRVerFound
|
||
);
|
||
|
||
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
|
||
);
|
||
|
||
NTSTATUS
|
||
UdfAllowExtendedDasdIo(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
#ifdef EXPERIMENTAL_MOUNT_OPEN_R_MEDIA
|
||
|
||
NTSTATUS
|
||
UdfCheckForOpenRMedia(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDEVICE_OBJECT TargetDeviceObject,
|
||
IN PULONG S,
|
||
IN PULONG N
|
||
);
|
||
|
||
#pragma alloc_text(PAGE, UdfCheckForOpenRMedia)
|
||
#endif
|
||
|
||
#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, 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, UdfUpdateVolumeLabel)
|
||
#pragma alloc_text(PAGE, UdfUpdateVolumeSerialNumber)
|
||
#pragma alloc_text(PAGE, UdfUserFsctl)
|
||
#pragma alloc_text(PAGE, UdfVerifyVolume)
|
||
#pragma alloc_text(PAGE, UdfAllowExtendedDasdIo)
|
||
#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;
|
||
|
||
case FSCTL_ALLOW_EXTENDED_DASD_IO:
|
||
|
||
Status = UdfAllowExtendedDasdIo( IrpContext, Irp );
|
||
break;
|
||
|
||
//
|
||
// We don't support any of the known or unknown requests.
|
||
//
|
||
|
||
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);
|
||
|
||
KIRQL SavedIrql;
|
||
|
||
ASSERT_EXCLUSIVE_VCB( Vcb );
|
||
|
||
//
|
||
// 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 the volume is already explicitly locked then fail. We use the
|
||
// Vpb locked flag as an 'explicit lock' flag in the same way as Fat.
|
||
//
|
||
|
||
IoAcquireVpbSpinLock( &SavedIrql );
|
||
|
||
if (!FlagOn( Vcb->Vpb->Flags, VPB_LOCKED ) &&
|
||
(Vcb->VcbCleanup == RemainingUserReferences) &&
|
||
(Vcb->VcbUserReference == Vcb->VcbResidualUserReference + RemainingUserReferences)) {
|
||
|
||
SetFlag( Vcb->VcbState, VCB_STATE_LOCKED );
|
||
SetFlag( Vcb->Vpb->Flags, VPB_LOCKED );
|
||
Vcb->VolumeLockFileObject = FileObject;
|
||
FinalStatus = STATUS_SUCCESS;
|
||
}
|
||
|
||
IoReleaseVpbSpinLock( SavedIrql );
|
||
|
||
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_NOT_LOCKED;
|
||
KIRQL SavedIrql;
|
||
|
||
IoAcquireVpbSpinLock( &SavedIrql );
|
||
|
||
if (FlagOn(Vcb->Vpb->Flags, VPB_LOCKED) &&
|
||
(FileObject == Vcb->VolumeLockFileObject)) {
|
||
|
||
ClearFlag( Vcb->VcbState, VCB_STATE_LOCKED );
|
||
ClearFlag( Vcb->Vpb->Flags, VPB_LOCKED);
|
||
Vcb->VolumeLockFileObject = NULL;
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
IoReleaseVpbSpinLock( SavedIrql );
|
||
|
||
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;
|
||
}
|
||
|
||
DebugTrace(( +1, Dbg, "UdfLockVolume()\n"));
|
||
|
||
//
|
||
// 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 );
|
||
}
|
||
|
||
DebugTrace(( -1, Dbg, "UdfLockVolume() -> 0x%X\n", Status));
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
}
|
||
|
||
DebugTrace(( +1, Dbg, "UdfDismountVolume()\n"));
|
||
|
||
Vcb = Fcb->Vcb;
|
||
|
||
//
|
||
// Make this request waitable.
|
||
//
|
||
|
||
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
|
||
|
||
//
|
||
// Acquire exclusive access to the Vcb, and take the global resource
|
||
// to sync. against mounts, verifies etc.
|
||
//
|
||
|
||
UdfAcquireUdfData( IrpContext);
|
||
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) {
|
||
|
||
Status = STATUS_VOLUME_DISMOUNTED;
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Invalidate the volume right now.
|
||
//
|
||
// The intent here is to make every subsequent operation
|
||
// on the volume fail and grease the rails toward dismount.
|
||
//
|
||
|
||
UdfLockVcb( IrpContext, Vcb );
|
||
|
||
if (Vcb->VcbCondition != VcbDismountInProgress) {
|
||
Vcb->VcbCondition = VcbInvalid;
|
||
}
|
||
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
|
||
//
|
||
// Set flag to tell the close path that we want to force dismount
|
||
// the volume when this handle is closed.
|
||
//
|
||
|
||
SetFlag( Ccb->Flags, CCB_FLAG_DISMOUNT_ON_CLOSE);
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Release all of our resources
|
||
//
|
||
|
||
UdfReleaseVcb( IrpContext, Vcb );
|
||
UdfReleaseUdfData( IrpContext);
|
||
|
||
DebugTrace(( -1, Dbg, "UdfDismountVolume() -> 0x%x\n", Status));
|
||
|
||
//
|
||
// Complete the request if there haven't been any exceptions.
|
||
//
|
||
|
||
UdfCompleteRequest( IrpContext, Irp, Status );
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
UdfAllowExtendedDasdIo(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine marks the CCB to indicate that the handle
|
||
may be used to read past the end of the volume file. The
|
||
handle must be a dasd handle.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp being processed.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation.
|
||
|
||
--*/
|
||
{
|
||
PIO_STACK_LOCATION IrpSp;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PFCB Fcb;
|
||
PCCB Ccb;
|
||
|
||
//
|
||
// Get the current Irp stack location and save some references.
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
//
|
||
// Extract and decode the file object and check for type of open.
|
||
//
|
||
|
||
if (UserVolumeOpen != UdfDecodeFileObject( IrpSp->FileObject, &Fcb, &Ccb )) {
|
||
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
else {
|
||
|
||
SetFlag( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO );
|
||
}
|
||
|
||
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.
|
||
//
|
||
|
||
if (Irp->AssociatedIrp.SystemBuffer != NULL) {
|
||
|
||
VolumeState = Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
} 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;
|
||
|
||
BOOLEAN UnlockVcb = FALSE;
|
||
|
||
LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0};
|
||
|
||
HANDLE Handle;
|
||
|
||
PVCB Vcb;
|
||
|
||
PLIST_ENTRY Links;
|
||
|
||
PFILE_OBJECT FileToMarkBad;
|
||
PDEVICE_OBJECT DeviceToMarkBad;
|
||
|
||
//
|
||
// We only allow this operation to be sent to our file system devices.
|
||
//
|
||
|
||
if (!UdfDeviceIsFsDo( IrpSp->DeviceObject)) {
|
||
|
||
UdfCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
|
||
|
||
return STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
|
||
//
|
||
// 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 defined(_WIN64)
|
||
if (IoIs32bitProcess( Irp )) {
|
||
|
||
if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof( UINT32 )) {
|
||
|
||
UdfCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
Handle = (HANDLE) LongToHandle( *((PUINT32) Irp->AssociatedIrp.SystemBuffer) );
|
||
|
||
} else {
|
||
#endif
|
||
if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof( HANDLE )) {
|
||
|
||
UdfCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
Handle = *((PHANDLE) Irp->AssociatedIrp.SystemBuffer);
|
||
#if defined(_WIN64)
|
||
}
|
||
#endif
|
||
|
||
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 );
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
//
|
||
// 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) {
|
||
|
||
//
|
||
// Take the VPB spinlock, and look to see if this volume is the
|
||
// one currently mounted on the actual device. If it is, pull it
|
||
// off immediately.
|
||
//
|
||
|
||
IoAcquireVpbSpinLock( &SavedIrql );
|
||
|
||
if (DeviceToMarkBad->Vpb == Vcb->Vpb) {
|
||
|
||
PVPB NewVpb = Vcb->SwapVpb;
|
||
|
||
ASSERT( FlagOn( Vcb->Vpb->Flags, VPB_MOUNTED));
|
||
ASSERT( NULL != NewVpb);
|
||
|
||
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 );
|
||
|
||
DeviceToMarkBad->Vpb = NewVpb;
|
||
Vcb->SwapVpb = NULL;
|
||
}
|
||
|
||
IoReleaseVpbSpinLock( SavedIrql );
|
||
|
||
if (Vcb->VcbCondition != VcbDismountInProgress) {
|
||
|
||
UdfSetVcbCondition( Vcb, VcbInvalid);
|
||
}
|
||
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
|
||
UdfAcquireVcbExclusive( IrpContext, Vcb, FALSE);
|
||
|
||
UdfPurgeVolume( IrpContext, Vcb, FALSE );
|
||
|
||
UnlockVcb = UdfCheckForDismount( IrpContext, Vcb, FALSE );
|
||
|
||
if (UnlockVcb) {
|
||
|
||
UdfReleaseVcb( IrpContext, Vcb);
|
||
}
|
||
|
||
} else {
|
||
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
}
|
||
}
|
||
|
||
UdfReleaseUdfData( IrpContext );
|
||
|
||
UdfCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
VOID
|
||
UdfRemountOldVcb(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB OldVcb,
|
||
IN PVCB NewVcb,
|
||
IN PDEVICE_OBJECT DeviceObjectWeTalkTo
|
||
)
|
||
{
|
||
KIRQL SavedIrql;
|
||
|
||
ObDereferenceObject( OldVcb->TargetDeviceObject );
|
||
|
||
IoAcquireVpbSpinLock( &SavedIrql);
|
||
|
||
NewVcb->Vpb->RealDevice->Vpb = OldVcb->Vpb;
|
||
|
||
OldVcb->Vpb->RealDevice = NewVcb->Vpb->RealDevice;
|
||
OldVcb->TargetDeviceObject = DeviceObjectWeTalkTo;
|
||
|
||
UdfSetVcbCondition( OldVcb, VcbMounted);
|
||
|
||
UdfSetMediaChangeCount( OldVcb, NewVcb->MediaChangeCount);
|
||
|
||
ClearFlag( OldVcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE);
|
||
|
||
IoReleaseVpbSpinLock( SavedIrql);
|
||
}
|
||
|
||
|
||
//
|
||
// 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;
|
||
BOOLEAN SetDoVerifyOnFail;
|
||
|
||
USHORT NSRVerFound = UDF_NSR_NO_VRS_FOUND;
|
||
|
||
ULONG BoundS;
|
||
ULONG BoundN;
|
||
|
||
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 ||
|
||
Vpb->RealDevice->DeviceType == FILE_DEVICE_VIRTUAL_DISK );
|
||
|
||
ASSERT( FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ));
|
||
|
||
DebugTrace(( +1, Dbg | UDFS_DEBUG_LEVEL_VERFYSUP, "UdfMountVolume (Vpb %p, Dev %p)\n",
|
||
Vpb, Vpb->RealDevice));
|
||
|
||
//
|
||
// 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;
|
||
|
||
SetDoVerifyOnFail = UdfRealDevNeedsVerify( IrpContext->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;
|
||
}
|
||
|
||
//
|
||
// Don't even attempt to mount floppy discs
|
||
//
|
||
|
||
if (FlagOn( Vpb->RealDevice->Characteristics, FILE_FLOPPY_DISKETTE)) {
|
||
|
||
UdfCompleteRequest( IrpContext, Irp, STATUS_UNRECOGNIZED_VOLUME );
|
||
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,
|
||
NULL,
|
||
0,
|
||
&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,
|
||
NULL,
|
||
0,
|
||
&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 find the multi-session bounds on this media.
|
||
//
|
||
|
||
UdfDetermineVolumeBounding( IrpContext,
|
||
DeviceObjectWeTalkTo,
|
||
&BoundS,
|
||
&BoundN );
|
||
|
||
//
|
||
// Now go confirm that this volume may be a UDF image by looking for a
|
||
// valid ISO 13346 Volume Recognition Sequence in the last and first
|
||
// sessions.
|
||
//
|
||
|
||
if (!UdfRecognizeVolume( IrpContext,
|
||
DeviceObjectWeTalkTo,
|
||
DiskGeometry.BytesPerSector,
|
||
&BoundS,
|
||
&BridgeMedia,
|
||
&NSRVerFound)) {
|
||
|
||
#ifdef EXPERIMENTAL_MOUNT_OPEN_R_MEDIA
|
||
|
||
if (DeviceObjectWeTalkTo->DeviceType == FILE_DEVICE_CD_ROM) {
|
||
|
||
DebugTrace(( 0, Dbg, "UdfMountVolume, recognition failed but continuing to look for open R volume\n"));
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
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;
|
||
}
|
||
|
||
//
|
||
// 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);
|
||
|
||
//
|
||
// Set the correct sector size. IO defaults to 512b for DISK_FS and 2k for
|
||
// CDROM_FS....
|
||
//
|
||
|
||
((PDEVICE_OBJECT) VolDo)->SectorSize = (USHORT) DiskGeometry.BytesPerSector;
|
||
|
||
ClearFlag( VolDo->DeviceObject.Flags, DO_DEVICE_INITIALIZING );
|
||
|
||
//
|
||
// 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 session bounds we determined earlier.
|
||
//
|
||
|
||
Vcb->BoundS = BoundS;
|
||
Vcb->BoundN = BoundN;
|
||
|
||
//
|
||
// Store the Vcb in the IrpContext as we didn't have one before.
|
||
//
|
||
|
||
IrpContext->Vcb = Vcb;
|
||
|
||
UdfAcquireVcbExclusive( IrpContext, Vcb, FALSE );
|
||
|
||
//
|
||
// Store the NSR version that we found
|
||
//
|
||
|
||
Vcb->NsrVersion = NSRVerFound;
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
UdfMarkRealDevVerifyOk( Vcb->Vpb->RealDevice);
|
||
|
||
//
|
||
// 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) status %X\n", Status ));
|
||
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 )) {
|
||
|
||
KIRQL SavedIrql;
|
||
|
||
DebugTrace((0, Dbg | UDFS_DEBUG_LEVEL_VERFYSUP, "Remounting Vcb %p (Vpb %p)\n",
|
||
OldVcb , OldVcb->Vpb));
|
||
//
|
||
// Link the old Vcb to point to the new device object that we
|
||
// should be talking to, dereferencing the previous. Call a nonpaged
|
||
// routine to do this since we take the Vpb spinlock.
|
||
//
|
||
|
||
UdfRemountOldVcb( IrpContext,
|
||
OldVcb,
|
||
Vcb,
|
||
DeviceObjectWeTalkTo);
|
||
|
||
//
|
||
// 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 );
|
||
|
||
UdfSetVcbCondition( Vcb, VcbMounted);
|
||
|
||
UdfReleaseVcb( IrpContext, Vcb );
|
||
Vcb = NULL;
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
} finally {
|
||
|
||
DebugUnwind( "UdfMountVolume" );
|
||
|
||
//
|
||
// If we are not mounting the device, then set the verify bit again.
|
||
//
|
||
|
||
if ((AbnormalTermination() || (Status != STATUS_SUCCESS)) &&
|
||
SetDoVerifyOnFail) {
|
||
|
||
UdfMarkRealDevForVerify( IrpContext->RealDevice);
|
||
}
|
||
|
||
//
|
||
// 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 = Vcb->MediaChangeCount;
|
||
ULONG Index;
|
||
|
||
PFILE_OBJECT FileObjectToNotify = NULL;
|
||
|
||
BOOLEAN ReturnError;
|
||
BOOLEAN ReleaseVcb = FALSE;
|
||
|
||
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 );
|
||
ASSERT( FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ));
|
||
|
||
//
|
||
// 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;
|
||
|
||
DebugTrace(( +1, Dbg, "UdfVerifyVolume, Vcb %08x\n", Vcb ));
|
||
|
||
//
|
||
// Acquire the global to synchronise against mounts and teardown.
|
||
//
|
||
|
||
UdfAcquireUdfData( IrpContext );
|
||
|
||
try {
|
||
|
||
UdfAcquireVcbExclusive( IrpContext, Vcb, FALSE );
|
||
ReleaseVcb = TRUE;
|
||
|
||
//
|
||
// 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,
|
||
NULL,
|
||
0,
|
||
&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) ||
|
||
(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" ));
|
||
|
||
UdfSetVcbCondition( Vcb, VcbMounted);
|
||
|
||
UdfMarkRealDevVerifyOk( Vpb->RealDevice);
|
||
|
||
//
|
||
// 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
|
||
//
|
||
|
||
UdfSetMediaChangeCount( Vcb, MediaChangeCount);
|
||
|
||
//
|
||
// Mark the Vcb as not mounted.
|
||
//
|
||
|
||
if (Status == STATUS_WRONG_VOLUME) {
|
||
|
||
UdfSetVcbCondition( Vcb, 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.
|
||
//
|
||
|
||
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;
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
|
||
Len;
|
||
|
||
//
|
||
// Advance to the next descriptor offset in the sequence.
|
||
//
|
||
|
||
Len -= BlockSize( Vcb ),
|
||
Lbn++) {
|
||
|
||
//
|
||
// Allocate a buffer to read fileset descriptors.
|
||
//
|
||
|
||
if (FSD == NULL) {
|
||
|
||
FSD = FsRtlAllocatePoolWithTag( UdfNonPagedPool,
|
||
UdfRawBufferSize( Vcb, sizeof(NSR_FSD) ),
|
||
TAG_NSR_FSD );
|
||
}
|
||
|
||
//
|
||
// Lookup the physical offset for this block. We could be mapping
|
||
// through a VAT here so we can't just assume that all the
|
||
// blocks in the extent are physically contiguous. The FSD seems to
|
||
// be the exception here - there's nothing that says it must be in
|
||
// physical partition, and it can have a terminator, => 2 blocks
|
||
// minumum. There is no single block virtual extent limitation in UDF 1.50.
|
||
//
|
||
|
||
Offset = LlBytesFromSectors( Vcb, UdfLookupPsnOfExtent( IrpContext,
|
||
Vcb,
|
||
LongAd->Start.Partition,
|
||
Lbn,
|
||
BlockSize( Vcb)));
|
||
|
||
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;
|
||
|
||
//
|
||
// Note that we must correct the values to take into account
|
||
// the changes that will be made next time through the for loop.
|
||
//
|
||
|
||
Len += BlockSize( Vcb );
|
||
Lbn -= 1;
|
||
}
|
||
|
||
UdfStoreFileSetDescriptorIfPrevailing( FileSetDescriptor, &FSD );
|
||
}
|
||
|
||
}
|
||
finally {
|
||
|
||
DebugUnwind( "UdfFindFileSetDescriptor");
|
||
|
||
//
|
||
// 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 MaxSize;
|
||
ULONG UnitSize = UdfRawReadSize( Vcb, sizeof(NSR_VD_GENERIC) );
|
||
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
ULONG ThisPass = 1;
|
||
ULONG MaxVdpExtents;
|
||
|
||
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++) {
|
||
|
||
MaxVdpExtents = 16;
|
||
|
||
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;
|
||
}
|
||
|
||
//
|
||
// Calculate the maximum size we expect this descriptor to be. For LVDs
|
||
// the descriptor can be followed by upto 2 partition maps, pushing it
|
||
// over the 512 byte ECMA desc. limit which we were assuming was the max.
|
||
//
|
||
|
||
MaxSize = sizeof( NSR_VD_GENERIC);
|
||
|
||
if (DESTAG_ID_NSR_LVOL == GenericVD->Destag.Ident) {
|
||
|
||
MaxSize += 2 * sizeof( PARTMAP_UDF_GENERIC);
|
||
|
||
ASSERT( BlockSize( Vcb) >= 1024);
|
||
}
|
||
|
||
if (GenericVD->Destag.Ident > DESTAG_ID_MAXIMUM_PART3 ||
|
||
|
||
!UdfVerifyDescriptor( IrpContext,
|
||
&GenericVD->Destag,
|
||
GenericVD->Destag.Ident,
|
||
MaxSize,
|
||
(ULONG) SectorsFromLlBytes( 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.
|
||
// We will only follow a maximum of 16 extents, to guard against loops.
|
||
//
|
||
|
||
if (0 == --MaxVdpExtents) {
|
||
|
||
try_leave( Status = STATUS_DISK_CORRUPT_ERROR );
|
||
}
|
||
|
||
//
|
||
// Bias the values by UnitSize, so that the next loop iteration will change them
|
||
// to the correct values.
|
||
//
|
||
|
||
Offset = LlBytesFromSectors( Vcb, ((PNSR_VDP) GenericVD)->Next.Lsn ) - UnitSize;
|
||
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 );
|
||
}
|
||
|
||
Len += UnitSize;
|
||
continue;
|
||
}
|
||
|
||
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;
|
||
USHORT ExpectedNsrVer;
|
||
|
||
//
|
||
// Our second pass is to pick up all relevant NSR02/3 PD
|
||
//
|
||
|
||
if (PartitionDescriptor->Destag.Ident != DESTAG_ID_NSR_PART) {
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Look at the NSR standard revision
|
||
//
|
||
|
||
if (UdfEqualEntityId( &PartitionDescriptor->ContentsID, &UdfNSR02Identifier, NULL )) {
|
||
|
||
ExpectedNsrVer = VsdIdentNSR02;
|
||
}
|
||
else if (UdfEqualEntityId( &PartitionDescriptor->ContentsID, &UdfNSR03Identifier, NULL )) {
|
||
|
||
ExpectedNsrVer = VsdIdentNSR03;
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Unknown NSR revision
|
||
//
|
||
|
||
ExpectedNsrVer = VsdIdentBad;
|
||
}
|
||
|
||
//
|
||
// Check that the NSR version in this PD matches what we found in the VRS earlier.
|
||
//
|
||
|
||
if (ExpectedNsrVer != Vcb->NsrVersion) {
|
||
|
||
#ifdef EXPERIMENTAL_MOUNT_OPEN_R_MEDIA
|
||
|
||
//
|
||
// If we didn't find a VRS (i.e. open CD/DVD-R media) then we ignore this, since
|
||
// we didn't have a VRS to infer the NSR version from. Just store what we
|
||
// found here.
|
||
//
|
||
|
||
if (Vcb->NsrVersion == UDF_NSR_NO_VRS_FOUND) {
|
||
|
||
Vcb->NsrVersion = ExpectedNsrVer;
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
DebugTrace(( 0, Dbg, "UdfFindVolumeDescriptors: NSR version in PartitionDescriptor (%d) != NSR found in VRS (%d)\n",
|
||
ExpectedNsrVer, Vcb->NsrVersion));
|
||
|
||
try_leave( Status = STATUS_UNRECOGNIZED_VOLUME );
|
||
}
|
||
}
|
||
|
||
UdfAddToPcb( *Pcb, (PNSR_PART) GenericVD );
|
||
}
|
||
} // inner descriptor loop.
|
||
|
||
//
|
||
// Now that a pass through the VDS has been completed, analyze the results.
|
||
//
|
||
|
||
if (ThisPass == 1) {
|
||
|
||
PNSR_PVD PVD;
|
||
PNSR_LVOL LVD;
|
||
USHORT MaxVerBasedOnNSR;
|
||
|
||
//
|
||
// 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" )))) {
|
||
|
||
try_leave( Status = STATUS_UNRECOGNIZED_VOLUME );
|
||
}
|
||
|
||
//
|
||
// Store away the UDF revision in the VCB for future reference, and clamp the
|
||
// maximum acceptable revision based on the previously encountered NSR version.
|
||
//
|
||
|
||
Vcb->UdfRevision = ((PUDF_SUFFIX_DOMAIN)&(LVD->DomainID.Suffix))->UdfRevision;
|
||
MaxVerBasedOnNSR = (VsdIdentNSR03 > Vcb->NsrVersion) ? UDF_VERSION_150 : UDF_VERSION_RECOGNIZED;
|
||
|
||
DebugTrace((0,Dbg,"UdfFindVolumeDescriptors() Pass 1: Found LVD specifying DomainID %x\n", ((PUDF_SUFFIX_DOMAIN)&(LVD->DomainID.Suffix))->UdfRevision));
|
||
|
||
if (
|
||
//
|
||
// 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) && (PVD->Destag.CRC != UDF_SNOW_WHITE_PVD_CRC) &&
|
||
(PVD->Destag.CRC != UDF_SNOW_WHITE_PVD_CRC_VARIANT_2)) &&
|
||
DebugTrace(( 0, Dbg,
|
||
"UdfFindVolumeDescriptors, PVD VolSetSeqMax %08x - volume in a non-unit volume set\n",
|
||
PVD->VolSetSeqMax ))) ||
|
||
|
||
(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 ))) ||
|
||
//
|
||
// 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" ))) ||
|
||
|
||
//
|
||
// 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" ))) ||
|
||
|
||
//
|
||
// 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" ))) ||
|
||
|
||
//
|
||
// 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,
|
||
MaxVerBasedOnNSR ) &&
|
||
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);
|
||
|
||
DebugTrace(( +1, Dbg, "UdfFindAnchorVolumeDescriptors()\n"));
|
||
|
||
//
|
||
// 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 = 0; ThisPass < 11; ThisPass++ ) {
|
||
|
||
if (ThisPass == 0) {
|
||
|
||
ReadLsn = Lsn = ANCHOR_SECTOR + Vcb->BoundS;
|
||
|
||
#ifdef EXPERIMENTAL_MOUNT_OPEN_R_MEDIA
|
||
}
|
||
else if (ThisPass == 1) {
|
||
|
||
//
|
||
// Open CD-R media typically only has a single AVDP at 512. Only
|
||
// consider this if we failed to find a VRS on the media.
|
||
//
|
||
|
||
if (Vcb->NsrVersion == UDF_NSR_NO_VRS_FOUND) {
|
||
|
||
ReadLsn = Lsn = 2*ANCHOR_SECTOR + Vcb->BoundS;
|
||
}
|
||
else {
|
||
|
||
continue;
|
||
}
|
||
#else
|
||
} else if (ThisPass == 1) {
|
||
|
||
continue;
|
||
#endif
|
||
} 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) {
|
||
|
||
ULONG SubPass = (ThisPass > 6) ? (ThisPass - 4) : ThisPass;
|
||
|
||
//
|
||
// 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;
|
||
}
|
||
|
||
//
|
||
// Note that although we're only looking at 2 sectors (N, N-256),
|
||
// because of the fuzziness of N on CD media (can include runout
|
||
// of 2 sectors) and method 2 addressing bugs in some drives, we
|
||
// potentially have to look at 8 locations... We work fowards to
|
||
// try and avoid reading invalid sectors (which can take some time).
|
||
//
|
||
|
||
ReadLsn = Lsn = Vcb->BoundN - ( SubPass == 3? (ANCHOR_SECTOR + 2): // 3,7
|
||
( SubPass == 4? ANCHOR_SECTOR: // 4,8
|
||
( SubPass == 5? 2 : 0 ))); // 5,9 6,10
|
||
|
||
//
|
||
// Also try the method 2 transformed version of each address (pass 7..10)
|
||
// If we get this far, it might take a while...
|
||
//
|
||
|
||
if (6 < ThisPass) {
|
||
|
||
ReadLsn = UdfMethod2TransformSector( Vcb, Lsn);
|
||
}
|
||
}
|
||
|
||
DebugTrace(( 0, Dbg, "Pass: %d Trying Lsn/ReadLsn %X / %X\n", ThisPass, Lsn, ReadLsn));
|
||
|
||
//
|
||
// 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 ) &&
|
||
UdfVerifyDescriptor( IrpContext,
|
||
&(*AnchorVolumeDescriptor)->Destag,
|
||
DESTAG_ID_NSR_ANCHOR,
|
||
sizeof(NSR_ANCHOR),
|
||
Lsn,
|
||
TRUE)
|
||
) {
|
||
|
||
//
|
||
// Got one! Set the method 2 fixup appropriately.
|
||
//
|
||
|
||
if (ReadLsn != Lsn) {
|
||
|
||
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 );
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (11 == ThisPass) {
|
||
|
||
Status = STATUS_UNRECOGNIZED_VOLUME;
|
||
}
|
||
|
||
DebugTrace(( -1, Dbg, "UdfFindAnchorVolumeDescriptors() -> %X\n", Status));
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
BOOLEAN
|
||
UdfRecognizeVolume (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN ULONG SectorSize,
|
||
IN OUT PULONG BoundS,
|
||
IN OUT PBOOLEAN Bridge,
|
||
OUT PUSHORT NSRVerFound
|
||
)
|
||
|
||
/*++
|
||
|
||
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
|
||
|
||
NSRVerFound - returns either VsdIdentNSR02 or VsdIdentNSR03 if successful
|
||
|
||
Return Value:
|
||
|
||
Boolean TRUE if we found NSR02/3, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
BOOLEAN FoundBEA;
|
||
BOOLEAN FoundNSR;
|
||
BOOLEAN Resolved;
|
||
|
||
ULONG AssumedDescriptorSize = sizeof(VSD_GENERIC);
|
||
|
||
USHORT ThisRecordType;
|
||
|
||
PVSD_GENERIC VolumeStructureDescriptor;
|
||
PVSD_GENERIC VolumeStructureDescriptorBuffer;
|
||
|
||
ULONGLONG Offset;
|
||
ULONGLONG StartOffset;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Check the input parameters
|
||
//
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext);
|
||
|
||
VolumeStructureDescriptorBuffer = (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 {
|
||
|
||
Retry:
|
||
|
||
FoundBEA =
|
||
FoundNSR =
|
||
Resolved = FALSE;
|
||
|
||
StartOffset =
|
||
Offset = (SectorSize * (ULONGLONG)(*BoundS)) + VRA_BOUNDARY_LOCATION;
|
||
|
||
while (!Resolved) {
|
||
|
||
//
|
||
// It's possible that the sector size is > 2k which is the descriptor
|
||
// size. Only read if we've processed all 2k blocks in the prev. sector
|
||
//
|
||
|
||
if (0 == (Offset & (SectorSize - 1))) {
|
||
|
||
VolumeStructureDescriptor = VolumeStructureDescriptorBuffer;
|
||
|
||
Status = UdfReadSectors( IrpContext,
|
||
Offset,
|
||
UdfRawReadSizeN( SectorSize,
|
||
sizeof(VSD_GENERIC) ),
|
||
TRUE,
|
||
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.
|
||
//
|
||
|
||
ThisRecordType = (USHORT)UdfFindInParseTable( VsdIdentParseTable,
|
||
VolumeStructureDescriptor->Ident,
|
||
VSD_LENGTH_IDENT );
|
||
switch ( ThisRecordType ) {
|
||
|
||
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:
|
||
case VsdIdentNSR03:
|
||
|
||
//
|
||
// We recognize NSR02/3 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 NSR0%c\n", ((VsdIdentNSR02 == ThisRecordType) ? '2' : '3')));
|
||
|
||
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;
|
||
*NSRVerFound = ThisRecordType; // Report the NSR version we found here
|
||
break;
|
||
}
|
||
|
||
break;
|
||
|
||
case VsdIdentCD001:
|
||
case VsdIdentCDW01:
|
||
case VsdIdentNSR01:
|
||
case VsdIdentCDW02:
|
||
case VsdIdentBOOT2:
|
||
|
||
DebugTrace(( 0, Dbg, "UdfRecognizeVolume, got a valid but uninteresting 13346 descriptor (%d)\n", ThisRecordType ));
|
||
|
||
//
|
||
// Valid but uninteresting (to us) descriptors
|
||
//
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
DebugTrace(( 0, Dbg, "UdfRecognizeVolume, got an invalid 13346 descriptor (%d)\n", ThisRecordType ));
|
||
|
||
//
|
||
// This probably was a false alert, but in any case there is nothing
|
||
// on this volume for us. Exception is if this media sector size
|
||
// is >= 4k, and this was the second descriptor. We'll allow
|
||
// a failure here, and switch to reading in whole sector increments.
|
||
//
|
||
|
||
if ((Offset == (StartOffset + sizeof(VSD_GENERIC))) &&
|
||
(SectorSize > sizeof( VSD_GENERIC))) {
|
||
|
||
Offset -= AssumedDescriptorSize;
|
||
AssumedDescriptorSize = SectorSize;
|
||
}
|
||
else {
|
||
|
||
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. Exception is if this media sector size
|
||
// is >= 4k, and this was the second descriptor. We'll allow
|
||
// a failure here, and switch to reading in whole sector increments.
|
||
//
|
||
|
||
if ((Offset == (StartOffset + sizeof(VSD_GENERIC))) &&
|
||
(SectorSize > sizeof( VSD_GENERIC))) {
|
||
|
||
Offset -= AssumedDescriptorSize;
|
||
AssumedDescriptorSize = SectorSize;
|
||
}
|
||
else {
|
||
|
||
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;
|
||
}
|
||
|
||
Offset += AssumedDescriptorSize;
|
||
VolumeStructureDescriptor = Add2Ptr( VolumeStructureDescriptor,
|
||
sizeof( VSD_GENERIC),
|
||
PVSD_GENERIC);
|
||
}
|
||
|
||
//
|
||
// If this was the first pass, and we weren't looking at the start
|
||
// of the disc (i.e. later session), and we didn't find anything,
|
||
// then try the first track in the first session.
|
||
//
|
||
|
||
if (!FoundNSR && (0 != *BoundS)) {
|
||
|
||
DebugTrace(( 0, Dbg, "UdfRecognizeVolume, failed to find VRS in last session, trying first\n" ));
|
||
|
||
*BoundS = 0;
|
||
|
||
goto Retry;
|
||
}
|
||
}
|
||
finally {
|
||
|
||
DebugUnwind( "UdfRecognizeVolume" );
|
||
|
||
//
|
||
// Free up our temporary buffer
|
||
//
|
||
|
||
UdfFreePool( &VolumeStructureDescriptorBuffer );
|
||
|
||
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.
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
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 );
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
#ifdef EXPERIMENTAL_MOUNT_OPEN_R_MEDIA
|
||
|
||
NTSTATUS
|
||
UdfCheckForOpenRMedia(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDEVICE_OBJECT TargetDeviceObject,
|
||
IN PULONG S,
|
||
IN PULONG N
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Examines the media in the target device to determine whether or not it
|
||
is C/DVD-R media in an open state (i.e. current open track is packet
|
||
mode... etc) that could be UDF. If so, updates S and N.
|
||
|
||
Arguments:
|
||
|
||
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:
|
||
|
||
NTSTATUS from underlying drivers.
|
||
|
||
--*/
|
||
{
|
||
CDB Cdb;
|
||
|
||
NTSTATUS Status;
|
||
|
||
UCHAR Buffer[256];
|
||
ULONG BufferSize = sizeof( Buffer);
|
||
ULONG FixedPacketSize;
|
||
ULONG Feature;
|
||
|
||
PTRACK_INFORMATION TrackInfo = (PTRACK_INFORMATION)Buffer;
|
||
DISK_INFORMATION DiscInfo;
|
||
|
||
//
|
||
// Issue a read disc information.
|
||
//
|
||
|
||
RtlZeroMemory( &Cdb, sizeof( Cdb));
|
||
|
||
Cdb.READ_DISK_INFORMATION.OperationCode = SCSIOP_READ_DISK_INFORMATION;
|
||
Cdb.READ_DISK_INFORMATION.AllocationLength[0] = (UCHAR)(BufferSize >> 8);
|
||
Cdb.READ_DISK_INFORMATION.AllocationLength[1] = (UCHAR)(BufferSize & 0xff);
|
||
|
||
Status = UdfSendSptCdb( TargetDeviceObject,
|
||
&Cdb,
|
||
Buffer,
|
||
&BufferSize,
|
||
TRUE,
|
||
5,
|
||
NULL,
|
||
0,
|
||
NULL,
|
||
NULL);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
//
|
||
// Wouldn't expect this to fail except on really old CD-ROM drives.
|
||
//
|
||
|
||
DebugTrace((0, Dbg, "READ_DISC_INFORMATION failed 0x%x\n", Status));
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Preserve the disc info.
|
||
//
|
||
|
||
RtlCopyMemory( &DiscInfo, Buffer, sizeof( DiscInfo));
|
||
|
||
//
|
||
// Fail if disc state and last session/border aren't both incomplete.
|
||
//
|
||
|
||
if (!(DiscInfo.LastSessionStatus == 1) &&
|
||
(DiscInfo.DiskStatus == 1)) {
|
||
|
||
return STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
|
||
//
|
||
// Read track information for the last track on the media.
|
||
//
|
||
|
||
RtlZeroMemory( &Cdb, sizeof( Cdb));
|
||
BufferSize = sizeof( Buffer);
|
||
|
||
Cdb.READ_TRACK_INFORMATION.OperationCode = SCSIOP_READ_TRACK_INFORMATION;
|
||
Cdb.READ_TRACK_INFORMATION.AllocationLength[0] = (UCHAR)(BufferSize >> 8);
|
||
Cdb.READ_TRACK_INFORMATION.AllocationLength[1] = (UCHAR)(BufferSize & 0xff);
|
||
Cdb.READ_TRACK_INFORMATION.Track = 1; // blockaddress field => track/rzone number
|
||
Cdb.READ_TRACK_INFORMATION.BlockAddress[3] = 0xff; // = invisible / open track
|
||
|
||
Status = UdfSendSptCdb( TargetDeviceObject,
|
||
&Cdb,
|
||
Buffer,
|
||
&BufferSize,
|
||
TRUE,
|
||
5,
|
||
NULL,
|
||
0,
|
||
NULL,
|
||
NULL);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
//
|
||
// Wouldn't expect this to fail except on really old CD-ROM drives.
|
||
//
|
||
|
||
DebugTrace((0, Dbg, "READ_TRACK_INFORMATION failed 0x%x\n", Status));
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Check that the last track is...
|
||
// - variable packet mode
|
||
// - not reserved / damaged / FP / blank and that either the NWA or LRA is valid.
|
||
//
|
||
|
||
if (TrackInfo->Damage || TrackInfo->FP || TrackInfo->Blank || !TrackInfo->Packet ||
|
||
TrackInfo->RT || !(TrackInfo->NWA_V || (TrackInfo->Reserved3 & 1))) {
|
||
|
||
return STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
|
||
//
|
||
// If the last recorded block address is valid, use that.
|
||
//
|
||
|
||
if (TrackInfo->Reserved3 & 1) {
|
||
|
||
SwapCopyUchar4( N, TrackInfo->FixedPacketSize + 8);
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Pull out the NWA.
|
||
//
|
||
|
||
SwapCopyUchar4( N, TrackInfo->NextWritableAddress);
|
||
|
||
//
|
||
// Skip back past the runout blocks. We expect that DVD-R drives
|
||
// will return the LRA, so we assume CD-R here (7 blocks runout).
|
||
//
|
||
|
||
*N -= 8;
|
||
}
|
||
|
||
//
|
||
// Set S to zero, since we expect an AVDP at sector 512.
|
||
//
|
||
|
||
*S = 0;
|
||
|
||
DebugTrace(( 0, Dbg, "UdfCheckForOpenRMedia -> N = 0x%x\n", *N));
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
#endif
|
||
|
||
|
||
VOID
|
||
UdfDetermineVolumeBounding (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDEVICE_OBJECT TargetDeviceObject,
|
||
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;
|
||
CDROM_READ_TOC_EX Command;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Check input.
|
||
//
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
//
|
||
// Whack the inputs to the benign state.
|
||
//
|
||
|
||
*S = *N = 0;
|
||
|
||
//
|
||
// Currently we do nothing here for non CD class devices. This does
|
||
// mean that we can't mount (e.g.) WORM/RAM/MO media which has been
|
||
// recorded with sequential UDF and a VAT.
|
||
//
|
||
|
||
if (TargetDeviceObject->DeviceType != FILE_DEVICE_CD_ROM) {
|
||
|
||
DebugTrace(( 0, Dbg, "Not determining volume bounds / session info - not CDROM class device\n"));
|
||
return;
|
||
}
|
||
|
||
//
|
||
// 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, S %08x N %08x\n",
|
||
S,
|
||
N ));
|
||
//
|
||
// Zero the command block. This conveniently corresponds to an
|
||
// LBA mode READ_TOC request.
|
||
//
|
||
|
||
RtlZeroMemory( &Command, sizeof( Command));
|
||
|
||
//
|
||
// 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_EX,
|
||
TargetDeviceObject,
|
||
&Command,
|
||
sizeof( Command),
|
||
CdromToc,
|
||
sizeof( CDROM_TOC ),
|
||
FALSE,
|
||
TRUE,
|
||
NULL );
|
||
|
||
//
|
||
// If this failed, try again with the MSF variant of the command
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status) &&
|
||
(STATUS_INSUFFICIENT_RESOURCES != Status)) {
|
||
|
||
Command.Msf = 1;
|
||
|
||
Status = UdfPerformDevIoCtrl( IrpContext,
|
||
IOCTL_CDROM_READ_TOC_EX,
|
||
TargetDeviceObject,
|
||
&Command,
|
||
sizeof( Command),
|
||
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 )) {
|
||
|
||
#ifdef EXPERIMENTAL_MOUNT_OPEN_R_MEDIA
|
||
|
||
//
|
||
// On 'open' CD/DVD-R media the TOC is not available. So look for media
|
||
// in this state which might be packet written UDF+VAT. This will only
|
||
// succeed on CD/DVD-R drives. Regardless of success/failure here, we're
|
||
// done.
|
||
//
|
||
|
||
Status = UdfCheckForOpenRMedia( IrpContext, TargetDeviceObject, S, N);
|
||
#endif
|
||
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 );
|
||
}
|
||
|
||
#if DBG
|
||
{
|
||
ULONG Track;
|
||
|
||
for ( Track = 0; Track <= (ULONG)(CdromToc->LastTrack - CdromToc->FirstTrack + 1); Track++) {
|
||
|
||
DebugTrace(( 0, Dbg, " TOC[%02x]: Num: %x Ctrl/Adr: %x/%x Addr: %08x\n", Track, CdromToc->TrackData[Track].TrackNumber, CdromToc->TrackData[Track].Control, CdromToc->TrackData[Track].Adr, *(PULONG)(CdromToc->TrackData[Track].Address)));
|
||
}
|
||
}
|
||
#endif
|
||
|
||
TrackData = &CdromToc->TrackData[(CdromToc->LastTrack - CdromToc->FirstTrack + 1)];
|
||
|
||
//
|
||
// Last track better have number 0xAA ...
|
||
//
|
||
|
||
if (TrackData->TrackNumber != 0xaa) {
|
||
|
||
DebugTrace(( 0, Dbg, "UdfDetermineVolumeBounding, TOC malf (aa not last)\n" ));
|
||
|
||
//
|
||
// Some drives do this wrong, apparently, so we won't enforce it.
|
||
//
|
||
|
||
// try_leave( NOTHING );
|
||
}
|
||
|
||
//
|
||
// Now, find the AA (leadout 'track') info
|
||
//
|
||
|
||
if (Command.Msf) {
|
||
|
||
//
|
||
// 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. And actually the maximum 'legal' value is 63/59/74.
|
||
//
|
||
|
||
*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;
|
||
}
|
||
else {
|
||
|
||
//
|
||
// The non-MSF (LBA) request succeeded, so just fix the endianness.
|
||
//
|
||
|
||
SwapCopyUchar4( N, &TrackData->Address);
|
||
|
||
if (0 != *N) {
|
||
|
||
*N -= 1;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Seems that some DVD drives always return AA start 0x6dd39 (which is the max legally
|
||
// representable MSF value 99/59/74) to TOC queries, even in LBA mode. If this
|
||
// is what we have for the leadout address, then lets see what READ_CAPACITY says.
|
||
// We'll also issue read capacity if the address is > than this, since we must
|
||
// be dealing with DVD or DDCD media, so the drive must support the command and
|
||
// it should give a definitive answer.
|
||
//
|
||
|
||
if (0x6dd38 <= *N) {
|
||
|
||
PDISK_GEOMETRY_EX Geometry = (PVOID)CdromToc;
|
||
ULONG Blocks;
|
||
|
||
Status = UdfPerformDevIoCtrl( IrpContext,
|
||
IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX,
|
||
TargetDeviceObject,
|
||
NULL,
|
||
0,
|
||
CdromToc,
|
||
sizeof( CDROM_TOC ),
|
||
FALSE,
|
||
TRUE,
|
||
NULL );
|
||
|
||
if (NT_SUCCESS( Status)) {
|
||
|
||
Blocks = (ULONG)(Geometry->DiskSize.QuadPart / Geometry->Geometry.BytesPerSector) - 1;
|
||
|
||
if (Blocks > *N) {
|
||
|
||
DebugTrace((0, Dbg, "Using READ_CAPACITY media size of 0x%X in place of maxed out READ_TOC value\n", Blocks));
|
||
|
||
*N = Blocks;
|
||
}
|
||
}
|
||
else {
|
||
|
||
DebugTrace(( 0, Dbg, "GET_DRIVE_GEO failed, %x\n", Status));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Query the last session information from the driver. Not that this
|
||
// actually issues an LBA mode READ_TOC_EX and pulls the address from
|
||
// there.
|
||
//
|
||
|
||
Status = UdfPerformDevIoCtrl( IrpContext,
|
||
IOCTL_CDROM_GET_LAST_SESSION,
|
||
TargetDeviceObject,
|
||
NULL,
|
||
0,
|
||
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 first track in the last
|
||
// session as a logical block address.
|
||
//
|
||
|
||
SwapCopyUchar4( S, &CdromToc->TrackData[0].Address );
|
||
|
||
//
|
||
// Save grief if the session info is messed up.
|
||
//
|
||
|
||
if (*N <= *S) {
|
||
|
||
DebugTrace(( 0, Dbg, "UdfDetermineVolumeBounding, N (0x%x) before S (0x%x), whacking both back!\n", *N, *S ));
|
||
*S = *N = 0;
|
||
}
|
||
}
|
||
|
||
DebugTrace(( 0, Dbg, "UdfDetermineVolumeBounding, S 0x%08x, N (== AA start - 150) 0x%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();
|
||
|
||
//
|
||
// Check inputs.
|
||
//
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
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.
|
||
//
|
||
|
||
*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();
|
||
|
||
//
|
||
// Check input.
|
||
//
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
//
|
||
// The serial number is just off of the FSD. This matches Win9x.
|
||
//
|
||
|
||
VsnLe = UdfSerial32( (PCHAR) Fsd, sizeof( NSR_FSD ));
|
||
SwapCopyUchar4( VolumeSerialNumber, &VsnLe );
|
||
}
|
||
|