/* * ReactOS kernel * Copyright (C) 2002, 2003 ReactOS Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: drivers/filesystems/cdfs/fsctl.c * PURPOSE: CDROM (ISO 9660) filesystem driver * PROGRAMMER: Art Yerkes * Eric Kohl */ /* INCLUDES *****************************************************************/ #include "cdfs.h" #define NDEBUG #include /* FUNCTIONS ****************************************************************/ static __inline int msf_to_lba (UCHAR m, UCHAR s, UCHAR f) { return (((m * 60) + s) * 75 + f) - 150; } static VOID CdfsGetPVDData( PUCHAR Buffer, PCDINFO CdInfo) { PPVD Pvd; USHORT i; PUCHAR pc; PWCHAR pw; union { ULONG Value; UCHAR Part[4]; } Serial; Pvd = (PPVD)Buffer; /* Calculate the volume serial number */ Serial.Value = 0; for (i = 0; i < 2048; i += 4) { /* DON'T optimize this to ULONG!!! (breaks overflow) */ Serial.Part[3] += Buffer[i+0]; Serial.Part[2] += Buffer[i+1]; Serial.Part[1] += Buffer[i+2]; Serial.Part[0] += Buffer[i+3]; } CdInfo->SerialNumber = Serial.Value; /* Extract the volume label */ pc = Pvd->VolumeId; pw = CdInfo->VolumeLabel; for (i = 0; i < (MAXIMUM_VOLUME_LABEL_LENGTH / sizeof(WCHAR)) - 1; i++) { *pw++ = (WCHAR)*pc++; } *pw = 0; /* Trim trailing spaces */ while (pw > CdInfo->VolumeLabel) { if (*--pw != ' ') break; /* Remove the space */ *pw = '\0'; /* Decrease size */ i--; } CdInfo->VolumeLabelLength = i * sizeof(WCHAR); CdInfo->VolumeSpaceSize = Pvd->VolumeSpaceSizeL; CdInfo->RootStart = Pvd->RootDirRecord.ExtentLocationL; CdInfo->RootSize = Pvd->RootDirRecord.DataLengthL; DPRINT("VolumeSerial: %08lx\n", CdInfo->SerialNumber); DPRINT("VolumeLabel: '%S'\n", CdInfo->VolumeLabel); DPRINT("VolumeLabelLength: %lu\n", CdInfo->VolumeLabelLength); DPRINT("VolumeSize: %lu\n", Pvd->VolumeSpaceSizeL); DPRINT("RootStart: %lu\n", Pvd->RootDirRecord.ExtentLocationL); DPRINT("RootSize: %lu\n", Pvd->RootDirRecord.DataLengthL); DPRINT("PathTableSize: %lu\n", Pvd->PathTableSizeL); DPRINT("PathTablePos: %lu\n", Pvd->LPathTablePos); DPRINT("OptPathTablePos: %lu\n", Pvd->LOptPathTablePos); #if 0 DbgPrint("******** PVD **********\n"); DbgPrint("VdType: %d\n", Pvd->VdType); DbgPrint("StandardId: '%.*s'\n", 5, Pvd->StandardId); DbgPrint("VdVersion: %d\n", Pvd->VdVersion); DbgPrint("SystemId: '%.*s'\n", 32, Pvd->SystemId); DbgPrint("VolumeId: '%.*s'\n", 32, Pvd->VolumeId); DbgPrint("VolumeSpaceSizeL: %d (%x)\n", Pvd->VolumeSpaceSizeL, Pvd->VolumeSpaceSizeL); DbgPrint("VolumeSpaceSizeM: %d (%x)\n", Pvd->VolumeSpaceSizeM, Pvd->VolumeSpaceSizeM); DbgPrint("VolumeSetSize: %d (%x)\n", Pvd->VolumeSequenceNumber, Pvd->VolumeSequenceNumber); DbgPrint("VolumeSequenceNumber: %d (%x)\n", Pvd->VolumeSequenceNumber, Pvd->VolumeSequenceNumber); DbgPrint("LogicalBlockSize: %d (%x)\n", Pvd->LogicalBlockSize, Pvd->LogicalBlockSize); DbgPrint("PathTableSizeL: %d (%x)\n", Pvd->PathTableSizeL, Pvd->PathTableSizeL); DbgPrint("PathTableSizeM: %d (%x)\n", Pvd->PathTableSizeM, Pvd->PathTableSizeM); DbgPrint("LPathTablePos: %d (%x)\n", Pvd->LPathTablePos, Pvd->LPathTablePos); DbgPrint("LOptPathTablePos: %d (%x)\n", Pvd->LOptPathTablePos, Pvd->LOptPathTablePos); DbgPrint("MPathTablePos: %d (%x)\n", Pvd->MPathTablePos, Pvd->MPathTablePos); DbgPrint("MOptPathTablePos: %d (%x)\n", Pvd->MOptPathTablePos, Pvd->MOptPathTablePos); DbgPrint("VolumeSetIdentifier: '%.*s'\n", 128, Pvd->VolumeSetIdentifier); DbgPrint("PublisherIdentifier: '%.*s'\n", 128, Pvd->PublisherIdentifier); DbgPrint("******** Root *********\n"); DbgPrint("RecordLength: %d\n", Pvd->RootDirRecord.RecordLength); DbgPrint("ExtAttrRecordLength: %d\n", Pvd->RootDirRecord.ExtAttrRecordLength); DbgPrint("ExtentLocationL: %d\n", Pvd->RootDirRecord.ExtentLocationL); DbgPrint("DataLengthL: %d\n", Pvd->RootDirRecord.DataLengthL); DbgPrint("Year: %d\n", Pvd->RootDirRecord.Year); DbgPrint("Month: %d\n", Pvd->RootDirRecord.Month); DbgPrint("Day: %d\n", Pvd->RootDirRecord.Day); DbgPrint("Hour: %d\n", Pvd->RootDirRecord.Hour); DbgPrint("Minute: %d\n", Pvd->RootDirRecord.Minute); DbgPrint("Second: %d\n", Pvd->RootDirRecord.Second); DbgPrint("TimeZone: %d\n", Pvd->RootDirRecord.TimeZone); DbgPrint("FileFlags: %d\n", Pvd->RootDirRecord.FileFlags); DbgPrint("FileUnitSize: %d\n", Pvd->RootDirRecord.FileUnitSize); DbgPrint("InterleaveGapSize: %d\n", Pvd->RootDirRecord.InterleaveGapSize); DbgPrint("VolumeSequenceNumber: %d\n", Pvd->RootDirRecord.VolumeSequenceNumber); DbgPrint("FileIdLength: %d\n", Pvd->RootDirRecord.FileIdLength); DbgPrint("FileId: '%.*s'\n", Pvd->RootDirRecord.FileId); DbgPrint("***********************\n"); #endif } static VOID CdfsGetSVDData( PUCHAR Buffer, PCDINFO CdInfo) { PSVD Svd; ULONG JolietLevel = 0; Svd = (PSVD)Buffer; DPRINT("EscapeSequences: '%.32s'\n", Svd->EscapeSequences); if (strncmp((PCHAR)Svd->EscapeSequences, "%/@", 3) == 0) { DPRINT("Joliet extension found (UCS-2 Level 1)\n"); JolietLevel = 1; } else if (strncmp((PCHAR)Svd->EscapeSequences, "%/C", 3) == 0) { DPRINT("Joliet extension found (UCS-2 Level 2)\n"); JolietLevel = 2; } else if (strncmp((PCHAR)Svd->EscapeSequences, "%/E", 3) == 0) { DPRINT("Joliet extension found (UCS-2 Level 3)\n"); JolietLevel = 3; } CdInfo->JolietLevel = JolietLevel; if (JolietLevel != 0) { CdInfo->RootStart = Svd->RootDirRecord.ExtentLocationL; CdInfo->RootSize = Svd->RootDirRecord.DataLengthL; DPRINT("RootStart: %lu\n", Svd->RootDirRecord.ExtentLocationL); DPRINT("RootSize: %lu\n", Svd->RootDirRecord.DataLengthL); } } static NTSTATUS CdfsGetVolumeData( PDEVICE_OBJECT DeviceObject, PCDINFO CdInfo) { PUCHAR Buffer; NTSTATUS Status; ULONG Sector; PVD_HEADER VdHeader; ULONG Size; ULONG Offset; CDROM_TOC Toc; DPRINT("CdfsGetVolumeData\n"); Buffer = ExAllocatePoolWithTag(NonPagedPool, CDFS_BASIC_SECTOR, CDFS_TAG); if (Buffer == NULL) return STATUS_INSUFFICIENT_RESOURCES; Size = sizeof(Toc); Status = CdfsDeviceIoControl(DeviceObject, IOCTL_CDROM_READ_TOC, NULL, 0, &Toc, &Size, TRUE); if (NT_SUCCESS(Status)) { DPRINT("FirstTrack %u, LastTrack %u, TrackNumber %u\n", Toc.FirstTrack, Toc.LastTrack, Toc.TrackData[0].TrackNumber); Offset = Toc.TrackData[0].Address[1] * 60 * 75; Offset += Toc.TrackData[0].Address[2] * 75; Offset += Toc.TrackData[0].Address[3]; if (Offset >= 150) { /* Remove MSF numbering offset of first frame */ /* FIXME: should be done only for real cdroms? */ Offset -= 150; } } else { DPRINT1("Allowing mount of CDFS volume on non-CD device\n"); Offset = 0; } CdInfo->VolumeOffset = Offset; DPRINT("Offset of first track in last session %u\n", Offset); CdInfo->JolietLevel = 0; VdHeader = (PVD_HEADER)Buffer; Buffer[0] = 0; for (Sector = CDFS_PRIMARY_DESCRIPTOR_LOCATION; Sector < 100 && Buffer[0] != 255; Sector++) { /* Read the Primary Volume Descriptor (PVD) */ Status = CdfsReadSectors(DeviceObject, Sector + Offset, 1, Buffer, TRUE); if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(Buffer, CDFS_TAG); return Status; } if (Sector == CDFS_PRIMARY_DESCRIPTOR_LOCATION) { DPRINT("CD-identifier: [%.5s]\n", Buffer + 1); if (Buffer[0] != 1 || Buffer[1] != 'C' || Buffer[2] != 'D' || Buffer[3] != '0' || Buffer[4] != '0' || Buffer[5] != '1') { ExFreePoolWithTag(Buffer, CDFS_TAG); return STATUS_UNRECOGNIZED_VOLUME; } } switch (VdHeader->VdType) { case 0: DPRINT("BootVolumeDescriptor found!\n"); break; case 1: DPRINT("PrimaryVolumeDescriptor found!\n"); CdfsGetPVDData(Buffer, CdInfo); break; case 2: DPRINT("SupplementaryVolumeDescriptor found!\n"); CdfsGetSVDData(Buffer, CdInfo); break; case 3: DPRINT("VolumePartitionDescriptor found!\n"); break; case 255: DPRINT("VolumeDescriptorSetTerminator found!\n"); break; default: DPRINT1("Unknown volume descriptor type %u found!\n", VdHeader->VdType); break; } } ExFreePoolWithTag(Buffer, CDFS_TAG); return STATUS_SUCCESS; } static NTSTATUS CdfsMountVolume( PDEVICE_OBJECT DeviceObject, PIRP Irp) { PDEVICE_EXTENSION DeviceExt = NULL; PDEVICE_OBJECT NewDeviceObject = NULL; PDEVICE_OBJECT DeviceToMount; PIO_STACK_LOCATION Stack; PFCB Fcb = NULL; PCCB Ccb = NULL; PVPB Vpb; NTSTATUS Status; CDINFO CdInfo; DEVICE_TYPE FilesystemDeviceType; DPRINT("CdfsMountVolume() called\n"); if (DeviceObject == CdfsGlobalData->CdFsDeviceObject) { FilesystemDeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM; } else if (DeviceObject == CdfsGlobalData->HddFsDeviceObject) { FilesystemDeviceType = FILE_DEVICE_DISK_FILE_SYSTEM; } else { Status = STATUS_INVALID_DEVICE_REQUEST; goto ByeBye; } Stack = IoGetCurrentIrpStackLocation(Irp); DeviceToMount = Stack->Parameters.MountVolume.DeviceObject; Vpb = Stack->Parameters.MountVolume.Vpb; Status = CdfsGetVolumeData(DeviceToMount, &CdInfo); if (!NT_SUCCESS(Status)) { goto ByeBye; } Status = IoCreateDevice(CdfsGlobalData->DriverObject, sizeof(DEVICE_EXTENSION), NULL, FilesystemDeviceType, DeviceToMount->Characteristics, FALSE, &NewDeviceObject); if (!NT_SUCCESS(Status)) goto ByeBye; NewDeviceObject->Flags = NewDeviceObject->Flags | DO_DIRECT_IO; NewDeviceObject->Flags &= ~DO_VERIFY_VOLUME; DeviceExt = (PVOID)NewDeviceObject->DeviceExtension; RtlZeroMemory(DeviceExt, sizeof(DEVICE_EXTENSION)); Vpb->SerialNumber = CdInfo.SerialNumber; Vpb->VolumeLabelLength = CdInfo.VolumeLabelLength; RtlCopyMemory(Vpb->VolumeLabel, CdInfo.VolumeLabel, CdInfo.VolumeLabelLength); RtlCopyMemory(&DeviceExt->CdInfo, &CdInfo, sizeof(CDINFO)); NewDeviceObject->Vpb = DeviceToMount->Vpb; DeviceExt->VolumeDevice = NewDeviceObject; DeviceExt->StorageDevice = DeviceToMount; DeviceExt->StorageDevice->Vpb->DeviceObject = NewDeviceObject; DeviceExt->StorageDevice->Vpb->RealDevice = DeviceExt->StorageDevice; DeviceExt->StorageDevice->Vpb->Flags |= VPB_MOUNTED; NewDeviceObject->StackSize = DeviceExt->StorageDevice->StackSize + 1; NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; /* Close (and cleanup) might be called from IoCreateStreamFileObject * but we use this resource from CdfsCleanup, therefore it should be * initialized no later than this. */ ExInitializeResourceLite(&DeviceExt->DirResource); DeviceExt->StreamFileObject = IoCreateStreamFileObject(NULL, DeviceExt->StorageDevice); Fcb = CdfsCreateFCB(NULL); if (Fcb == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ByeBye; } Ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), CDFS_CCB_TAG); if (Ccb == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ByeBye; } RtlZeroMemory(Ccb, sizeof(CCB)); DeviceExt->StreamFileObject->ReadAccess = TRUE; DeviceExt->StreamFileObject->WriteAccess = FALSE; DeviceExt->StreamFileObject->DeleteAccess = FALSE; DeviceExt->StreamFileObject->FsContext = Fcb; DeviceExt->StreamFileObject->FsContext2 = Ccb; DeviceExt->StreamFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers; DeviceExt->StreamFileObject->PrivateCacheMap = NULL; DeviceExt->StreamFileObject->Vpb = DeviceExt->Vpb; Ccb->PtrFileObject = DeviceExt->StreamFileObject; Fcb->FileObject = DeviceExt->StreamFileObject; Fcb->DevExt = (PDEVICE_EXTENSION)DeviceExt->StorageDevice; Fcb->Flags = FCB_IS_VOLUME_STREAM; Fcb->RFCB.FileSize.QuadPart = (DeviceExt->CdInfo.VolumeSpaceSize + DeviceExt->CdInfo.VolumeOffset) * BLOCKSIZE; Fcb->RFCB.ValidDataLength = Fcb->RFCB.AllocationSize = Fcb->RFCB.FileSize; Fcb->Entry.ExtentLocationL = 0; Fcb->Entry.DataLengthL = (DeviceExt->CdInfo.VolumeSpaceSize + DeviceExt->CdInfo.VolumeOffset) * BLOCKSIZE; _SEH2_TRY { CcInitializeCacheMap(DeviceExt->StreamFileObject, (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize), TRUE, &(CdfsGlobalData->CacheMgrCallbacks), Fcb); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); goto ByeBye; } _SEH2_END; ExInitializeResourceLite(&DeviceExt->VcbResource); KeInitializeSpinLock(&DeviceExt->FcbListLock); InitializeListHead(&DeviceExt->FcbListHead); FsRtlNotifyInitializeSync(&DeviceExt->NotifySync); InitializeListHead(&DeviceExt->NotifyList); Status = STATUS_SUCCESS; ByeBye: if (!NT_SUCCESS(Status)) { /* Cleanup */ if (DeviceExt && DeviceExt->StreamFileObject) ObDereferenceObject(DeviceExt->StreamFileObject); if (Fcb) ExFreePoolWithTag(Fcb, CDFS_NONPAGED_FCB_TAG); if (NewDeviceObject) IoDeleteDevice(NewDeviceObject); } DPRINT("CdfsMountVolume() done (Status: %lx)\n", Status); return Status; } static NTSTATUS CdfsVerifyVolume( PDEVICE_OBJECT DeviceObject, PIRP Irp) { PDEVICE_EXTENSION DeviceExt; PIO_STACK_LOCATION Stack; NTSTATUS Status; CDINFO CdInfo; PLIST_ENTRY Entry; PFCB Fcb; PVPB VpbToVerify; DPRINT("CdfsVerifyVolume() called\n"); DeviceExt = DeviceObject->DeviceExtension; Stack = IoGetCurrentIrpStackLocation (Irp); VpbToVerify = Stack->Parameters.VerifyVolume.Vpb; FsRtlEnterFileSystem(); ExAcquireResourceExclusiveLite(&DeviceExt->VcbResource, TRUE); if (!(VpbToVerify->RealDevice->Flags & DO_VERIFY_VOLUME)) { DPRINT1("Volume has been verified!\n"); ExReleaseResourceLite (&DeviceExt->VcbResource); FsRtlExitFileSystem(); return STATUS_SUCCESS; } DPRINT1("Device object %p Device to verify %p\n", DeviceObject, VpbToVerify->RealDevice); Status = CdfsGetVolumeData(VpbToVerify->RealDevice, &CdInfo); if (NT_SUCCESS(Status) && CdInfo.SerialNumber == VpbToVerify->SerialNumber && CdInfo.VolumeLabelLength == VpbToVerify->VolumeLabelLength && !wcsncmp(CdInfo.VolumeLabel, VpbToVerify->VolumeLabel, CdInfo.VolumeLabelLength)) { DPRINT1("Same volume!\n"); /* FIXME: Flush and purge metadata */ Status = STATUS_SUCCESS; } else { DPRINT1("Different volume!\n"); /* FIXME: force volume dismount */ Entry = DeviceExt->FcbListHead.Flink; while (Entry != &DeviceExt->FcbListHead) { Fcb = (PFCB)CONTAINING_RECORD(Entry, FCB, FcbListEntry); DPRINT1("OpenFile %wZ RefCount %ld\n", &Fcb->PathName, Fcb->RefCount); Entry = Entry->Flink; } Status = STATUS_WRONG_VOLUME; } VpbToVerify->RealDevice->Flags &= ~DO_VERIFY_VOLUME; ExReleaseResourceLite(&DeviceExt->VcbResource); FsRtlExitFileSystem(); return Status; } NTSTATUS NTAPI CdfsSetCompression( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIO_STACK_LOCATION Stack; USHORT CompressionState; UNREFERENCED_PARAMETER(DeviceObject); Stack = IoGetCurrentIrpStackLocation(Irp); if (Stack->Parameters.DeviceIoControl.InputBufferLength != sizeof(CompressionState)) return STATUS_INVALID_DEVICE_REQUEST; CompressionState = *(USHORT *)Irp->AssociatedIrp.SystemBuffer; if (CompressionState != COMPRESSION_FORMAT_NONE) return STATUS_INVALID_PARAMETER; return STATUS_SUCCESS; } NTSTATUS NTAPI CdfsFileSystemControl( PCDFS_IRP_CONTEXT IrpContext) { PIRP Irp; PDEVICE_OBJECT DeviceObject; PIO_STACK_LOCATION Stack; NTSTATUS Status; DPRINT("CdfsFileSystemControl() called\n"); ASSERT(IrpContext); DeviceObject = IrpContext->DeviceObject; Irp = IrpContext->Irp; Stack = IrpContext->Stack; Irp->IoStatus.Information = 0; switch (IrpContext->MinorFunction) { case IRP_MN_KERNEL_CALL: case IRP_MN_USER_FS_REQUEST: switch (Stack->Parameters.DeviceIoControl.IoControlCode) { case FSCTL_SET_COMPRESSION: DPRINT("CDFS: IRP_MN_USER_FS_REQUEST / FSCTL_SET_COMPRESSION\n"); Status = CdfsSetCompression(DeviceObject, Irp); break; default: DPRINT1("CDFS: IRP_MN_USER_FS_REQUEST / Unknown IoControlCode 0x%x\n", Stack->Parameters.DeviceIoControl.IoControlCode); Status = STATUS_INVALID_DEVICE_REQUEST; } break; case IRP_MN_MOUNT_VOLUME: DPRINT("CDFS: IRP_MN_MOUNT_VOLUME\n"); Status = CdfsMountVolume(DeviceObject, Irp); break; case IRP_MN_VERIFY_VOLUME: DPRINT1("CDFS: IRP_MN_VERIFY_VOLUME\n"); Status = CdfsVerifyVolume(DeviceObject, Irp); break; default: DPRINT1("CDFS FSC: MinorFunction %u\n", Stack->MinorFunction); Status = STATUS_INVALID_DEVICE_REQUEST; break; } return Status; } /* EOF */