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