3 * Copyright (C) 2002, 2003 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
22 * FILE: drivers/filesystems/cdfs/fsctl.c
23 * PURPOSE: CDROM (ISO 9660) filesystem driver
24 * PROGRAMMER: Art Yerkes
28 /* INCLUDES *****************************************************************/
35 /* FUNCTIONS ****************************************************************/
38 int msf_to_lba (UCHAR m
, UCHAR s
, UCHAR f
)
40 return (((m
* 60) + s
) * 75 + f
) - 150;
63 /* Calculate the volume serial number */
65 for (i
= 0; i
< 2048; i
+= 4)
67 /* DON'T optimize this to ULONG!!! (breaks overflow) */
68 Serial
.Part
[3] += Buffer
[i
+0];
69 Serial
.Part
[2] += Buffer
[i
+1];
70 Serial
.Part
[1] += Buffer
[i
+2];
71 Serial
.Part
[0] += Buffer
[i
+3];
73 CdInfo
->SerialNumber
= Serial
.Value
;
75 /* Extract the volume label */
77 pw
= CdInfo
->VolumeLabel
;
78 for (i
= 0; i
< (MAXIMUM_VOLUME_LABEL_LENGTH
/ sizeof(WCHAR
)) - 1; i
++)
84 /* Trim trailing spaces */
85 while (pw
> CdInfo
->VolumeLabel
)
87 if (*--pw
!= ' ') break;
89 /* Remove the space */
96 CdInfo
->VolumeLabelLength
= i
* sizeof(WCHAR
);
98 CdInfo
->VolumeSpaceSize
= Pvd
->VolumeSpaceSizeL
;
99 CdInfo
->RootStart
= Pvd
->RootDirRecord
.ExtentLocationL
;
100 CdInfo
->RootSize
= Pvd
->RootDirRecord
.DataLengthL
;
102 DPRINT("VolumeSerial: %08lx\n", CdInfo
->SerialNumber
);
103 DPRINT("VolumeLabel: '%S'\n", CdInfo
->VolumeLabel
);
104 DPRINT("VolumeLabelLength: %lu\n", CdInfo
->VolumeLabelLength
);
105 DPRINT("VolumeSize: %lu\n", Pvd
->VolumeSpaceSizeL
);
106 DPRINT("RootStart: %lu\n", Pvd
->RootDirRecord
.ExtentLocationL
);
107 DPRINT("RootSize: %lu\n", Pvd
->RootDirRecord
.DataLengthL
);
108 DPRINT("PathTableSize: %lu\n", Pvd
->PathTableSizeL
);
109 DPRINT("PathTablePos: %lu\n", Pvd
->LPathTablePos
);
110 DPRINT("OptPathTablePos: %lu\n", Pvd
->LOptPathTablePos
);
113 DbgPrint("******** PVD **********\n");
114 DbgPrint("VdType: %d\n", Pvd
->VdType
);
115 DbgPrint("StandardId: '%.*s'\n", 5, Pvd
->StandardId
);
116 DbgPrint("VdVersion: %d\n", Pvd
->VdVersion
);
117 DbgPrint("SystemId: '%.*s'\n", 32, Pvd
->SystemId
);
118 DbgPrint("VolumeId: '%.*s'\n", 32, Pvd
->VolumeId
);
119 DbgPrint("VolumeSpaceSizeL: %d (%x)\n", Pvd
->VolumeSpaceSizeL
, Pvd
->VolumeSpaceSizeL
);
120 DbgPrint("VolumeSpaceSizeM: %d (%x)\n", Pvd
->VolumeSpaceSizeM
, Pvd
->VolumeSpaceSizeM
);
121 DbgPrint("VolumeSetSize: %d (%x)\n", Pvd
->VolumeSequenceNumber
, Pvd
->VolumeSequenceNumber
);
122 DbgPrint("VolumeSequenceNumber: %d (%x)\n", Pvd
->VolumeSequenceNumber
, Pvd
->VolumeSequenceNumber
);
123 DbgPrint("LogicalBlockSize: %d (%x)\n", Pvd
->LogicalBlockSize
, Pvd
->LogicalBlockSize
);
124 DbgPrint("PathTableSizeL: %d (%x)\n", Pvd
->PathTableSizeL
, Pvd
->PathTableSizeL
);
125 DbgPrint("PathTableSizeM: %d (%x)\n", Pvd
->PathTableSizeM
, Pvd
->PathTableSizeM
);
126 DbgPrint("LPathTablePos: %d (%x)\n", Pvd
->LPathTablePos
, Pvd
->LPathTablePos
);
127 DbgPrint("LOptPathTablePos: %d (%x)\n", Pvd
->LOptPathTablePos
, Pvd
->LOptPathTablePos
);
128 DbgPrint("MPathTablePos: %d (%x)\n", Pvd
->MPathTablePos
, Pvd
->MPathTablePos
);
129 DbgPrint("MOptPathTablePos: %d (%x)\n", Pvd
->MOptPathTablePos
, Pvd
->MOptPathTablePos
);
130 DbgPrint("VolumeSetIdentifier: '%.*s'\n", 128, Pvd
->VolumeSetIdentifier
);
131 DbgPrint("PublisherIdentifier: '%.*s'\n", 128, Pvd
->PublisherIdentifier
);
132 DbgPrint("******** Root *********\n");
133 DbgPrint("RecordLength: %d\n", Pvd
->RootDirRecord
.RecordLength
);
134 DbgPrint("ExtAttrRecordLength: %d\n", Pvd
->RootDirRecord
.ExtAttrRecordLength
);
135 DbgPrint("ExtentLocationL: %d\n", Pvd
->RootDirRecord
.ExtentLocationL
);
136 DbgPrint("DataLengthL: %d\n", Pvd
->RootDirRecord
.DataLengthL
);
137 DbgPrint("Year: %d\n", Pvd
->RootDirRecord
.Year
);
138 DbgPrint("Month: %d\n", Pvd
->RootDirRecord
.Month
);
139 DbgPrint("Day: %d\n", Pvd
->RootDirRecord
.Day
);
140 DbgPrint("Hour: %d\n", Pvd
->RootDirRecord
.Hour
);
141 DbgPrint("Minute: %d\n", Pvd
->RootDirRecord
.Minute
);
142 DbgPrint("Second: %d\n", Pvd
->RootDirRecord
.Second
);
143 DbgPrint("TimeZone: %d\n", Pvd
->RootDirRecord
.TimeZone
);
144 DbgPrint("FileFlags: %d\n", Pvd
->RootDirRecord
.FileFlags
);
145 DbgPrint("FileUnitSize: %d\n", Pvd
->RootDirRecord
.FileUnitSize
);
146 DbgPrint("InterleaveGapSize: %d\n", Pvd
->RootDirRecord
.InterleaveGapSize
);
147 DbgPrint("VolumeSequenceNumber: %d\n", Pvd
->RootDirRecord
.VolumeSequenceNumber
);
148 DbgPrint("FileIdLength: %d\n", Pvd
->RootDirRecord
.FileIdLength
);
149 DbgPrint("FileId: '%.*s'\n", Pvd
->RootDirRecord
.FileId
);
150 DbgPrint("***********************\n");
162 ULONG JolietLevel
= 0;
166 DPRINT("EscapeSequences: '%.32s'\n", Svd
->EscapeSequences
);
168 if (strncmp((PCHAR
)Svd
->EscapeSequences
, "%/@", 3) == 0)
170 DPRINT("Joliet extension found (UCS-2 Level 1)\n");
173 else if (strncmp((PCHAR
)Svd
->EscapeSequences
, "%/C", 3) == 0)
175 DPRINT("Joliet extension found (UCS-2 Level 2)\n");
178 else if (strncmp((PCHAR
)Svd
->EscapeSequences
, "%/E", 3) == 0)
180 DPRINT("Joliet extension found (UCS-2 Level 3)\n");
184 CdInfo
->JolietLevel
= JolietLevel
;
186 if (JolietLevel
!= 0)
188 CdInfo
->RootStart
= Svd
->RootDirRecord
.ExtentLocationL
;
189 CdInfo
->RootSize
= Svd
->RootDirRecord
.DataLengthL
;
191 DPRINT("RootStart: %lu\n", Svd
->RootDirRecord
.ExtentLocationL
);
192 DPRINT("RootSize: %lu\n", Svd
->RootDirRecord
.DataLengthL
);
200 PDEVICE_OBJECT DeviceObject
,
211 DPRINT("CdfsGetVolumeData\n");
213 Buffer
= ExAllocatePoolWithTag(NonPagedPool
, CDFS_BASIC_SECTOR
, CDFS_TAG
);
215 return STATUS_INSUFFICIENT_RESOURCES
;
218 Status
= CdfsDeviceIoControl(DeviceObject
,
219 IOCTL_CDROM_READ_TOC
,
225 if (NT_SUCCESS(Status
))
228 DPRINT("FirstTrack %u, LastTrack %u, TrackNumber %u\n",
229 Toc
.FirstTrack
, Toc
.LastTrack
, Toc
.TrackData
[0].TrackNumber
);
231 Offset
= Toc
.TrackData
[0].Address
[1] * 60 * 75;
232 Offset
+= Toc
.TrackData
[0].Address
[2] * 75;
233 Offset
+= Toc
.TrackData
[0].Address
[3];
236 /* Remove MSF numbering offset of first frame */
237 /* FIXME: should be done only for real cdroms? */
243 DPRINT1("Allowing mount of CDFS volume on non-CD device\n");
247 CdInfo
->VolumeOffset
= Offset
;
248 DPRINT("Offset of first track in last session %u\n", Offset
);
250 CdInfo
->JolietLevel
= 0;
251 VdHeader
= (PVD_HEADER
)Buffer
;
254 for (Sector
= CDFS_PRIMARY_DESCRIPTOR_LOCATION
; Sector
< 100 && Buffer
[0] != 255; Sector
++)
256 /* Read the Primary Volume Descriptor (PVD) */
257 Status
= CdfsReadSectors(DeviceObject
,
262 if (!NT_SUCCESS(Status
))
264 ExFreePoolWithTag(Buffer
, CDFS_TAG
);
268 if (Sector
== CDFS_PRIMARY_DESCRIPTOR_LOCATION
)
270 DPRINT("CD-identifier: [%.5s]\n", Buffer
+ 1);
272 if (Buffer
[0] != 1 || Buffer
[1] != 'C' || Buffer
[2] != 'D' ||
273 Buffer
[3] != '0' || Buffer
[4] != '0' || Buffer
[5] != '1')
275 ExFreePoolWithTag(Buffer
, CDFS_TAG
);
276 return STATUS_UNRECOGNIZED_VOLUME
;
280 switch (VdHeader
->VdType
)
283 DPRINT("BootVolumeDescriptor found!\n");
287 DPRINT("PrimaryVolumeDescriptor found!\n");
288 CdfsGetPVDData(Buffer
, CdInfo
);
292 DPRINT("SupplementaryVolumeDescriptor found!\n");
293 CdfsGetSVDData(Buffer
, CdInfo
);
297 DPRINT("VolumePartitionDescriptor found!\n");
301 DPRINT("VolumeDescriptorSetTerminator found!\n");
305 DPRINT1("Unknown volume descriptor type %u found!\n", VdHeader
->VdType
);
310 ExFreePoolWithTag(Buffer
, CDFS_TAG
);
312 return STATUS_SUCCESS
;
319 PDEVICE_OBJECT DeviceObject
,
322 PDEVICE_EXTENSION DeviceExt
= NULL
;
323 PDEVICE_OBJECT NewDeviceObject
= NULL
;
324 PDEVICE_OBJECT DeviceToMount
;
325 PIO_STACK_LOCATION Stack
;
331 DEVICE_TYPE FilesystemDeviceType
;
333 DPRINT("CdfsMountVolume() called\n");
335 if (DeviceObject
== CdfsGlobalData
->CdFsDeviceObject
)
337 FilesystemDeviceType
= FILE_DEVICE_CD_ROM_FILE_SYSTEM
;
339 else if (DeviceObject
== CdfsGlobalData
->HddFsDeviceObject
)
341 FilesystemDeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
345 Status
= STATUS_INVALID_DEVICE_REQUEST
;
349 Stack
= IoGetCurrentIrpStackLocation(Irp
);
350 DeviceToMount
= Stack
->Parameters
.MountVolume
.DeviceObject
;
351 Vpb
= Stack
->Parameters
.MountVolume
.Vpb
;
353 Status
= CdfsGetVolumeData(DeviceToMount
, &CdInfo
);
354 if (!NT_SUCCESS(Status
))
359 Status
= IoCreateDevice(CdfsGlobalData
->DriverObject
,
360 sizeof(DEVICE_EXTENSION
),
362 FilesystemDeviceType
,
363 DeviceToMount
->Characteristics
,
366 if (!NT_SUCCESS(Status
))
369 NewDeviceObject
->Flags
= NewDeviceObject
->Flags
| DO_DIRECT_IO
;
370 NewDeviceObject
->Flags
&= ~DO_VERIFY_VOLUME
;
371 DeviceExt
= (PVOID
)NewDeviceObject
->DeviceExtension
;
372 RtlZeroMemory(DeviceExt
,
373 sizeof(DEVICE_EXTENSION
));
375 Vpb
->SerialNumber
= CdInfo
.SerialNumber
;
376 Vpb
->VolumeLabelLength
= CdInfo
.VolumeLabelLength
;
377 RtlCopyMemory(Vpb
->VolumeLabel
, CdInfo
.VolumeLabel
, CdInfo
.VolumeLabelLength
);
378 RtlCopyMemory(&DeviceExt
->CdInfo
, &CdInfo
, sizeof(CDINFO
));
380 NewDeviceObject
->Vpb
= DeviceToMount
->Vpb
;
382 DeviceExt
->VolumeDevice
= NewDeviceObject
;
383 DeviceExt
->StorageDevice
= DeviceToMount
;
384 DeviceExt
->StorageDevice
->Vpb
->DeviceObject
= NewDeviceObject
;
385 DeviceExt
->StorageDevice
->Vpb
->RealDevice
= DeviceExt
->StorageDevice
;
386 DeviceExt
->StorageDevice
->Vpb
->Flags
|= VPB_MOUNTED
;
387 NewDeviceObject
->StackSize
= DeviceExt
->StorageDevice
->StackSize
+ 1;
388 NewDeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
390 /* Close (and cleanup) might be called from IoCreateStreamFileObject
391 * but we use this resource from CdfsCleanup, therefore it should be
392 * initialized no later than this. */
393 ExInitializeResourceLite(&DeviceExt
->DirResource
);
395 DeviceExt
->StreamFileObject
= IoCreateStreamFileObject(NULL
,
396 DeviceExt
->StorageDevice
);
398 Fcb
= CdfsCreateFCB(NULL
);
401 Status
= STATUS_INSUFFICIENT_RESOURCES
;
405 Ccb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(CCB
), CDFS_CCB_TAG
);
408 Status
= STATUS_INSUFFICIENT_RESOURCES
;
415 DeviceExt
->StreamFileObject
->ReadAccess
= TRUE
;
416 DeviceExt
->StreamFileObject
->WriteAccess
= FALSE
;
417 DeviceExt
->StreamFileObject
->DeleteAccess
= FALSE
;
418 DeviceExt
->StreamFileObject
->FsContext
= Fcb
;
419 DeviceExt
->StreamFileObject
->FsContext2
= Ccb
;
420 DeviceExt
->StreamFileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
421 DeviceExt
->StreamFileObject
->PrivateCacheMap
= NULL
;
422 DeviceExt
->StreamFileObject
->Vpb
= DeviceExt
->Vpb
;
423 Ccb
->PtrFileObject
= DeviceExt
->StreamFileObject
;
424 Fcb
->FileObject
= DeviceExt
->StreamFileObject
;
425 Fcb
->DevExt
= (PDEVICE_EXTENSION
)DeviceExt
->StorageDevice
;
427 Fcb
->Flags
= FCB_IS_VOLUME_STREAM
;
429 Fcb
->RFCB
.FileSize
.QuadPart
= (DeviceExt
->CdInfo
.VolumeSpaceSize
+ DeviceExt
->CdInfo
.VolumeOffset
) * BLOCKSIZE
;
430 Fcb
->RFCB
.ValidDataLength
= Fcb
->RFCB
.AllocationSize
= Fcb
->RFCB
.FileSize
;
432 Fcb
->Entry
.ExtentLocationL
= 0;
433 Fcb
->Entry
.DataLengthL
= (DeviceExt
->CdInfo
.VolumeSpaceSize
+ DeviceExt
->CdInfo
.VolumeOffset
) * BLOCKSIZE
;
437 CcInitializeCacheMap(DeviceExt
->StreamFileObject
,
438 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
440 &(CdfsGlobalData
->CacheMgrCallbacks
),
443 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
445 Status
= _SEH2_GetExceptionCode();
450 ExInitializeResourceLite(&DeviceExt
->VcbResource
);
452 KeInitializeSpinLock(&DeviceExt
->FcbListLock
);
453 InitializeListHead(&DeviceExt
->FcbListHead
);
455 FsRtlNotifyInitializeSync(&DeviceExt
->NotifySync
);
456 InitializeListHead(&DeviceExt
->NotifyList
);
458 Status
= STATUS_SUCCESS
;
461 if (!NT_SUCCESS(Status
))
464 if (DeviceExt
&& DeviceExt
->StreamFileObject
)
465 ObDereferenceObject(DeviceExt
->StreamFileObject
);
467 ExFreePoolWithTag(Fcb
, CDFS_NONPAGED_FCB_TAG
);
469 IoDeleteDevice(NewDeviceObject
);
472 DPRINT("CdfsMountVolume() done (Status: %lx)\n", Status
);
481 PDEVICE_OBJECT DeviceObject
,
484 PDEVICE_EXTENSION DeviceExt
;
485 PIO_STACK_LOCATION Stack
;
492 DPRINT("CdfsVerifyVolume() called\n");
494 DeviceExt
= DeviceObject
->DeviceExtension
;
496 Stack
= IoGetCurrentIrpStackLocation (Irp
);
497 VpbToVerify
= Stack
->Parameters
.VerifyVolume
.Vpb
;
499 FsRtlEnterFileSystem();
500 ExAcquireResourceExclusiveLite(&DeviceExt
->VcbResource
,
503 if (!(VpbToVerify
->RealDevice
->Flags
& DO_VERIFY_VOLUME
))
505 DPRINT1("Volume has been verified!\n");
506 ExReleaseResourceLite (&DeviceExt
->VcbResource
);
507 FsRtlExitFileSystem();
508 return STATUS_SUCCESS
;
511 DPRINT1("Device object %p Device to verify %p\n", DeviceObject
, VpbToVerify
->RealDevice
);
513 Status
= CdfsGetVolumeData(VpbToVerify
->RealDevice
,
515 if (NT_SUCCESS(Status
) &&
516 CdInfo
.SerialNumber
== VpbToVerify
->SerialNumber
&&
517 CdInfo
.VolumeLabelLength
== VpbToVerify
->VolumeLabelLength
&&
518 !wcsncmp(CdInfo
.VolumeLabel
, VpbToVerify
->VolumeLabel
, CdInfo
.VolumeLabelLength
))
520 DPRINT1("Same volume!\n");
522 /* FIXME: Flush and purge metadata */
524 Status
= STATUS_SUCCESS
;
528 DPRINT1("Different volume!\n");
530 /* FIXME: force volume dismount */
531 Entry
= DeviceExt
->FcbListHead
.Flink
;
532 while (Entry
!= &DeviceExt
->FcbListHead
)
534 Fcb
= (PFCB
)CONTAINING_RECORD(Entry
, FCB
, FcbListEntry
);
535 DPRINT1("OpenFile %wZ RefCount %ld\n", &Fcb
->PathName
, Fcb
->RefCount
);
537 Entry
= Entry
->Flink
;
540 Status
= STATUS_WRONG_VOLUME
;
543 VpbToVerify
->RealDevice
->Flags
&= ~DO_VERIFY_VOLUME
;
545 ExReleaseResourceLite(&DeviceExt
->VcbResource
);
546 FsRtlExitFileSystem();
555 IN PDEVICE_OBJECT DeviceObject
,
558 PIO_STACK_LOCATION Stack
;
559 USHORT CompressionState
;
561 UNREFERENCED_PARAMETER(DeviceObject
);
563 Stack
= IoGetCurrentIrpStackLocation(Irp
);
565 if (Stack
->Parameters
.DeviceIoControl
.InputBufferLength
!= sizeof(CompressionState
))
566 return STATUS_INVALID_DEVICE_REQUEST
;
568 CompressionState
= *(USHORT
*)Irp
->AssociatedIrp
.SystemBuffer
;
569 if (CompressionState
!= COMPRESSION_FORMAT_NONE
)
570 return STATUS_INVALID_PARAMETER
;
572 return STATUS_SUCCESS
;
578 CdfsFileSystemControl(
579 PCDFS_IRP_CONTEXT IrpContext
)
582 PDEVICE_OBJECT DeviceObject
;
583 PIO_STACK_LOCATION Stack
;
586 DPRINT("CdfsFileSystemControl() called\n");
590 DeviceObject
= IrpContext
->DeviceObject
;
591 Irp
= IrpContext
->Irp
;
592 Stack
= IrpContext
->Stack
;
594 Irp
->IoStatus
.Information
= 0;
596 switch (IrpContext
->MinorFunction
)
598 case IRP_MN_KERNEL_CALL
:
599 case IRP_MN_USER_FS_REQUEST
:
600 switch (Stack
->Parameters
.DeviceIoControl
.IoControlCode
)
602 case FSCTL_SET_COMPRESSION
:
603 DPRINT("CDFS: IRP_MN_USER_FS_REQUEST / FSCTL_SET_COMPRESSION\n");
604 Status
= CdfsSetCompression(DeviceObject
, Irp
);
608 DPRINT1("CDFS: IRP_MN_USER_FS_REQUEST / Unknown IoControlCode 0x%x\n",
609 Stack
->Parameters
.DeviceIoControl
.IoControlCode
);
610 Status
= STATUS_INVALID_DEVICE_REQUEST
;
614 case IRP_MN_MOUNT_VOLUME
:
615 DPRINT("CDFS: IRP_MN_MOUNT_VOLUME\n");
616 Status
= CdfsMountVolume(DeviceObject
, Irp
);
619 case IRP_MN_VERIFY_VOLUME
:
620 DPRINT1("CDFS: IRP_MN_VERIFY_VOLUME\n");
621 Status
= CdfsVerifyVolume(DeviceObject
, Irp
);
625 DPRINT1("CDFS FSC: MinorFunction %u\n", Stack
->MinorFunction
);
626 Status
= STATUS_INVALID_DEVICE_REQUEST
;