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