Sync to trunk r39350.
[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 /*
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
22 * FILE: drivers/fs/cdfs/fsctl.c
23 * PURPOSE: CDROM (ISO 9660) filesystem driver
24 * PROGRAMMER: Art Yerkes
25 * Eric Kohl
26 */
27
28 /* INCLUDES *****************************************************************/
29
30 #include "cdfs.h"
31
32 #define NDEBUG
33 #include <debug.h>
34
35 /* FUNCTIONS ****************************************************************/
36
37 static __inline
38 int msf_to_lba (UCHAR m, UCHAR s, UCHAR f)
39 {
40 return (((m * 60) + s) * 75 + f) - 150;
41 }
42
43
44 static VOID
45 CdfsGetPVDData(PUCHAR Buffer,
46 PCDINFO CdInfo)
47 {
48 PPVD Pvd;
49 ULONG i;
50 PUCHAR pc;
51 PWCHAR pw;
52
53 union
54 {
55 ULONG Value;
56 UCHAR Part[4];
57 } Serial;
58
59 Pvd = (PPVD)Buffer;
60
61 /* Calculate the volume serial number */
62 Serial.Value = 0;
63 for (i = 0; i < 2048; i += 4)
64 {
65 /* DON'T optimize this to ULONG!!! (breaks overflow) */
66 Serial.Part[0] += Buffer[i+3];
67 Serial.Part[1] += Buffer[i+2];
68 Serial.Part[2] += Buffer[i+1];
69 Serial.Part[3] += Buffer[i+0];
70 }
71 CdInfo->SerialNumber = Serial.Value;
72
73 /* Extract the volume label */
74 pc = Pvd->VolumeId;
75 pw = CdInfo->VolumeLabel;
76 for (i = 0; i < MAXIMUM_VOLUME_LABEL_LENGTH && *pc != ' '; i++)
77 {
78 *pw++ = (WCHAR)*pc++;
79 }
80 *pw = 0;
81 CdInfo->VolumeLabelLength = i * sizeof(WCHAR);
82
83 CdInfo->VolumeSpaceSize = Pvd->VolumeSpaceSizeL;
84 CdInfo->RootStart = Pvd->RootDirRecord.ExtentLocationL;
85 CdInfo->RootSize = Pvd->RootDirRecord.DataLengthL;
86
87 DPRINT("VolumeSerial: %08lx\n", CdInfo->SerialNumber);
88 DPRINT("VolumeLabel: '%S'\n", CdInfo->VolumeLabel);
89 DPRINT("VolumeLabelLength: %lu\n", CdInfo->VolumeLabelLength);
90 DPRINT("VolumeSize: %lu\n", Pvd->VolumeSpaceSizeL);
91 DPRINT("RootStart: %lu\n", Pvd->RootDirRecord.ExtentLocationL);
92 DPRINT("RootSize: %lu\n", Pvd->RootDirRecord.DataLengthL);
93 DPRINT("PathTableSize: %lu\n", Pvd->PathTableSizeL);
94 DPRINT("PathTablePos: %lu\n", Pvd->LPathTablePos);
95 DPRINT("OptPathTablePos: %lu\n", Pvd->LOptPathTablePos);
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((PCHAR)Svd->EscapeSequences, "%/@", 3) == 0)
152 {
153 DPRINT("Joliet extension found (UCS-2 Level 1)\n");
154 JolietLevel = 1;
155 }
156 else if (strncmp((PCHAR)Svd->EscapeSequences, "%/C", 3) == 0)
157 {
158 DPRINT("Joliet extension found (UCS-2 Level 2)\n");
159 JolietLevel = 2;
160 }
161 else if (strncmp((PCHAR)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 if (Buffer == NULL)
205 return STATUS_INSUFFICIENT_RESOURCES;
206
207 Size = sizeof(Toc);
208 Status = CdfsDeviceIoControl(DeviceObject,
209 IOCTL_CDROM_GET_LAST_SESSION,
210 NULL,
211 0,
212 &Toc,
213 &Size,
214 TRUE);
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 = CdfsReadSectors (DeviceObject,
241 Sector + Offset,
242 1,
243 Buffer,
244 TRUE);
245 if (!NT_SUCCESS(Status))
246 {
247 ExFreePool(Buffer);
248 return Status;
249 }
250
251 if (Sector == CDFS_PRIMARY_DESCRIPTOR_LOCATION)
252 {
253 DPRINT("CD-identifier: [%.5s]\n", Buffer + 1);
254
255 if (Buffer[0] != 1 || Buffer[1] != 'C' || Buffer[2] != 'D' ||
256 Buffer[3] != '0' || Buffer[4] != '0' || Buffer[5] != '1')
257 {
258 ExFreePool(Buffer);
259 return STATUS_UNRECOGNIZED_VOLUME;
260 }
261 }
262
263 switch (VdHeader->VdType)
264 {
265 case 0:
266 DPRINT("BootVolumeDescriptor found!\n");
267 break;
268
269 case 1:
270 DPRINT("PrimaryVolumeDescriptor found!\n");
271 CdfsGetPVDData(Buffer, CdInfo);
272 break;
273
274 case 2:
275 DPRINT("SupplementaryVolumeDescriptor found!\n");
276 CdfsGetSVDData(Buffer, CdInfo);
277 break;
278
279 case 3:
280 DPRINT("VolumePartitionDescriptor found!\n");
281 break;
282
283 case 255:
284 DPRINT("VolumeDescriptorSetTerminator found!\n");
285 break;
286
287 default:
288 DPRINT1("Unknown volume descriptor type %u found!\n", VdHeader->VdType);
289 break;
290 }
291 }
292
293 ExFreePool(Buffer);
294
295 return(STATUS_SUCCESS);
296 }
297
298
299 static NTSTATUS
300 CdfsMountVolume(PDEVICE_OBJECT DeviceObject,
301 PIRP Irp)
302 {
303 PDEVICE_EXTENSION DeviceExt = NULL;
304 PDEVICE_OBJECT NewDeviceObject = NULL;
305 PDEVICE_OBJECT DeviceToMount;
306 PIO_STACK_LOCATION Stack;
307 PFCB Fcb = NULL;
308 PCCB Ccb = NULL;
309 PVPB Vpb;
310 NTSTATUS Status;
311 CDINFO CdInfo;
312
313 DPRINT("CdfsMountVolume() called\n");
314
315 if (DeviceObject != CdfsGlobalData->DeviceObject)
316 {
317 Status = STATUS_INVALID_DEVICE_REQUEST;
318 goto ByeBye;
319 }
320
321 Stack = IoGetCurrentIrpStackLocation(Irp);
322 DeviceToMount = Stack->Parameters.MountVolume.DeviceObject;
323 Vpb = Stack->Parameters.MountVolume.Vpb;
324
325 Status = CdfsGetVolumeData(DeviceToMount, &CdInfo);
326 if (!NT_SUCCESS(Status))
327 {
328 goto ByeBye;
329 }
330
331 Status = IoCreateDevice(CdfsGlobalData->DriverObject,
332 sizeof(DEVICE_EXTENSION),
333 NULL,
334 FILE_DEVICE_CD_ROM_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 NewDeviceObject->Flags &= ~DO_VERIFY_VOLUME;
343 DeviceExt = (PVOID)NewDeviceObject->DeviceExtension;
344 RtlZeroMemory(DeviceExt,
345 sizeof(DEVICE_EXTENSION));
346
347 Vpb->SerialNumber = CdInfo.SerialNumber;
348 Vpb->VolumeLabelLength = CdInfo.VolumeLabelLength;
349 RtlCopyMemory(Vpb->VolumeLabel, CdInfo.VolumeLabel, CdInfo.VolumeLabelLength * sizeof(WCHAR));
350 RtlCopyMemory(&DeviceExt->CdInfo, &CdInfo, sizeof(CDINFO));
351
352 NewDeviceObject->Vpb = DeviceToMount->Vpb;
353
354 DeviceExt->VolumeDevice = NewDeviceObject;
355 DeviceExt->StorageDevice = DeviceToMount;
356 DeviceExt->StorageDevice->Vpb->DeviceObject = NewDeviceObject;
357 DeviceExt->StorageDevice->Vpb->RealDevice = DeviceExt->StorageDevice;
358 DeviceExt->StorageDevice->Vpb->Flags |= VPB_MOUNTED;
359 NewDeviceObject->StackSize = DeviceExt->StorageDevice->StackSize + 1;
360 NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
361
362 /* Close (and cleanup) might be called from IoCreateStreamFileObject
363 * but we use this resource from CdfsCleanup, therefore it should be
364 * initialized no later than this. */
365 ExInitializeResourceLite(&DeviceExt->DirResource);
366
367 DeviceExt->StreamFileObject = IoCreateStreamFileObject(NULL,
368 DeviceExt->StorageDevice);
369
370 Fcb = CdfsCreateFCB(NULL);
371 if (Fcb == NULL)
372 {
373 Status = STATUS_INSUFFICIENT_RESOURCES;
374 goto ByeBye;
375 }
376
377 Ccb = ExAllocatePoolWithTag(NonPagedPool,
378 sizeof(CCB),
379 TAG_CCB);
380 if (Ccb == NULL)
381 {
382 Status = STATUS_INSUFFICIENT_RESOURCES;
383 goto ByeBye;
384 }
385 RtlZeroMemory(Ccb,
386 sizeof(CCB));
387
388 DeviceExt->StreamFileObject->FsContext = Fcb;
389 DeviceExt->StreamFileObject->FsContext2 = Ccb;
390 DeviceExt->StreamFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
391 DeviceExt->StreamFileObject->PrivateCacheMap = NULL;
392 DeviceExt->StreamFileObject->Vpb = DeviceExt->Vpb;
393 Ccb->PtrFileObject = DeviceExt->StreamFileObject;
394 Fcb->FileObject = DeviceExt->StreamFileObject;
395 Fcb->DevExt = (PDEVICE_EXTENSION)DeviceExt->StorageDevice;
396
397 Fcb->Flags = FCB_IS_VOLUME_STREAM;
398
399 Fcb->RFCB.FileSize.QuadPart = (DeviceExt->CdInfo.VolumeSpaceSize + DeviceExt->CdInfo.VolumeOffset) * BLOCKSIZE;
400 Fcb->RFCB.ValidDataLength = Fcb->RFCB.AllocationSize = Fcb->RFCB.FileSize;
401
402 Fcb->Entry.ExtentLocationL = 0;
403 Fcb->Entry.DataLengthL = (DeviceExt->CdInfo.VolumeSpaceSize + DeviceExt->CdInfo.VolumeOffset) * BLOCKSIZE;
404
405 CcInitializeCacheMap(DeviceExt->StreamFileObject,
406 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
407 TRUE,
408 &(CdfsGlobalData->CacheMgrCallbacks),
409 Fcb);
410
411 ExInitializeResourceLite(&DeviceExt->VcbResource);
412
413 KeInitializeSpinLock(&DeviceExt->FcbListLock);
414 InitializeListHead(&DeviceExt->FcbListHead);
415
416 Status = STATUS_SUCCESS;
417
418 ByeBye:
419 if (!NT_SUCCESS(Status))
420 {
421 /* Cleanup */
422 if (DeviceExt && DeviceExt->StreamFileObject)
423 ObDereferenceObject(DeviceExt->StreamFileObject);
424 if (Fcb)
425 ExFreePool(Fcb);
426 if (Ccb)
427 ExFreePool(Ccb);
428 if (NewDeviceObject)
429 IoDeleteDevice(NewDeviceObject);
430 }
431
432 DPRINT("CdfsMountVolume() done (Status: %lx)\n", Status);
433
434 return(Status);
435 }
436
437
438 static NTSTATUS
439 CdfsVerifyVolume(PDEVICE_OBJECT DeviceObject,
440 PIRP Irp)
441 {
442 PDEVICE_EXTENSION DeviceExt;
443 PDEVICE_OBJECT DeviceToVerify;
444 PIO_STACK_LOCATION Stack;
445 NTSTATUS Status;
446 CDINFO CdInfo;
447
448 PLIST_ENTRY Entry;
449 PFCB Fcb;
450
451 DPRINT1 ("CdfsVerifyVolume() called\n");
452
453 #if 0
454 if (DeviceObject != CdfsGlobalData->DeviceObject)
455 {
456 DPRINT1("DeviceObject != CdfsGlobalData->DeviceObject\n");
457 return(STATUS_INVALID_DEVICE_REQUEST);
458 }
459 #endif
460
461 DeviceExt = DeviceObject->DeviceExtension;
462
463 Stack = IoGetCurrentIrpStackLocation (Irp);
464 DeviceToVerify = Stack->Parameters.VerifyVolume.DeviceObject;
465
466 ExAcquireResourceExclusiveLite (&DeviceExt->VcbResource,
467 TRUE);
468
469 if (!(DeviceToVerify->Flags & DO_VERIFY_VOLUME))
470 {
471 DPRINT1 ("Volume has been verified!\n");
472 ExReleaseResourceLite (&DeviceExt->VcbResource);
473 return STATUS_SUCCESS;
474 }
475
476 DPRINT1 ("Device object %p Device to verify %p\n", DeviceObject, DeviceToVerify);
477
478 Status = CdfsGetVolumeData (DeviceToVerify,
479 &CdInfo);
480 if (NT_SUCCESS(Status) &&
481 CdInfo.SerialNumber == DeviceToVerify->Vpb->SerialNumber &&
482 CdInfo.VolumeLabelLength == DeviceToVerify->Vpb->VolumeLabelLength &&
483 !wcsncmp (CdInfo.VolumeLabel, DeviceToVerify->Vpb->VolumeLabel, CdInfo.VolumeLabelLength))
484 {
485 DPRINT1 ("Same volume!\n");
486
487 /* FIXME: Flush and purge metadata */
488
489 Status = STATUS_SUCCESS;
490 }
491 else
492 {
493 DPRINT1 ("Different volume!\n");
494
495 /* FIXME: force volume dismount */
496 Entry = DeviceExt->FcbListHead.Flink;
497 while (Entry != &DeviceExt->FcbListHead)
498 {
499 Fcb = (PFCB)CONTAINING_RECORD(Entry, FCB, FcbListEntry);
500 DPRINT1("OpenFile %S RefCount %ld\n", Fcb->PathName, Fcb->RefCount);
501
502 Entry = Entry->Flink;
503 }
504
505 Status = STATUS_WRONG_VOLUME;
506 }
507
508 DeviceToVerify->Flags &= ~DO_VERIFY_VOLUME;
509
510 ExReleaseResourceLite (&DeviceExt->VcbResource);
511
512 return Status;
513 }
514
515
516 NTSTATUS NTAPI
517 CdfsSetCompression(
518 IN PDEVICE_OBJECT DeviceObject,
519 IN PIRP Irp)
520 {
521 PIO_STACK_LOCATION Stack;
522 USHORT CompressionState;
523
524 Stack = IoGetCurrentIrpStackLocation(Irp);
525
526 if (Stack->Parameters.DeviceIoControl.InputBufferLength != sizeof(CompressionState))
527 return STATUS_INVALID_DEVICE_REQUEST;
528
529 CompressionState = *(USHORT *)Irp->AssociatedIrp.SystemBuffer;
530 if (CompressionState != COMPRESSION_FORMAT_NONE)
531 return STATUS_INVALID_PARAMETER;
532
533 return STATUS_SUCCESS;
534 }
535
536
537 NTSTATUS NTAPI
538 CdfsFileSystemControl(PDEVICE_OBJECT DeviceObject,
539 PIRP Irp)
540 {
541 PIO_STACK_LOCATION Stack;
542 NTSTATUS Status;
543
544 DPRINT("CdfsFileSystemControl() called\n");
545
546 Stack = IoGetCurrentIrpStackLocation(Irp);
547
548 switch (Stack->MinorFunction)
549 {
550 case IRP_MN_USER_FS_REQUEST:
551 switch (Stack->Parameters.DeviceIoControl.IoControlCode)
552 {
553 case FSCTL_SET_COMPRESSION:
554 DPRINT("CDFS: IRP_MN_USER_FS_REQUEST / FSCTL_SET_COMPRESSION\n");
555 Status = CdfsSetCompression(DeviceObject, Irp);
556 break;
557
558 default:
559 DPRINT1("CDFS: IRP_MN_USER_FS_REQUEST / Unknown IoControlCode 0x%x\n",
560 Stack->Parameters.DeviceIoControl.IoControlCode);
561 Status = STATUS_INVALID_DEVICE_REQUEST;
562 }
563 break;
564
565 case IRP_MN_MOUNT_VOLUME:
566 DPRINT("CDFS: IRP_MN_MOUNT_VOLUME\n");
567 Status = CdfsMountVolume(DeviceObject, Irp);
568 break;
569
570 case IRP_MN_VERIFY_VOLUME:
571 DPRINT1("CDFS: IRP_MN_VERIFY_VOLUME\n");
572 Status = CdfsVerifyVolume(DeviceObject, Irp);
573 break;
574
575 default:
576 DPRINT1("CDFS FSC: MinorFunction %d\n", Stack->MinorFunction);
577 Status = STATUS_INVALID_DEVICE_REQUEST;
578 break;
579 }
580
581 Irp->IoStatus.Status = Status;
582 Irp->IoStatus.Information = 0;
583
584 IoCompleteRequest(Irp, IO_NO_INCREMENT);
585
586 return(Status);
587 }
588
589 /* EOF */