You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4920 lines
139 KiB
4920 lines
139 KiB
/*++
|
|
|
|
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 );
|
|
}
|
|
|