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