|
|
/*++
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; }
|