- There is no reason for CDFS to use CcRos-specific functions, it ends up with the...
[reactos.git] / reactos / drivers / filesystems / cdfs / fsctl.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2002, 2003 ReactOS Team
4 *
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.
9 *
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.
14 *
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.
18 */
19 /* $Id$
20 *
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
26 * Eric Kohl
27 */
28
29 /* INCLUDES *****************************************************************/
30
31 #include "cdfs.h"
32
33 #define NDEBUG
34 #include <debug.h>
35
36 /* FUNCTIONS ****************************************************************/
37
38 static __inline
39 int msf_to_lba (UCHAR m, UCHAR s, UCHAR f)
40 {
41 return (((m * 60) + s) * 75 + f) - 150;
42 }
43
44
45 static VOID
46 CdfsGetPVDData(PUCHAR Buffer,
47 PCDINFO CdInfo)
48 {
49 PPVD Pvd;
50 ULONG i;
51 PUCHAR pc;
52 PWCHAR pw;
53
54 union
55 {
56 ULONG Value;
57 UCHAR Part[4];
58 } Serial;
59
60 Pvd = (PPVD)Buffer;
61
62 /* Calculate the volume serial number */
63 Serial.Value = 0;
64 for (i = 0; i < 2048; i += 4)
65 {
66 /* DON'T optimize this to ULONG!!! (breaks overflow) */
67 Serial.Part[0] += Buffer[i+3];
68 Serial.Part[1] += Buffer[i+2];
69 Serial.Part[2] += Buffer[i+1];
70 Serial.Part[3] += Buffer[i+0];
71 }
72 CdInfo->SerialNumber = Serial.Value;
73
74 /* Extract the volume label */
75 pc = Pvd->VolumeId;
76 pw = CdInfo->VolumeLabel;
77 for (i = 0; i < MAXIMUM_VOLUME_LABEL_LENGTH && *pc != ' '; i++)
78 {
79 *pw++ = (WCHAR)*pc++;
80 }
81 *pw = 0;
82 CdInfo->VolumeLabelLength = i * sizeof(WCHAR);
83
84 CdInfo->VolumeSpaceSize = Pvd->VolumeSpaceSizeL;
85 CdInfo->RootStart = Pvd->RootDirRecord.ExtentLocationL;
86 CdInfo->RootSize = Pvd->RootDirRecord.DataLengthL;
87
88 DPRINT("VolumeSerial: %08lx\n", CdInfo->SerialNumber);
89 DPRINT("VolumeLabel: '%S'\n", CdInfo->VolumeLabel);
90 DPRINT("VolumeLabelLength: %lu\n", CdInfo->VolumeLabelLength);
91 DPRINT("VolumeSize: %lu\n", Pvd->VolumeSpaceSizeL);
92 DPRINT("RootStart: %lu\n", Pvd->RootDirRecord.ExtentLocationL);
93 DPRINT("RootSize: %lu\n", Pvd->RootDirRecord.DataLengthL);
94 DPRINT("PathTableSize: %lu\n", Pvd->PathTableSizeL);
95 DPRINT("PathTablePos: %lu\n", Pvd->LPathTablePos);
96 DPRINT("OptPathTablePos: %lu\n", Pvd->LOptPathTablePos);
97
98 #if 0
99 DbgPrint("******** PVD **********\n");
100 DbgPrint("VdType: %d\n", Pvd->VdType);
101 DbgPrint("StandardId: '%.*s'\n", 5, Pvd->StandardId);
102 DbgPrint("VdVersion: %d\n", Pvd->VdVersion);
103 DbgPrint("SystemId: '%.*s'\n", 32, Pvd->SystemId);
104 DbgPrint("VolumeId: '%.*s'\n", 32, Pvd->VolumeId);
105 DbgPrint("VolumeSpaceSizeL: %d (%x)\n", Pvd->VolumeSpaceSizeL, Pvd->VolumeSpaceSizeL);
106 DbgPrint("VolumeSpaceSizeM: %d (%x)\n", Pvd->VolumeSpaceSizeM, Pvd->VolumeSpaceSizeM);
107 DbgPrint("VolumeSetSize: %d (%x)\n", Pvd->VolumeSequenceNumber, Pvd->VolumeSequenceNumber);
108 DbgPrint("VolumeSequenceNumber: %d (%x)\n", Pvd->VolumeSequenceNumber, Pvd->VolumeSequenceNumber);
109 DbgPrint("LogicalBlockSize: %d (%x)\n", Pvd->LogicalBlockSize, Pvd->LogicalBlockSize);
110 DbgPrint("PathTableSizeL: %d (%x)\n", Pvd->PathTableSizeL, Pvd->PathTableSizeL);
111 DbgPrint("PathTableSizeM: %d (%x)\n", Pvd->PathTableSizeM, Pvd->PathTableSizeM);
112 DbgPrint("LPathTablePos: %d (%x)\n", Pvd->LPathTablePos, Pvd->LPathTablePos);
113 DbgPrint("LOptPathTablePos: %d (%x)\n", Pvd->LOptPathTablePos, Pvd->LOptPathTablePos);
114 DbgPrint("MPathTablePos: %d (%x)\n", Pvd->MPathTablePos, Pvd->MPathTablePos);
115 DbgPrint("MOptPathTablePos: %d (%x)\n", Pvd->MOptPathTablePos, Pvd->MOptPathTablePos);
116 DbgPrint("VolumeSetIdentifier: '%.*s'\n", 128, Pvd->VolumeSetIdentifier);
117 DbgPrint("PublisherIdentifier: '%.*s'\n", 128, Pvd->PublisherIdentifier);
118 DbgPrint("******** Root *********\n");
119 DbgPrint("RecordLength: %d\n", Pvd->RootDirRecord.RecordLength);
120 DbgPrint("ExtAttrRecordLength: %d\n", Pvd->RootDirRecord.ExtAttrRecordLength);
121 DbgPrint("ExtentLocationL: %d\n", Pvd->RootDirRecord.ExtentLocationL);
122 DbgPrint("DataLengthL: %d\n", Pvd->RootDirRecord.DataLengthL);
123 DbgPrint("Year: %d\n", Pvd->RootDirRecord.Year);
124 DbgPrint("Month: %d\n", Pvd->RootDirRecord.Month);
125 DbgPrint("Day: %d\n", Pvd->RootDirRecord.Day);
126 DbgPrint("Hour: %d\n", Pvd->RootDirRecord.Hour);
127 DbgPrint("Minute: %d\n", Pvd->RootDirRecord.Minute);
128 DbgPrint("Second: %d\n", Pvd->RootDirRecord.Second);
129 DbgPrint("TimeZone: %d\n", Pvd->RootDirRecord.TimeZone);
130 DbgPrint("FileFlags: %d\n", Pvd->RootDirRecord.FileFlags);
131 DbgPrint("FileUnitSize: %d\n", Pvd->RootDirRecord.FileUnitSize);
132 DbgPrint("InterleaveGapSize: %d\n", Pvd->RootDirRecord.InterleaveGapSize);
133 DbgPrint("VolumeSequenceNumber: %d\n", Pvd->RootDirRecord.VolumeSequenceNumber);
134 DbgPrint("FileIdLength: %d\n", Pvd->RootDirRecord.FileIdLength);
135 DbgPrint("FileId: '%.*s'\n", Pvd->RootDirRecord.FileId);
136 DbgPrint("***********************\n");
137 #endif
138 }
139
140
141 static VOID
142 CdfsGetSVDData(PUCHAR Buffer,
143 PCDINFO CdInfo)
144 {
145 PSVD Svd;
146 ULONG JolietLevel = 0;
147
148 Svd = (PSVD)Buffer;
149
150 DPRINT("EscapeSequences: '%.32s'\n", Svd->EscapeSequences);
151
152 if (strncmp((PCHAR)Svd->EscapeSequences, "%/@", 3) == 0)
153 {
154 DPRINT("Joliet extension found (UCS-2 Level 1)\n");
155 JolietLevel = 1;
156 }
157 else if (strncmp((PCHAR)Svd->EscapeSequences, "%/C", 3) == 0)
158 {
159 DPRINT("Joliet extension found (UCS-2 Level 2)\n");
160 JolietLevel = 2;
161 }
162 else if (strncmp((PCHAR)Svd->EscapeSequences, "%/E", 3) == 0)
163 {
164 DPRINT("Joliet extension found (UCS-2 Level 3)\n");
165 JolietLevel = 3;
166 }
167
168 CdInfo->JolietLevel = JolietLevel;
169
170 if (JolietLevel != 0)
171 {
172 CdInfo->RootStart = Svd->RootDirRecord.ExtentLocationL;
173 CdInfo->RootSize = Svd->RootDirRecord.DataLengthL;
174
175 DPRINT("RootStart: %lu\n", Svd->RootDirRecord.ExtentLocationL);
176 DPRINT("RootSize: %lu\n", Svd->RootDirRecord.DataLengthL);
177 }
178 }
179
180
181 static NTSTATUS
182 CdfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
183 PCDINFO CdInfo)
184 {
185 PUCHAR Buffer;
186 NTSTATUS Status;
187 ULONG Sector;
188 PVD_HEADER VdHeader;
189 ULONG Size;
190 ULONG Offset;
191 ULONG i;
192 struct
193 {
194 UCHAR Length[2];
195 UCHAR FirstSession;
196 UCHAR LastSession;
197 TRACK_DATA TrackData;
198 }
199 Toc;
200
201 DPRINT("CdfsGetVolumeData\n");
202
203 Buffer = ExAllocatePool(NonPagedPool,
204 CDFS_BASIC_SECTOR);
205 if (Buffer == NULL)
206 return STATUS_INSUFFICIENT_RESOURCES;
207
208 Size = sizeof(Toc);
209 Status = CdfsDeviceIoControl(DeviceObject,
210 IOCTL_CDROM_GET_LAST_SESSION,
211 NULL,
212 0,
213 &Toc,
214 &Size,
215 TRUE);
216 if (!NT_SUCCESS(Status))
217 {
218 ExFreePool(Buffer);
219 return Status;
220 }
221
222 DPRINT("FirstSession %d, LastSession %d, FirstTrack %d\n",
223 Toc.FirstSession, Toc.LastSession, Toc.TrackData.TrackNumber);
224
225 Offset = 0;
226 for (i = 0; i < 4; i++)
227 {
228 Offset = (Offset << 8) + Toc.TrackData.Address[i];
229 }
230 CdInfo->VolumeOffset = Offset;
231
232 DPRINT("Offset of first track in last session %d\n", Offset);
233
234 CdInfo->JolietLevel = 0;
235 VdHeader = (PVD_HEADER)Buffer;
236 Buffer[0] = 0;
237
238 for (Sector = CDFS_PRIMARY_DESCRIPTOR_LOCATION; Sector < 100 && Buffer[0] != 255; Sector++)
239 {
240 /* Read the Primary Volume Descriptor (PVD) */
241 Status = CdfsReadSectors (DeviceObject,
242 Sector + Offset,
243 1,
244 Buffer,
245 TRUE);
246 if (!NT_SUCCESS(Status))
247 {
248 ExFreePool(Buffer);
249 return Status;
250 }
251
252 if (Sector == CDFS_PRIMARY_DESCRIPTOR_LOCATION)
253 {
254 DPRINT("CD-identifier: [%.5s]\n", Buffer + 1);
255
256 if (Buffer[0] != 1 || Buffer[1] != 'C' || Buffer[2] != 'D' ||
257 Buffer[3] != '0' || Buffer[4] != '0' || Buffer[5] != '1')
258 {
259 ExFreePool(Buffer);
260 return STATUS_UNRECOGNIZED_VOLUME;
261 }
262 }
263
264 switch (VdHeader->VdType)
265 {
266 case 0:
267 DPRINT("BootVolumeDescriptor found!\n");
268 break;
269
270 case 1:
271 DPRINT("PrimaryVolumeDescriptor found!\n");
272 CdfsGetPVDData(Buffer, CdInfo);
273 break;
274
275 case 2:
276 DPRINT("SupplementaryVolumeDescriptor found!\n");
277 CdfsGetSVDData(Buffer, CdInfo);
278 break;
279
280 case 3:
281 DPRINT("VolumePartitionDescriptor found!\n");
282 break;
283
284 case 255:
285 DPRINT("VolumeDescriptorSetTerminator found!\n");
286 break;
287
288 default:
289 DPRINT1("Unknown volume descriptor type %u found!\n", VdHeader->VdType);
290 break;
291 }
292 }
293
294 ExFreePool(Buffer);
295
296 return(STATUS_SUCCESS);
297 }
298
299
300 static NTSTATUS
301 CdfsMountVolume(PDEVICE_OBJECT DeviceObject,
302 PIRP Irp)
303 {
304 PDEVICE_EXTENSION DeviceExt = NULL;
305 PDEVICE_OBJECT NewDeviceObject = NULL;
306 PDEVICE_OBJECT DeviceToMount;
307 PIO_STACK_LOCATION Stack;
308 PFCB Fcb = NULL;
309 PCCB Ccb = NULL;
310 PVPB Vpb;
311 NTSTATUS Status;
312 CDINFO CdInfo;
313
314 DPRINT("CdfsMountVolume() called\n");
315
316 if (DeviceObject != CdfsGlobalData->DeviceObject)
317 {
318 Status = STATUS_INVALID_DEVICE_REQUEST;
319 goto ByeBye;
320 }
321
322 Stack = IoGetCurrentIrpStackLocation(Irp);
323 DeviceToMount = Stack->Parameters.MountVolume.DeviceObject;
324 Vpb = Stack->Parameters.MountVolume.Vpb;
325
326 Status = CdfsGetVolumeData(DeviceToMount, &CdInfo);
327 if (!NT_SUCCESS(Status))
328 {
329 goto ByeBye;
330 }
331
332 Status = IoCreateDevice(CdfsGlobalData->DriverObject,
333 sizeof(DEVICE_EXTENSION),
334 NULL,
335 FILE_DEVICE_FILE_SYSTEM,
336 // FILE_DEVICE_DISK_FILE_SYSTEM,
337 0,
338 FALSE,
339 &NewDeviceObject);
340 if (!NT_SUCCESS(Status))
341 goto ByeBye;
342
343 NewDeviceObject->Flags = NewDeviceObject->Flags | DO_DIRECT_IO;
344 NewDeviceObject->Flags &= ~DO_VERIFY_VOLUME;
345 DeviceExt = (PVOID)NewDeviceObject->DeviceExtension;
346 RtlZeroMemory(DeviceExt,
347 sizeof(DEVICE_EXTENSION));
348
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));
353
354 NewDeviceObject->Vpb = DeviceToMount->Vpb;
355
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 NewDeviceObject->StackSize = DeviceExt->StorageDevice->StackSize + 1;
362 NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
363
364 DeviceExt->StreamFileObject = IoCreateStreamFileObject(NULL,
365 DeviceExt->StorageDevice);
366
367 Fcb = CdfsCreateFCB(NULL);
368 if (Fcb == NULL)
369 {
370 Status = STATUS_INSUFFICIENT_RESOURCES;
371 goto ByeBye;
372 }
373
374 Ccb = ExAllocatePoolWithTag(NonPagedPool,
375 sizeof(CCB),
376 TAG_CCB);
377 if (Ccb == NULL)
378 {
379 Status = STATUS_INSUFFICIENT_RESOURCES;
380 goto ByeBye;
381 }
382 RtlZeroMemory(Ccb,
383 sizeof(CCB));
384
385 DeviceExt->StreamFileObject->FsContext = Fcb;
386 DeviceExt->StreamFileObject->FsContext2 = Ccb;
387 DeviceExt->StreamFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
388 DeviceExt->StreamFileObject->PrivateCacheMap = NULL;
389 DeviceExt->StreamFileObject->Vpb = DeviceExt->Vpb;
390 Ccb->PtrFileObject = DeviceExt->StreamFileObject;
391 Fcb->FileObject = DeviceExt->StreamFileObject;
392 Fcb->DevExt = (PDEVICE_EXTENSION)DeviceExt->StorageDevice;
393
394 Fcb->Flags = FCB_IS_VOLUME_STREAM;
395
396 Fcb->RFCB.FileSize.QuadPart = (DeviceExt->CdInfo.VolumeSpaceSize + DeviceExt->CdInfo.VolumeOffset) * BLOCKSIZE;
397 Fcb->RFCB.ValidDataLength = Fcb->RFCB.AllocationSize = Fcb->RFCB.FileSize;
398
399 Fcb->Entry.ExtentLocationL = 0;
400 Fcb->Entry.DataLengthL = (DeviceExt->CdInfo.VolumeSpaceSize + DeviceExt->CdInfo.VolumeOffset) * BLOCKSIZE;
401
402 CcInitializeCacheMap(DeviceExt->StreamFileObject,
403 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
404 TRUE,
405 NULL,
406 NULL);
407
408 ExInitializeResourceLite(&DeviceExt->VcbResource);
409 ExInitializeResourceLite(&DeviceExt->DirResource);
410
411 KeInitializeSpinLock(&DeviceExt->FcbListLock);
412 InitializeListHead(&DeviceExt->FcbListHead);
413
414 Status = STATUS_SUCCESS;
415
416 ByeBye:
417 if (!NT_SUCCESS(Status))
418 {
419 /* Cleanup */
420 if (DeviceExt && DeviceExt->StreamFileObject)
421 ObDereferenceObject(DeviceExt->StreamFileObject);
422 if (Fcb)
423 ExFreePool(Fcb);
424 if (Ccb)
425 ExFreePool(Ccb);
426 if (NewDeviceObject)
427 IoDeleteDevice(NewDeviceObject);
428 }
429
430 DPRINT("CdfsMountVolume() done (Status: %lx)\n", Status);
431
432 return(Status);
433 }
434
435
436 static NTSTATUS
437 CdfsVerifyVolume(PDEVICE_OBJECT DeviceObject,
438 PIRP Irp)
439 {
440 PDEVICE_EXTENSION DeviceExt;
441 PDEVICE_OBJECT DeviceToVerify;
442 PIO_STACK_LOCATION Stack;
443 NTSTATUS Status;
444 CDINFO CdInfo;
445
446 PLIST_ENTRY Entry;
447 PFCB Fcb;
448
449 DPRINT1 ("CdfsVerifyVolume() called\n");
450
451 #if 0
452 if (DeviceObject != CdfsGlobalData->DeviceObject)
453 {
454 DPRINT1("DeviceObject != CdfsGlobalData->DeviceObject\n");
455 return(STATUS_INVALID_DEVICE_REQUEST);
456 }
457 #endif
458
459 DeviceExt = DeviceObject->DeviceExtension;
460
461 Stack = IoGetCurrentIrpStackLocation (Irp);
462 DeviceToVerify = Stack->Parameters.VerifyVolume.DeviceObject;
463
464 ExAcquireResourceExclusiveLite (&DeviceExt->VcbResource,
465 TRUE);
466
467 if (!(DeviceToVerify->Flags & DO_VERIFY_VOLUME))
468 {
469 DPRINT1 ("Volume has been verified!\n");
470 ExReleaseResourceLite (&DeviceExt->VcbResource);
471 return STATUS_SUCCESS;
472 }
473
474 DPRINT1 ("Device object %p Device to verify %p\n", DeviceObject, DeviceToVerify);
475
476 Status = CdfsGetVolumeData (DeviceToVerify,
477 &CdInfo);
478 if (NT_SUCCESS(Status) &&
479 CdInfo.SerialNumber == DeviceToVerify->Vpb->SerialNumber &&
480 CdInfo.VolumeLabelLength == DeviceToVerify->Vpb->VolumeLabelLength &&
481 !wcsncmp (CdInfo.VolumeLabel, DeviceToVerify->Vpb->VolumeLabel, CdInfo.VolumeLabelLength))
482 {
483 DPRINT1 ("Same volume!\n");
484
485 /* FIXME: Flush and purge metadata */
486
487 Status = STATUS_SUCCESS;
488 }
489 else
490 {
491 DPRINT1 ("Different volume!\n");
492
493 /* FIXME: force volume dismount */
494 Entry = DeviceExt->FcbListHead.Flink;
495 while (Entry != &DeviceExt->FcbListHead)
496 {
497 Fcb = (PFCB)CONTAINING_RECORD(Entry, FCB, FcbListEntry);
498 DPRINT1("OpenFile %S RefCount %ld\n", Fcb->PathName, Fcb->RefCount);
499
500 Entry = Entry->Flink;
501 }
502
503 Status = STATUS_WRONG_VOLUME;
504 }
505
506 DeviceToVerify->Flags &= ~DO_VERIFY_VOLUME;
507
508 ExReleaseResourceLite (&DeviceExt->VcbResource);
509
510 return Status;
511 }
512
513
514 NTSTATUS STDCALL
515 CdfsFileSystemControl(PDEVICE_OBJECT DeviceObject,
516 PIRP Irp)
517 {
518 PIO_STACK_LOCATION Stack;
519 NTSTATUS Status;
520
521 DPRINT("CdfsFileSystemControl() called\n");
522
523 Stack = IoGetCurrentIrpStackLocation(Irp);
524
525 switch (Stack->MinorFunction)
526 {
527 case IRP_MN_USER_FS_REQUEST:
528 DPRINT1("CDFS: IRP_MN_USER_FS_REQUEST\n");
529 Status = STATUS_INVALID_DEVICE_REQUEST;
530 break;
531
532 case IRP_MN_MOUNT_VOLUME:
533 DPRINT("CDFS: IRP_MN_MOUNT_VOLUME\n");
534 Status = CdfsMountVolume(DeviceObject, Irp);
535 break;
536
537 case IRP_MN_VERIFY_VOLUME:
538 DPRINT1("CDFS: IRP_MN_VERIFY_VOLUME\n");
539 Status = CdfsVerifyVolume(DeviceObject, Irp);
540 break;
541
542 default:
543 DPRINT1("CDFS FSC: MinorFunction %d\n", Stack->MinorFunction);
544 Status = STATUS_INVALID_DEVICE_REQUEST;
545 break;
546 }
547
548 Irp->IoStatus.Status = Status;
549 Irp->IoStatus.Information = 0;
550
551 IoCompleteRequest(Irp, IO_NO_INCREMENT);
552
553 return(Status);
554 }
555
556 /* EOF */