minor corrections by M.Taguchi
[reactos.git] / reactos / drivers / fs / 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: fsctl.c,v 1.17 2003/11/13 15:25:08 ekohl Exp $
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 <ddk/ntddk.h>
32 #include <ntos/minmax.h>
33
34 #define NDEBUG
35 #include <debug.h>
36
37 #include "cdfs.h"
38
39
40 /* FUNCTIONS ****************************************************************/
41
42 static inline
43 int msf_to_lba (BYTE m, BYTE s, BYTE f)
44 {
45 return (((m * 60) + s) * 75 + f) - 150;
46 }
47
48
49 static VOID
50 CdfsGetPVDData(PUCHAR Buffer,
51 PCDINFO CdInfo)
52 {
53 PPVD Pvd;
54 ULONG i;
55 PCHAR pc;
56 PWCHAR pw;
57
58 union
59 {
60 ULONG Value;
61 UCHAR Part[4];
62 } Serial;
63
64 Pvd = (PPVD)Buffer;
65
66 /* Calculate the volume serial number */
67 Serial.Value = 0;
68 for (i = 0; i < 2048; i += 4)
69 {
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];
75 }
76 CdInfo->SerialNumber = Serial.Value;
77
78 /* Extract the volume label */
79 pc = Pvd->VolumeId;
80 pw = CdInfo->VolumeLabel;
81 for (i = 0; i < MAXIMUM_VOLUME_LABEL_LENGTH && *pc != ' '; i++)
82 {
83 *pw++ = (WCHAR)*pc++;
84 }
85 *pw = 0;
86 CdInfo->VolumeLabelLength = i;
87
88 CdInfo->VolumeSpaceSize = Pvd->VolumeSpaceSizeL;
89 CdInfo->RootStart = Pvd->RootDirRecord.ExtentLocationL;
90 CdInfo->RootSize = Pvd->RootDirRecord.DataLengthL;
91
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);
98
99 #if 0
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");
138 #endif
139 }
140
141
142 static VOID
143 CdfsGetSVDData(PUCHAR Buffer,
144 PCDINFO CdInfo)
145 {
146 PSVD Svd;
147 ULONG JolietLevel = 0;
148
149 Svd = (PSVD)Buffer;
150
151 DPRINT("EscapeSequences: '%.32s'\n", Svd->EscapeSequences);
152
153 if (strncmp(Svd->EscapeSequences, "%/@", 3) == 0)
154 {
155 DPRINT("Joliet extension found (UCS-2 Level 1)\n");
156 JolietLevel = 1;
157 }
158 else if (strncmp(Svd->EscapeSequences, "%/C", 3) == 0)
159 {
160 DPRINT("Joliet extension found (UCS-2 Level 2)\n");
161 JolietLevel = 2;
162 }
163 else if (strncmp(Svd->EscapeSequences, "%/E", 3) == 0)
164 {
165 DPRINT("Joliet extension found (UCS-2 Level 3)\n");
166 JolietLevel = 3;
167 }
168
169 CdInfo->JolietLevel = JolietLevel;
170
171 if (JolietLevel != 0)
172 {
173 CdInfo->RootStart = Svd->RootDirRecord.ExtentLocationL;
174 CdInfo->RootSize = Svd->RootDirRecord.DataLengthL;
175
176 DPRINT("RootStart: %lu\n", Svd->RootDirRecord.ExtentLocationL);
177 DPRINT("RootSize: %lu\n", Svd->RootDirRecord.DataLengthL);
178 }
179 }
180
181
182 static NTSTATUS
183 CdfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
184 PCDINFO CdInfo)
185 {
186 PUCHAR Buffer;
187 NTSTATUS Status;
188 ULONG Sector;
189 PVD_HEADER VdHeader;
190 ULONG Size;
191 ULONG Offset;
192 ULONG i;
193 struct
194 {
195 UCHAR Length[2];
196 UCHAR FirstSession;
197 UCHAR LastSession;
198 TRACK_DATA TrackData;
199 }
200 Toc;
201
202 DPRINT("CdfsGetVolumeData\n");
203
204 Buffer = ExAllocatePool(NonPagedPool,
205 CDFS_BASIC_SECTOR);
206 if (Buffer == NULL)
207 return STATUS_INSUFFICIENT_RESOURCES;
208
209 Size = sizeof(Toc);
210 Status = CdfsDeviceIoControl(DeviceObject,
211 IOCTL_CDROM_GET_LAST_SESSION,
212 NULL,
213 0,
214 &Toc,
215 &Size,
216 TRUE);
217 if (!NT_SUCCESS(Status))
218 {
219 ExFreePool(Buffer);
220 return Status;
221 }
222
223 DPRINT("FirstSession %d, LastSession %d, FirstTrack %d\n",
224 Toc.FirstSession, Toc.LastSession, Toc.TrackData.TrackNumber);
225
226 Offset = 0;
227 for (i = 0; i < 4; i++)
228 {
229 Offset = (Offset << 8) + Toc.TrackData.Address[i];
230 }
231 CdInfo->VolumeOffset = Offset;
232
233 DPRINT("Offset of first track in last session %d\n", Offset);
234
235 CdInfo->JolietLevel = 0;
236 VdHeader = (PVD_HEADER)Buffer;
237 Buffer[0] = 0;
238
239 for (Sector = CDFS_PRIMARY_DESCRIPTOR_LOCATION; Sector < 100 && Buffer[0] != 255; Sector++)
240 {
241 /* Read the Primary Volume Descriptor (PVD) */
242 Status = CdfsReadSectors (DeviceObject,
243 Sector + Offset,
244 1,
245 Buffer,
246 TRUE);
247 if (!NT_SUCCESS(Status))
248 {
249 ExFreePool(Buffer);
250 return Status;
251 }
252
253 if (Sector == CDFS_PRIMARY_DESCRIPTOR_LOCATION)
254 {
255 DPRINT("CD-identifier: [%.5s]\n", Buffer + 1);
256
257 if (Buffer[0] != 1 || Buffer[1] != 'C' || Buffer[2] != 'D' ||
258 Buffer[3] != '0' || Buffer[4] != '0' || Buffer[5] != '1')
259 {
260 ExFreePool(Buffer);
261 return STATUS_UNRECOGNIZED_VOLUME;
262 }
263 }
264
265 switch (VdHeader->VdType)
266 {
267 case 0:
268 DPRINT("BootVolumeDescriptor found!\n");
269 break;
270
271 case 1:
272 DPRINT("PrimaryVolumeDescriptor found!\n");
273 CdfsGetPVDData(Buffer, CdInfo);
274 break;
275
276 case 2:
277 DPRINT("SupplementaryVolumeDescriptor found!\n");
278 CdfsGetSVDData(Buffer, CdInfo);
279 break;
280
281 case 3:
282 DPRINT("VolumePartitionDescriptor found!\n");
283 break;
284
285 case 255:
286 DPRINT("VolumeDescriptorSetTerminator found!\n");
287 break;
288
289 default:
290 DPRINT1("Unknown volume descriptor type %u found!\n", VdHeader->VdType);
291 break;
292 }
293 }
294
295 ExFreePool(Buffer);
296
297 return(STATUS_SUCCESS);
298 }
299
300
301 static NTSTATUS
302 CdfsMountVolume(PDEVICE_OBJECT DeviceObject,
303 PIRP Irp)
304 {
305 PDEVICE_EXTENSION DeviceExt = NULL;
306 PDEVICE_OBJECT NewDeviceObject = NULL;
307 PDEVICE_OBJECT DeviceToMount;
308 PIO_STACK_LOCATION Stack;
309 PFCB Fcb = NULL;
310 PCCB Ccb = NULL;
311 PVPB Vpb;
312 NTSTATUS Status;
313 CDINFO CdInfo;
314
315 DPRINT("CdfsMountVolume() called\n");
316
317 if (DeviceObject != CdfsGlobalData->DeviceObject)
318 {
319 Status = STATUS_INVALID_DEVICE_REQUEST;
320 goto ByeBye;
321 }
322
323 Stack = IoGetCurrentIrpStackLocation(Irp);
324 DeviceToMount = Stack->Parameters.MountVolume.DeviceObject;
325 Vpb = Stack->Parameters.MountVolume.Vpb;
326
327 Status = CdfsGetVolumeData(DeviceToMount, &CdInfo);
328 if (!NT_SUCCESS(Status))
329 {
330 goto ByeBye;
331 }
332
333 Status = IoCreateDevice(CdfsGlobalData->DriverObject,
334 sizeof(DEVICE_EXTENSION),
335 NULL,
336 FILE_DEVICE_FILE_SYSTEM,
337 // FILE_DEVICE_DISK_FILE_SYSTEM,
338 0,
339 FALSE,
340 &NewDeviceObject);
341 if (!NT_SUCCESS(Status))
342 goto ByeBye;
343
344 NewDeviceObject->Flags = NewDeviceObject->Flags | DO_DIRECT_IO;
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 DeviceObject->StackSize = DeviceExt->StorageDevice->StackSize + 1;
362 DeviceObject->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->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;
394
395 Fcb->Flags = FCB_IS_VOLUME_STREAM;
396
397 Fcb->RFCB.FileSize.QuadPart = (DeviceExt->CdInfo.VolumeSpaceSize + DeviceExt->CdInfo.VolumeOffset) * BLOCKSIZE;
398 Fcb->RFCB.ValidDataLength = Fcb->RFCB.AllocationSize = Fcb->RFCB.FileSize;
399
400 Fcb->Entry.ExtentLocationL = 0;
401 Fcb->Entry.DataLengthL = (DeviceExt->CdInfo.VolumeSpaceSize + DeviceExt->CdInfo.VolumeOffset) * BLOCKSIZE;
402
403 Status = CcRosInitializeFileCache(DeviceExt->StreamFileObject,
404 PAGE_SIZE);
405 if (!NT_SUCCESS (Status))
406 {
407 DbgPrint("CcRosInitializeFileCache failed\n");
408 goto ByeBye;
409 }
410
411 ExInitializeResourceLite(&DeviceExt->VcbResource);
412 ExInitializeResourceLite(&DeviceExt->DirResource);
413
414 KeInitializeSpinLock(&DeviceExt->FcbListLock);
415 InitializeListHead(&DeviceExt->FcbListHead);
416
417 Status = STATUS_SUCCESS;
418
419 ByeBye:
420 if (!NT_SUCCESS(Status))
421 {
422 /* Cleanup */
423 if (DeviceExt && DeviceExt->StreamFileObject)
424 ObDereferenceObject(DeviceExt->StreamFileObject);
425 if (Fcb)
426 ExFreePool(Fcb);
427 if (Ccb)
428 ExFreePool(Ccb);
429 if (NewDeviceObject)
430 IoDeleteDevice(NewDeviceObject);
431 }
432
433 DPRINT("CdfsMountVolume() done (Status: %lx)\n", Status);
434
435 return(Status);
436 }
437
438
439 static NTSTATUS
440 CdfsVerifyVolume(PDEVICE_OBJECT DeviceObject,
441 PIRP Irp)
442 {
443 PDEVICE_EXTENSION DeviceExt;
444 PDEVICE_OBJECT DeviceToVerify;
445 PIO_STACK_LOCATION Stack;
446 NTSTATUS Status;
447 CDINFO CdInfo;
448
449 PLIST_ENTRY Entry;
450 PFCB Fcb;
451
452 DPRINT1 ("CdfsVerifyVolume() called\n");
453
454 #if 0
455 if (DeviceObject != CdfsGlobalData->DeviceObject)
456 {
457 DPRINT1("DeviceObject != CdfsGlobalData->DeviceObject\n");
458 return(STATUS_INVALID_DEVICE_REQUEST);
459 }
460 #endif
461
462 DeviceExt = DeviceObject->DeviceExtension;
463
464 Stack = IoGetCurrentIrpStackLocation (Irp);
465 DeviceToVerify = Stack->Parameters.VerifyVolume.DeviceObject;
466
467 ExAcquireResourceExclusiveLite (&DeviceExt->VcbResource,
468 TRUE);
469
470 if (!(DeviceToVerify->Flags & DO_VERIFY_VOLUME))
471 {
472 DPRINT1 ("Volume has been verified!\n");
473 ExReleaseResourceLite (&DeviceExt->VcbResource);
474 return STATUS_SUCCESS;
475 }
476
477 DPRINT1 ("Device object %p Device to verify %p\n", DeviceObject, DeviceToVerify);
478
479 Status = CdfsGetVolumeData (DeviceToVerify,
480 &CdInfo);
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))
485 {
486 DPRINT1 ("Same volume!\n");
487
488 /* FIXME: Flush and purge metadata */
489
490 Status = STATUS_SUCCESS;
491 }
492 else
493 {
494 DPRINT1 ("Different volume!\n");
495
496 /* FIXME: force volume dismount */
497 Entry = DeviceExt->FcbListHead.Flink;
498 while (Entry != &DeviceExt->FcbListHead)
499 {
500 Fcb = (PFCB)CONTAINING_RECORD(Entry, FCB, FcbListEntry);
501 DPRINT1("OpenFile %S RefCount %ld\n", Fcb->PathName, Fcb->RefCount);
502
503 Entry = Entry->Flink;
504 }
505
506 Status = STATUS_WRONG_VOLUME;
507 }
508
509 DeviceToVerify->Flags &= ~DO_VERIFY_VOLUME;
510
511 ExReleaseResourceLite (&DeviceExt->VcbResource);
512
513 return Status;
514 }
515
516
517 NTSTATUS STDCALL
518 CdfsFileSystemControl(PDEVICE_OBJECT DeviceObject,
519 PIRP Irp)
520 {
521 PIO_STACK_LOCATION Stack;
522 NTSTATUS Status;
523
524 DPRINT("CdfsFileSystemControl() called\n");
525
526 Stack = IoGetCurrentIrpStackLocation(Irp);
527
528 switch (Stack->MinorFunction)
529 {
530 case IRP_MN_USER_FS_REQUEST:
531 DPRINT1("CDFS: IRP_MN_USER_FS_REQUEST\n");
532 Status = STATUS_INVALID_DEVICE_REQUEST;
533 break;
534
535 case IRP_MN_MOUNT_VOLUME:
536 DPRINT("CDFS: IRP_MN_MOUNT_VOLUME\n");
537 Status = CdfsMountVolume(DeviceObject, Irp);
538 break;
539
540 case IRP_MN_VERIFY_VOLUME:
541 DPRINT1("CDFS: IRP_MN_VERIFY_VOLUME\n");
542 Status = CdfsVerifyVolume(DeviceObject, Irp);
543 break;
544
545 default:
546 DPRINT1("CDFS FSC: MinorFunction %d\n", Stack->MinorFunction);
547 Status = STATUS_INVALID_DEVICE_REQUEST;
548 break;
549 }
550
551 Irp->IoStatus.Status = Status;
552 Irp->IoStatus.Information = 0;
553
554 IoCompleteRequest(Irp, IO_NO_INCREMENT);
555
556 return(Status);
557 }
558
559 /* EOF */