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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 /* $Id: fsctl.c,v 1.17 2003/11/13 15:25:08 ekohl Exp $
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * FILE: drivers/fs/cdfs/fsctl.c
24 * PURPOSE: CDROM (ISO 9660) filesystem driver
25 * PROGRAMMER: Art Yerkes
29 /* INCLUDES *****************************************************************/
31 #include <ddk/ntddk.h>
32 #include <ntos/minmax.h>
40 /* FUNCTIONS ****************************************************************/
43 int msf_to_lba (BYTE m
, BYTE s
, BYTE f
)
45 return (((m
* 60) + s
) * 75 + f
) - 150;
50 CdfsGetPVDData(PUCHAR Buffer
,
66 /* Calculate the volume serial number */
68 for (i
= 0; i
< 2048; i
+= 4)
70 /* DON'T optimize this to ULONG!!! (breaks overflow) */
71 Serial
.Part
[0] += Buffer
[i
+3];
72 Serial
.Part
[1] += Buffer
[i
+2];
73 Serial
.Part
[2] += Buffer
[i
+1];
74 Serial
.Part
[3] += Buffer
[i
+0];
76 CdInfo
->SerialNumber
= Serial
.Value
;
78 /* Extract the volume label */
80 pw
= CdInfo
->VolumeLabel
;
81 for (i
= 0; i
< MAXIMUM_VOLUME_LABEL_LENGTH
&& *pc
!= ' '; i
++)
86 CdInfo
->VolumeLabelLength
= i
;
88 CdInfo
->VolumeSpaceSize
= Pvd
->VolumeSpaceSizeL
;
89 CdInfo
->RootStart
= Pvd
->RootDirRecord
.ExtentLocationL
;
90 CdInfo
->RootSize
= Pvd
->RootDirRecord
.DataLengthL
;
92 DPRINT("VolumeSerial: %08lx\n", CdInfo
->SerialNumber
);
93 DPRINT("VolumeLabel: '%S'\n", CdInfo
->VolumeLabel
);
94 DPRINT("VolumeLabelLength: %lu\n", CdInfo
->VolumeLabelLength
);
95 DPRINT("VolumeSize: %lu\n", Pvd
->VolumeSpaceSizeL
);
96 DPRINT("RootStart: %lu\n", Pvd
->RootDirRecord
.ExtentLocationL
);
97 DPRINT("RootSize: %lu\n", Pvd
->RootDirRecord
.DataLengthL
);
100 DbgPrint("******** PVD **********\n");
101 DbgPrint("VdType: %d\n", Pvd
->VdType
);
102 DbgPrint("StandardId: '%.*s'\n", 5, Pvd
->StandardId
);
103 DbgPrint("VdVersion: %d\n", Pvd
->VdVersion
);
104 DbgPrint("SystemId: '%.*s'\n", 32, Pvd
->SystemId
);
105 DbgPrint("VolumeId: '%.*s'\n", 32, Pvd
->VolumeId
);
106 DbgPrint("VolumeSpaceSizeL: %d (%x)\n", Pvd
->VolumeSpaceSizeL
, Pvd
->VolumeSpaceSizeL
);
107 DbgPrint("VolumeSpaceSizeM: %d (%x)\n", Pvd
->VolumeSpaceSizeM
, Pvd
->VolumeSpaceSizeM
);
108 DbgPrint("VolumeSetSize: %d (%x)\n", Pvd
->VolumeSequenceNumber
, Pvd
->VolumeSequenceNumber
);
109 DbgPrint("VolumeSequenceNumber: %d (%x)\n", Pvd
->VolumeSequenceNumber
, Pvd
->VolumeSequenceNumber
);
110 DbgPrint("LogicalBlockSize: %d (%x)\n", Pvd
->LogicalBlockSize
, Pvd
->LogicalBlockSize
);
111 DbgPrint("PathTableSizeL: %d (%x)\n", Pvd
->PathTableSizeL
, Pvd
->PathTableSizeL
);
112 DbgPrint("PathTableSizeM: %d (%x)\n", Pvd
->PathTableSizeM
, Pvd
->PathTableSizeM
);
113 DbgPrint("LPathTablePos: %d (%x)\n", Pvd
->LPathTablePos
, Pvd
->LPathTablePos
);
114 DbgPrint("LOptPathTablePos: %d (%x)\n", Pvd
->LOptPathTablePos
, Pvd
->LOptPathTablePos
);
115 DbgPrint("MPathTablePos: %d (%x)\n", Pvd
->MPathTablePos
, Pvd
->MPathTablePos
);
116 DbgPrint("MOptPathTablePos: %d (%x)\n", Pvd
->MOptPathTablePos
, Pvd
->MOptPathTablePos
);
117 DbgPrint("VolumeSetIdentifier: '%.*s'\n", 128, Pvd
->VolumeSetIdentifier
);
118 DbgPrint("PublisherIdentifier: '%.*s'\n", 128, Pvd
->PublisherIdentifier
);
119 DbgPrint("******** Root *********\n");
120 DbgPrint("RecordLength: %d\n", Pvd
->RootDirRecord
.RecordLength
);
121 DbgPrint("ExtAttrRecordLength: %d\n", Pvd
->RootDirRecord
.ExtAttrRecordLength
);
122 DbgPrint("ExtentLocationL: %d\n", Pvd
->RootDirRecord
.ExtentLocationL
);
123 DbgPrint("DataLengthL: %d\n", Pvd
->RootDirRecord
.DataLengthL
);
124 DbgPrint("Year: %d\n", Pvd
->RootDirRecord
.Year
);
125 DbgPrint("Month: %d\n", Pvd
->RootDirRecord
.Month
);
126 DbgPrint("Day: %d\n", Pvd
->RootDirRecord
.Day
);
127 DbgPrint("Hour: %d\n", Pvd
->RootDirRecord
.Hour
);
128 DbgPrint("Minute: %d\n", Pvd
->RootDirRecord
.Minute
);
129 DbgPrint("Second: %d\n", Pvd
->RootDirRecord
.Second
);
130 DbgPrint("TimeZone: %d\n", Pvd
->RootDirRecord
.TimeZone
);
131 DbgPrint("FileFlags: %d\n", Pvd
->RootDirRecord
.FileFlags
);
132 DbgPrint("FileUnitSize: %d\n", Pvd
->RootDirRecord
.FileUnitSize
);
133 DbgPrint("InterleaveGapSize: %d\n", Pvd
->RootDirRecord
.InterleaveGapSize
);
134 DbgPrint("VolumeSequenceNumber: %d\n", Pvd
->RootDirRecord
.VolumeSequenceNumber
);
135 DbgPrint("FileIdLength: %d\n", Pvd
->RootDirRecord
.FileIdLength
);
136 DbgPrint("FileId: '%.*s'\n", Pvd
->RootDirRecord
.FileId
);
137 DbgPrint("***********************\n");
143 CdfsGetSVDData(PUCHAR Buffer
,
147 ULONG JolietLevel
= 0;
151 DPRINT("EscapeSequences: '%.32s'\n", Svd
->EscapeSequences
);
153 if (strncmp(Svd
->EscapeSequences
, "%/@", 3) == 0)
155 DPRINT("Joliet extension found (UCS-2 Level 1)\n");
158 else if (strncmp(Svd
->EscapeSequences
, "%/C", 3) == 0)
160 DPRINT("Joliet extension found (UCS-2 Level 2)\n");
163 else if (strncmp(Svd
->EscapeSequences
, "%/E", 3) == 0)
165 DPRINT("Joliet extension found (UCS-2 Level 3)\n");
169 CdInfo
->JolietLevel
= JolietLevel
;
171 if (JolietLevel
!= 0)
173 CdInfo
->RootStart
= Svd
->RootDirRecord
.ExtentLocationL
;
174 CdInfo
->RootSize
= Svd
->RootDirRecord
.DataLengthL
;
176 DPRINT("RootStart: %lu\n", Svd
->RootDirRecord
.ExtentLocationL
);
177 DPRINT("RootSize: %lu\n", Svd
->RootDirRecord
.DataLengthL
);
183 CdfsGetVolumeData(PDEVICE_OBJECT DeviceObject
,
198 TRACK_DATA TrackData
;
202 DPRINT("CdfsGetVolumeData\n");
204 Buffer
= ExAllocatePool(NonPagedPool
,
207 return STATUS_INSUFFICIENT_RESOURCES
;
210 Status
= CdfsDeviceIoControl(DeviceObject
,
211 IOCTL_CDROM_GET_LAST_SESSION
,
217 if (!NT_SUCCESS(Status
))
223 DPRINT("FirstSession %d, LastSession %d, FirstTrack %d\n",
224 Toc
.FirstSession
, Toc
.LastSession
, Toc
.TrackData
.TrackNumber
);
227 for (i
= 0; i
< 4; i
++)
229 Offset
= (Offset
<< 8) + Toc
.TrackData
.Address
[i
];
231 CdInfo
->VolumeOffset
= Offset
;
233 DPRINT("Offset of first track in last session %d\n", Offset
);
235 CdInfo
->JolietLevel
= 0;
236 VdHeader
= (PVD_HEADER
)Buffer
;
239 for (Sector
= CDFS_PRIMARY_DESCRIPTOR_LOCATION
; Sector
< 100 && Buffer
[0] != 255; Sector
++)
241 /* Read the Primary Volume Descriptor (PVD) */
242 Status
= CdfsReadSectors (DeviceObject
,
247 if (!NT_SUCCESS(Status
))
253 if (Sector
== CDFS_PRIMARY_DESCRIPTOR_LOCATION
)
255 DPRINT("CD-identifier: [%.5s]\n", Buffer
+ 1);
257 if (Buffer
[0] != 1 || Buffer
[1] != 'C' || Buffer
[2] != 'D' ||
258 Buffer
[3] != '0' || Buffer
[4] != '0' || Buffer
[5] != '1')
261 return STATUS_UNRECOGNIZED_VOLUME
;
265 switch (VdHeader
->VdType
)
268 DPRINT("BootVolumeDescriptor found!\n");
272 DPRINT("PrimaryVolumeDescriptor found!\n");
273 CdfsGetPVDData(Buffer
, CdInfo
);
277 DPRINT("SupplementaryVolumeDescriptor found!\n");
278 CdfsGetSVDData(Buffer
, CdInfo
);
282 DPRINT("VolumePartitionDescriptor found!\n");
286 DPRINT("VolumeDescriptorSetTerminator found!\n");
290 DPRINT1("Unknown volume descriptor type %u found!\n", VdHeader
->VdType
);
297 return(STATUS_SUCCESS
);
302 CdfsMountVolume(PDEVICE_OBJECT DeviceObject
,
305 PDEVICE_EXTENSION DeviceExt
= NULL
;
306 PDEVICE_OBJECT NewDeviceObject
= NULL
;
307 PDEVICE_OBJECT DeviceToMount
;
308 PIO_STACK_LOCATION Stack
;
315 DPRINT("CdfsMountVolume() called\n");
317 if (DeviceObject
!= CdfsGlobalData
->DeviceObject
)
319 Status
= STATUS_INVALID_DEVICE_REQUEST
;
323 Stack
= IoGetCurrentIrpStackLocation(Irp
);
324 DeviceToMount
= Stack
->Parameters
.MountVolume
.DeviceObject
;
325 Vpb
= Stack
->Parameters
.MountVolume
.Vpb
;
327 Status
= CdfsGetVolumeData(DeviceToMount
, &CdInfo
);
328 if (!NT_SUCCESS(Status
))
333 Status
= IoCreateDevice(CdfsGlobalData
->DriverObject
,
334 sizeof(DEVICE_EXTENSION
),
336 FILE_DEVICE_FILE_SYSTEM
,
337 // FILE_DEVICE_DISK_FILE_SYSTEM,
341 if (!NT_SUCCESS(Status
))
344 NewDeviceObject
->Flags
= NewDeviceObject
->Flags
| DO_DIRECT_IO
;
345 DeviceExt
= (PVOID
)NewDeviceObject
->DeviceExtension
;
346 RtlZeroMemory(DeviceExt
,
347 sizeof(DEVICE_EXTENSION
));
349 Vpb
->SerialNumber
= CdInfo
.SerialNumber
;
350 Vpb
->VolumeLabelLength
= CdInfo
.VolumeLabelLength
;
351 RtlCopyMemory(Vpb
->VolumeLabel
, CdInfo
.VolumeLabel
, CdInfo
.VolumeLabelLength
* sizeof(WCHAR
));
352 RtlCopyMemory(&DeviceExt
->CdInfo
, &CdInfo
, sizeof(CDINFO
));
354 NewDeviceObject
->Vpb
= DeviceToMount
->Vpb
;
356 DeviceExt
->VolumeDevice
= NewDeviceObject
;
357 DeviceExt
->StorageDevice
= DeviceToMount
;
358 DeviceExt
->StorageDevice
->Vpb
->DeviceObject
= NewDeviceObject
;
359 DeviceExt
->StorageDevice
->Vpb
->RealDevice
= DeviceExt
->StorageDevice
;
360 DeviceExt
->StorageDevice
->Vpb
->Flags
|= VPB_MOUNTED
;
361 DeviceObject
->StackSize
= DeviceExt
->StorageDevice
->StackSize
+ 1;
362 DeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
364 DeviceExt
->StreamFileObject
= IoCreateStreamFileObject(NULL
,
365 DeviceExt
->StorageDevice
);
367 Fcb
= CdfsCreateFCB(NULL
);
370 Status
= STATUS_INSUFFICIENT_RESOURCES
;
374 Ccb
= ExAllocatePoolWithTag(NonPagedPool
,
379 Status
= STATUS_INSUFFICIENT_RESOURCES
;
385 DeviceExt
->StreamFileObject
->Flags
= DeviceExt
->StreamFileObject
->Flags
| FO_FCB_IS_VALID
| FO_DIRECT_CACHE_PAGING_READ
;
386 DeviceExt
->StreamFileObject
->FsContext
= Fcb
;
387 DeviceExt
->StreamFileObject
->FsContext2
= Ccb
;
388 DeviceExt
->StreamFileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
389 DeviceExt
->StreamFileObject
->PrivateCacheMap
= NULL
;
390 DeviceExt
->StreamFileObject
->Vpb
= DeviceExt
->Vpb
;
391 Ccb
->PtrFileObject
= DeviceExt
->StreamFileObject
;
392 Fcb
->FileObject
= DeviceExt
->StreamFileObject
;
393 Fcb
->DevExt
= (PDEVICE_EXTENSION
)DeviceExt
->StorageDevice
;
395 Fcb
->Flags
= FCB_IS_VOLUME_STREAM
;
397 Fcb
->RFCB
.FileSize
.QuadPart
= (DeviceExt
->CdInfo
.VolumeSpaceSize
+ DeviceExt
->CdInfo
.VolumeOffset
) * BLOCKSIZE
;
398 Fcb
->RFCB
.ValidDataLength
= Fcb
->RFCB
.AllocationSize
= Fcb
->RFCB
.FileSize
;
400 Fcb
->Entry
.ExtentLocationL
= 0;
401 Fcb
->Entry
.DataLengthL
= (DeviceExt
->CdInfo
.VolumeSpaceSize
+ DeviceExt
->CdInfo
.VolumeOffset
) * BLOCKSIZE
;
403 Status
= CcRosInitializeFileCache(DeviceExt
->StreamFileObject
,
405 if (!NT_SUCCESS (Status
))
407 DbgPrint("CcRosInitializeFileCache failed\n");
411 ExInitializeResourceLite(&DeviceExt
->VcbResource
);
412 ExInitializeResourceLite(&DeviceExt
->DirResource
);
414 KeInitializeSpinLock(&DeviceExt
->FcbListLock
);
415 InitializeListHead(&DeviceExt
->FcbListHead
);
417 Status
= STATUS_SUCCESS
;
420 if (!NT_SUCCESS(Status
))
423 if (DeviceExt
&& DeviceExt
->StreamFileObject
)
424 ObDereferenceObject(DeviceExt
->StreamFileObject
);
430 IoDeleteDevice(NewDeviceObject
);
433 DPRINT("CdfsMountVolume() done (Status: %lx)\n", Status
);
440 CdfsVerifyVolume(PDEVICE_OBJECT DeviceObject
,
443 PDEVICE_EXTENSION DeviceExt
;
444 PDEVICE_OBJECT DeviceToVerify
;
445 PIO_STACK_LOCATION Stack
;
452 DPRINT1 ("CdfsVerifyVolume() called\n");
455 if (DeviceObject
!= CdfsGlobalData
->DeviceObject
)
457 DPRINT1("DeviceObject != CdfsGlobalData->DeviceObject\n");
458 return(STATUS_INVALID_DEVICE_REQUEST
);
462 DeviceExt
= DeviceObject
->DeviceExtension
;
464 Stack
= IoGetCurrentIrpStackLocation (Irp
);
465 DeviceToVerify
= Stack
->Parameters
.VerifyVolume
.DeviceObject
;
467 ExAcquireResourceExclusiveLite (&DeviceExt
->VcbResource
,
470 if (!(DeviceToVerify
->Flags
& DO_VERIFY_VOLUME
))
472 DPRINT1 ("Volume has been verified!\n");
473 ExReleaseResourceLite (&DeviceExt
->VcbResource
);
474 return STATUS_SUCCESS
;
477 DPRINT1 ("Device object %p Device to verify %p\n", DeviceObject
, DeviceToVerify
);
479 Status
= CdfsGetVolumeData (DeviceToVerify
,
481 if (NT_SUCCESS(Status
) &&
482 CdInfo
.SerialNumber
== DeviceToVerify
->Vpb
->SerialNumber
&&
483 CdInfo
.VolumeLabelLength
== DeviceToVerify
->Vpb
->VolumeLabelLength
&&
484 !wcsncmp (CdInfo
.VolumeLabel
, DeviceToVerify
->Vpb
->VolumeLabel
, CdInfo
.VolumeLabelLength
))
486 DPRINT1 ("Same volume!\n");
488 /* FIXME: Flush and purge metadata */
490 Status
= STATUS_SUCCESS
;
494 DPRINT1 ("Different volume!\n");
496 /* FIXME: force volume dismount */
497 Entry
= DeviceExt
->FcbListHead
.Flink
;
498 while (Entry
!= &DeviceExt
->FcbListHead
)
500 Fcb
= (PFCB
)CONTAINING_RECORD(Entry
, FCB
, FcbListEntry
);
501 DPRINT1("OpenFile %S RefCount %ld\n", Fcb
->PathName
, Fcb
->RefCount
);
503 Entry
= Entry
->Flink
;
506 Status
= STATUS_WRONG_VOLUME
;
509 DeviceToVerify
->Flags
&= ~DO_VERIFY_VOLUME
;
511 ExReleaseResourceLite (&DeviceExt
->VcbResource
);
518 CdfsFileSystemControl(PDEVICE_OBJECT DeviceObject
,
521 PIO_STACK_LOCATION Stack
;
524 DPRINT("CdfsFileSystemControl() called\n");
526 Stack
= IoGetCurrentIrpStackLocation(Irp
);
528 switch (Stack
->MinorFunction
)
530 case IRP_MN_USER_FS_REQUEST
:
531 DPRINT1("CDFS: IRP_MN_USER_FS_REQUEST\n");
532 Status
= STATUS_INVALID_DEVICE_REQUEST
;
535 case IRP_MN_MOUNT_VOLUME
:
536 DPRINT("CDFS: IRP_MN_MOUNT_VOLUME\n");
537 Status
= CdfsMountVolume(DeviceObject
, Irp
);
540 case IRP_MN_VERIFY_VOLUME
:
541 DPRINT1("CDFS: IRP_MN_VERIFY_VOLUME\n");
542 Status
= CdfsVerifyVolume(DeviceObject
, Irp
);
546 DPRINT1("CDFS FSC: MinorFunction %d\n", Stack
->MinorFunction
);
547 Status
= STATUS_INVALID_DEVICE_REQUEST
;
551 Irp
->IoStatus
.Status
= Status
;
552 Irp
->IoStatus
.Information
= 0;
554 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);