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.
615 lines
17 KiB
615 lines
17 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
udfs_rec.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the mini-file system recognizer for UDFS.
|
|
|
|
Author:
|
|
|
|
Dan Lovinger (danlo) 13-Feb-1997
|
|
|
|
Environment:
|
|
|
|
Kernel mode, local to I/O system
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "fs_rec.h"
|
|
#include "udfs_rec.h"
|
|
|
|
//
|
|
// The local debug trace level
|
|
//
|
|
|
|
#define Dbg (FSREC_DEBUG_LEVEL_UDFS)
|
|
|
|
//
|
|
// Tables of tokens we have to parse up from mount-time on-disk structures
|
|
//
|
|
|
|
PARSE_KEYVALUE VsdIdentParseTable[] = {
|
|
{ VSD_IDENT_BEA01, VsdIdentBEA01 },
|
|
{ VSD_IDENT_TEA01, VsdIdentTEA01 },
|
|
{ VSD_IDENT_CDROM, VsdIdentCDROM },
|
|
{ VSD_IDENT_CD001, VsdIdentCD001 },
|
|
{ VSD_IDENT_CDW01, VsdIdentCDW01 },
|
|
{ VSD_IDENT_CDW02, VsdIdentCDW02 },
|
|
{ VSD_IDENT_NSR01, VsdIdentNSR01 },
|
|
{ VSD_IDENT_NSR02, VsdIdentNSR02 },
|
|
{ VSD_IDENT_BOOT2, VsdIdentBOOT2 },
|
|
{ VSD_IDENT_NSR03, VsdIdentNSR03 },
|
|
{ NULL, VsdIdentBad }
|
|
};
|
|
|
|
NTSTATUS
|
|
UdfsRecGetLastSessionStart(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
OUT PULONG Psn
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,IsUdfsVolume)
|
|
#pragma alloc_text(PAGE,UdfsFindInParseTable)
|
|
#pragma alloc_text(PAGE,UdfsRecFsControl)
|
|
#pragma alloc_text(PAGE,UdfsRecGetLastSessionStart)
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
|
|
//
|
|
// This macro copies an unaligned src longword to a dst longword,
|
|
// performing an little/big endian swap.
|
|
//
|
|
|
|
#define SwapCopyUchar4(Dst,Src) { \
|
|
*((UNALIGNED UCHAR *)(Dst)) = *((UNALIGNED UCHAR *)(Src) + 3); \
|
|
*((UNALIGNED UCHAR *)(Dst) + 1) = *((UNALIGNED UCHAR *)(Src) + 2); \
|
|
*((UNALIGNED UCHAR *)(Dst) + 2) = *((UNALIGNED UCHAR *)(Src) + 1); \
|
|
*((UNALIGNED UCHAR *)(Dst) + 3) = *((UNALIGNED UCHAR *)(Src)); \
|
|
}
|
|
|
|
#define Max(a,b) (((a) > (b)) ? (a) : (b))
|
|
|
|
|
|
NTSTATUS
|
|
UdfsRecGetLastSessionStart(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
OUT PULONG Psn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function queries the underlying device for the address of the
|
|
first track in the last session. Does nothing for DISK devices.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to this driver's device object.
|
|
|
|
Psn - receives physical sector number of first block in last session,
|
|
0 for disk devices
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status of the operation.
|
|
|
|
-*/
|
|
{
|
|
KEVENT Event;
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
CDROM_TOC_SESSION_DATA SessionData;
|
|
PIRP Irp;
|
|
|
|
*Psn = 0;
|
|
|
|
if (DeviceObject->DeviceType != FILE_DEVICE_CD_ROM) {
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
KeInitializeEvent( &Event, SynchronizationEvent, FALSE );
|
|
|
|
Irp = IoBuildDeviceIoControlRequest( IOCTL_CDROM_GET_LAST_SESSION,
|
|
DeviceObject,
|
|
(PVOID) NULL,
|
|
0,
|
|
&SessionData,
|
|
sizeof( SessionData ),
|
|
FALSE,
|
|
&Event,
|
|
&ioStatus );
|
|
if (!Irp) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Override verify logic - we don't care. The fact we're in the picture means
|
|
// someone is trying to mount new/changed media in the first place.
|
|
//
|
|
|
|
SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME );
|
|
|
|
Status = IoCallDriver( DeviceObject, Irp );
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
|
|
(VOID) KeWaitForSingleObject( &Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER) NULL );
|
|
Status = ioStatus.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
return Status;
|
|
}
|
|
|
|
if (SessionData.FirstCompleteSession != SessionData.LastCompleteSession) {
|
|
|
|
SwapCopyUchar4( Psn, &SessionData.TrackData[0].Address );
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
UdfsRecFsControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function performs the mount and driver reload functions for this mini-
|
|
file system recognizer driver.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to this driver's device object.
|
|
|
|
Irp - Pointer to the I/O Request Packet (IRP) representing the function to
|
|
be performed.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status of the operation.
|
|
|
|
|
|
-*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
UNICODE_STRING driverName;
|
|
ULONG bytesPerSector;
|
|
PDEVICE_OBJECT targetDevice;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Begin by determining what function that is to be performed.
|
|
//
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
switch ( irpSp->MinorFunction ) {
|
|
|
|
case IRP_MN_MOUNT_VOLUME:
|
|
|
|
//
|
|
// Attempt to mount a volume: There are two different cases here:
|
|
//
|
|
// 1) The device is being opened for DASD access, that is, no
|
|
// file system is required, thus it is OK to allow RAW to
|
|
// to open it.
|
|
//
|
|
// 2) We need to rummage the media to see if this is a UDF volume.
|
|
//
|
|
|
|
status = STATUS_UNRECOGNIZED_VOLUME;
|
|
|
|
targetDevice = irpSp->Parameters.MountVolume.DeviceObject;
|
|
|
|
if (FsRecGetDeviceSectorSize( targetDevice,
|
|
&bytesPerSector )) {
|
|
|
|
if (IsUdfsVolume( targetDevice,
|
|
bytesPerSector )) {
|
|
|
|
status = STATUS_FS_DRIVER_REQUIRED;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case IRP_MN_LOAD_FILE_SYSTEM:
|
|
|
|
status = FsRecLoadFileSystem( DeviceObject,
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Udfs" );
|
|
break;
|
|
|
|
default:
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
}
|
|
|
|
//
|
|
// Finally, complete the request and return the same status code to the
|
|
// caller.
|
|
//
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
IsUdfsVolume (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG SectorSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine walks the Volume Recognition Sequence to determine
|
|
whether this volume contains an NSR02 (ISO 13346 Section 4) image.
|
|
|
|
Note: this routine is pretty much diked out of UdfsRecognizeVolume
|
|
in the real filesystem, modulo fitting it into the fs recognizer.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - device we are checking
|
|
|
|
SectorSize - size of a physical sector on this device
|
|
|
|
Return Value:
|
|
|
|
Boolean TRUE if we found NSR02, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN FoundNSR;
|
|
|
|
BOOLEAN FoundBEA;
|
|
BOOLEAN Resolved;
|
|
|
|
ULONG LastSessionStartPsn;
|
|
ULONG AssumedDescriptorSize = sizeof(VSD_GENERIC);
|
|
|
|
PVSD_GENERIC VolumeStructureDescriptor = NULL;
|
|
PVOID Buffer = NULL;
|
|
ULONGLONG Offset;
|
|
ULONGLONG StartOffset;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(( +1, Dbg,
|
|
"IsUdfsVolume, DevObj %08x SectorSize %08x\n",
|
|
DeviceObject,
|
|
SectorSize ));
|
|
|
|
//
|
|
// Find the start of the last session
|
|
//
|
|
|
|
if (!NT_SUCCESS( UdfsRecGetLastSessionStart( DeviceObject,
|
|
&LastSessionStartPsn))) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
Retry:
|
|
|
|
DebugTrace(( 0, Dbg, "IsUdfsVolume, Looking at session starting Psn == 0x%x\n", LastSessionStartPsn));
|
|
|
|
StartOffset =
|
|
Offset = (SectorSize * LastSessionStartPsn) + SectorAlignN( SectorSize, VRA_BOUNDARY_LOCATION );
|
|
|
|
FoundNSR =
|
|
FoundBEA =
|
|
Resolved = FALSE;
|
|
|
|
while (!Resolved) {
|
|
|
|
//
|
|
// The VRS descriptors are 2kb, and there's a lot of media out there where
|
|
// people have interpreted this as meaning the descriptors should be aligned
|
|
// on 2k boundries even on >2k sector size media. So we need to look at
|
|
// both 2k and sector offsets on such media. (ECMA 2/8.4 specifies that these
|
|
// descriptors shall all be aligned to the start of a sector).
|
|
//
|
|
|
|
if (0 == (Offset & (SectorSize - 1))) {
|
|
|
|
if (!FsRecReadBlock( DeviceObject,
|
|
(PLARGE_INTEGER)&Offset,
|
|
sizeof(VSD_GENERIC),
|
|
SectorSize,
|
|
&Buffer,
|
|
NULL )) {
|
|
break;
|
|
}
|
|
|
|
VolumeStructureDescriptor = Buffer;
|
|
}
|
|
|
|
//
|
|
// Now check the type of the descriptor. All ISO 13346 VSDs are
|
|
// of Type 0, 9660 PVDs are Type 1, 9660 SVDs are Type 2, and 9660
|
|
// terminating descriptors are Type 255.
|
|
//
|
|
|
|
if (VolumeStructureDescriptor->Type == 0) {
|
|
|
|
//
|
|
// In order to properly recognize the volume, we must know all of the
|
|
// Structure identifiers in ISO 13346 so that we can terminate if a
|
|
// badly formatted (or, shockingly, non 13346) volume is presented to us.
|
|
//
|
|
|
|
switch (UdfsFindInParseTable( VsdIdentParseTable,
|
|
VolumeStructureDescriptor->Ident,
|
|
VSD_LENGTH_IDENT )) {
|
|
case VsdIdentBEA01:
|
|
|
|
//
|
|
// Only one BEA may exist and its version must be 1 (2/9.2.3)
|
|
//
|
|
|
|
DebugTrace(( 0, Dbg, "IsUdfsVolume, got a BEA01\n" ));
|
|
|
|
|
|
if ((FoundBEA &&
|
|
DebugTrace(( 0, Dbg,
|
|
"IsUdfsVolume, ... but it is a duplicate!\n" ))) ||
|
|
|
|
(VolumeStructureDescriptor->Version != 1 &&
|
|
DebugTrace(( 0, Dbg,
|
|
"IsUdfsVolume, ... 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, "IsUdfsVolume, got a TEA01\n" ));
|
|
|
|
Resolved = TRUE;
|
|
break;
|
|
|
|
case VsdIdentNSR02:
|
|
case VsdIdentNSR03:
|
|
|
|
//
|
|
// We recognize NSR02 version 1 embedded after a BEA (3/9.1.3). For
|
|
// simplicity we will not bother being a complete nitpick and check
|
|
// for a bounding TEA, although we will be optimistic in the case where
|
|
// we fail to match the version.
|
|
//
|
|
|
|
DebugTrace(( 0, Dbg, "IsUdfsVolume, got an NSR02/3\n" ));
|
|
|
|
if ((FoundBEA ||
|
|
!DebugTrace(( 0, Dbg, "IsUdfsVolume, ... but we haven't seen a BEA01 yet!\n" ))) &&
|
|
|
|
(VolumeStructureDescriptor->Version == 1 ||
|
|
!DebugTrace(( 0, Dbg, "IsUdfsVolume, ... but it has a wacky version number %02x != 1\n",
|
|
VolumeStructureDescriptor->Version )))) {
|
|
|
|
FoundNSR = Resolved = TRUE;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case VsdIdentCD001:
|
|
case VsdIdentCDW01:
|
|
case VsdIdentNSR01:
|
|
case VsdIdentCDW02:
|
|
case VsdIdentBOOT2:
|
|
|
|
DebugTrace(( 0, Dbg, "IsUdfsVolume, got a valid but uninteresting 13346 descriptor\n" ));
|
|
|
|
//
|
|
// Valid but uninteresting (to us) descriptors
|
|
//
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DebugTrace(( 0, Dbg, "IsUdfsVolume, got an invalid 13346 descriptor\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 if (!FoundBEA && (VolumeStructureDescriptor->Type < 3 ||
|
|
VolumeStructureDescriptor->Type == 255)) {
|
|
|
|
DebugTrace(( 0, Dbg, "IsUdfsVolume, 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 (UdfsFindInParseTable( VsdIdentParseTable,
|
|
VolumeStructureDescriptor->Ident,
|
|
VSD_LENGTH_IDENT )) {
|
|
case VsdIdentCDROM:
|
|
case VsdIdentCD001:
|
|
|
|
DebugTrace(( 0, Dbg, "IsUdfsVolume, ... seems we have 9660 here\n" ));
|
|
|
|
//
|
|
// Note to our caller that we seem to have ISO 9660 here
|
|
//
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DebugTrace(( 0, Dbg, "IsUdfsVolume, ... 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, "IsUdfsVolume, got an unrecognizeable descriptor, probably not 13346/9660\n" ));
|
|
break;
|
|
}
|
|
|
|
Offset += AssumedDescriptorSize;
|
|
VolumeStructureDescriptor = (PVSD_GENERIC)(((PUCHAR)VolumeStructureDescriptor) + sizeof( VSD_GENERIC));
|
|
}
|
|
|
|
//
|
|
// If we were looking in the last session, and failed to find anything, then
|
|
// go back and try the first.
|
|
//
|
|
|
|
if (!FoundNSR && (0 != LastSessionStartPsn)) {
|
|
|
|
LastSessionStartPsn = 0;
|
|
|
|
goto Retry;
|
|
}
|
|
|
|
DebugTrace(( -1, Dbg, "IsUdfsVolume -> %c\n", ( FoundNSR ? 'T' : 'F' )));
|
|
|
|
//
|
|
// Free up our temporary buffer
|
|
//
|
|
|
|
if (Buffer) {
|
|
|
|
ExFreePool( Buffer );
|
|
}
|
|
|
|
return FoundNSR;
|
|
}
|
|
|
|
|
|
ULONG
|
|
UdfsFindInParseTable (
|
|
IN PPARSE_KEYVALUE ParseTable,
|
|
IN PCHAR Id,
|
|
IN ULONG MaxIdLen
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine walks a table of string key/value information for a match of the
|
|
input Id. MaxIdLen can be set to get a prefix match.
|
|
|
|
Arguments:
|
|
|
|
Table - This is the table being searched.
|
|
|
|
Id - Key value.
|
|
|
|
MaxIdLen - Maximum possible length of Id.
|
|
|
|
Return Value:
|
|
|
|
Value of matching entry, or the terminating (NULL) entry's value.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
while (ParseTable->Key != NULL) {
|
|
|
|
if (RtlEqualMemory(ParseTable->Key, Id, MaxIdLen)) {
|
|
|
|
break;
|
|
}
|
|
|
|
ParseTable++;
|
|
}
|
|
|
|
return ParseTable->Value;
|
|
}
|
|
|