[FASTFAT_NEW] Fix build with FASTFATDBG set
[reactos.git] / drivers / filesystems / fastfat_new / fsctrl.c
1 /*++
2
3
4 Copyright (c) 1989-2000 Microsoft Corporation
5
6 Module Name:
7
8 FsCtrl.c
9
10 Abstract:
11
12 This module implements the File System Control routines for Fat called
13 by the dispatch driver.
14
15
16 --*/
17
18 #include "fatprocs.h"
19
20 //
21 // The Bug check file id for this module
22 //
23
24 #define BugCheckFileId (FAT_BUG_CHECK_FSCTRL)
25
26 //
27 // The local debug trace level
28 //
29
30 #define Dbg (DEBUG_TRACE_FSCTRL)
31
32 //
33 // Local procedure prototypes
34 //
35
36 NTSTATUS
37 FatMountVolume (
38 IN PIRP_CONTEXT IrpContext,
39 IN PDEVICE_OBJECT TargetDeviceObject,
40 IN PVPB Vpb,
41 IN PDEVICE_OBJECT FsDeviceObject
42 );
43
44 NTSTATUS
45 FatVerifyVolume (
46 IN PIRP_CONTEXT IrpContext,
47 IN PIRP Irp
48 );
49
50 BOOLEAN
51 FatIsMediaWriteProtected (
52 IN PIRP_CONTEXT IrpContext,
53 IN PDEVICE_OBJECT TargetDeviceObject
54 );
55
56 NTSTATUS
57 FatUserFsCtrl (
58 IN PIRP_CONTEXT IrpContext,
59 IN PIRP Irp
60 );
61
62 NTSTATUS
63 FatOplockRequest (
64 IN PIRP_CONTEXT IrpContext,
65 IN PIRP Irp
66 );
67
68 NTSTATUS
69 FatLockVolume (
70 IN PIRP_CONTEXT IrpContext,
71 IN PIRP Irp
72 );
73
74 NTSTATUS
75 FatUnlockVolume (
76 IN PIRP_CONTEXT IrpContext,
77 IN PIRP Irp
78 );
79
80 NTSTATUS
81 FatDismountVolume (
82 IN PIRP_CONTEXT IrpContext,
83 IN PIRP Irp
84 );
85
86 NTSTATUS
87 FatDirtyVolume (
88 IN PIRP_CONTEXT IrpContext,
89 IN PIRP Irp
90 );
91
92 NTSTATUS
93 FatIsVolumeDirty (
94 IN PIRP_CONTEXT IrpContext,
95 IN PIRP Irp
96 );
97
98 NTSTATUS
99 FatIsVolumeMounted (
100 IN PIRP_CONTEXT IrpContext,
101 IN PIRP Irp
102 );
103
104 NTSTATUS
105 FatIsPathnameValid (
106 IN PIRP_CONTEXT IrpContext,
107 IN PIRP Irp
108 );
109
110 NTSTATUS
111 FatInvalidateVolumes (
112 IN PIRP Irp
113 );
114
115 VOID
116 FatScanForDismountedVcb (
117 IN PIRP_CONTEXT IrpContext
118 );
119
120 BOOLEAN
121 FatPerformVerifyDiskRead (
122 IN PIRP_CONTEXT IrpContext,
123 IN PVCB Vcb,
124 IN PVOID Buffer,
125 IN LBO Lbo,
126 IN ULONG NumberOfBytesToRead,
127 IN BOOLEAN ReturnOnError
128 );
129
130 NTSTATUS
131 FatQueryRetrievalPointers (
132 IN PIRP_CONTEXT IrpContext,
133 IN PIRP Irp
134 );
135
136 NTSTATUS
137 FatQueryBpb (
138 IN PIRP_CONTEXT IrpContext,
139 IN PIRP Irp
140 );
141
142 NTSTATUS
143 FatGetStatistics (
144 IN PIRP_CONTEXT IrpContext,
145 IN PIRP Irp
146 );
147
148 NTSTATUS
149 FatAllowExtendedDasdIo (
150 IN PIRP_CONTEXT IrpContext,
151 IN PIRP Irp
152 );
153
154 //
155 // Local support routine prototypes
156 //
157
158 NTSTATUS
159 FatGetVolumeBitmap (
160 IN PIRP_CONTEXT IrpContext,
161 IN PIRP Irp
162 );
163
164 NTSTATUS
165 FatGetRetrievalPointers (
166 IN PIRP_CONTEXT IrpContext,
167 IN PIRP Irp
168 );
169
170 NTSTATUS
171 FatMoveFile (
172 IN PIRP_CONTEXT IrpContext,
173 IN PIRP Irp
174 );
175
176 VOID
177 FatComputeMoveFileSplicePoints (
178 PIRP_CONTEXT IrpContext,
179 PFCB FcbOrDcb,
180 ULONG FileOffset,
181 ULONG TargetCluster,
182 ULONG BytesToReallocate,
183 PULONG FirstSpliceSourceCluster,
184 PULONG FirstSpliceTargetCluster,
185 PULONG SecondSpliceSourceCluster,
186 PULONG SecondSpliceTargetCluster,
187 PLARGE_MCB SourceMcb
188 );
189
190 VOID
191 FatComputeMoveFileParameter (
192 IN PIRP_CONTEXT IrpContext,
193 IN PFCB FcbOrDcb,
194 IN ULONG BufferSize,
195 IN ULONG FileOffset,
196 IN OUT PULONG ByteCount,
197 OUT PULONG BytesToReallocate,
198 OUT PULONG BytesToWrite,
199 OUT PLARGE_INTEGER SourceLbo
200 );
201
202 NTSTATUS
203 FatSearchBufferForLabel(
204 IN PIRP_CONTEXT IrpContext,
205 IN PVPB Vpb,
206 IN PVOID Buffer,
207 IN ULONG Size,
208 OUT PBOOLEAN LabelFound
209 );
210
211 VOID
212 FatVerifyLookupFatEntry (
213 IN PIRP_CONTEXT IrpContext,
214 IN PVCB Vcb,
215 IN ULONG FatIndex,
216 IN OUT PULONG FatEntry
217 );
218
219
220 #ifdef ALLOC_PRAGMA
221 #pragma alloc_text(PAGE, FatAddMcbEntry)
222 #pragma alloc_text(PAGE, FatAllowExtendedDasdIo)
223 #pragma alloc_text(PAGE, FatCommonFileSystemControl)
224 #pragma alloc_text(PAGE, FatComputeMoveFileParameter)
225 #pragma alloc_text(PAGE, FatComputeMoveFileSplicePoints)
226 #pragma alloc_text(PAGE, FatDirtyVolume)
227 #pragma alloc_text(PAGE, FatDismountVolume)
228 #pragma alloc_text(PAGE, FatFsdFileSystemControl)
229 #pragma alloc_text(PAGE, FatGetRetrievalPointers)
230 #pragma alloc_text(PAGE, FatGetStatistics)
231 #pragma alloc_text(PAGE, FatGetVolumeBitmap)
232 #pragma alloc_text(PAGE, FatIsMediaWriteProtected)
233 #pragma alloc_text(PAGE, FatIsPathnameValid)
234 #pragma alloc_text(PAGE, FatIsVolumeDirty)
235 #pragma alloc_text(PAGE, FatIsVolumeMounted)
236 #pragma alloc_text(PAGE, FatLockVolume)
237 #pragma alloc_text(PAGE, FatLookupLastMcbEntry)
238 #pragma alloc_text(PAGE, FatMountVolume)
239 #pragma alloc_text(PAGE, FatMoveFile)
240 #pragma alloc_text(PAGE, FatOplockRequest)
241 #pragma alloc_text(PAGE, FatPerformVerifyDiskRead)
242 #pragma alloc_text(PAGE, FatQueryBpb)
243 #pragma alloc_text(PAGE, FatQueryRetrievalPointers)
244 #pragma alloc_text(PAGE, FatRemoveMcbEntry)
245 #pragma alloc_text(PAGE, FatScanForDismountedVcb)
246 #pragma alloc_text(PAGE, FatSearchBufferForLabel)
247 #pragma alloc_text(PAGE, FatUnlockVolume)
248 #pragma alloc_text(PAGE, FatUserFsCtrl)
249 #pragma alloc_text(PAGE, FatVerifyLookupFatEntry)
250 #pragma alloc_text(PAGE, FatVerifyVolume)
251 #endif
252
253 #if DBG
254
255 BOOLEAN FatMoveFileDebug = 0;
256
257 #endif
258
259 //
260 // These wrappers go around the MCB package; we scale the LBO's passed
261 // in (which can be bigger than 32 bits on fat32) by the volume's sector
262 // size.
263 //
264 // Note we now use the real large mcb package. This means these shims
265 // now also convert the -1 unused LBN number to the 0 of the original
266 // mcb package.
267 //
268
269 #define MCB_SCALE_LOG2 (Vcb->AllocationSupport.LogOfBytesPerSector)
270 #define MCB_SCALE (1 << MCB_SCALE_LOG2)
271 #define MCB_SCALE_MODULO (MCB_SCALE - 1)
272
273 \f
274 BOOLEAN
275 FatAddMcbEntry (
276 IN PVCB Vcb,
277 IN PLARGE_MCB Mcb,
278 IN VBO Vbo,
279 IN LBO Lbo,
280 IN ULONG SectorCount
281 )
282
283 {
284 PAGED_CODE();
285
286 if (SectorCount) {
287
288 //
289 // Round up sectors, but be careful as SectorCount approaches 4Gb.
290 // Note that for x>0, (x+m-1)/m = ((x-1)/m)+(m/m) = ((x-1)/m)+1
291 //
292
293 SectorCount--;
294 SectorCount >>= MCB_SCALE_LOG2;
295 SectorCount++;
296 }
297
298 Vbo >>= MCB_SCALE_LOG2;
299 Lbo >>= MCB_SCALE_LOG2;
300
301 return FsRtlAddLargeMcbEntry( Mcb,
302 ((LONGLONG) Vbo),
303 ((LONGLONG) Lbo),
304 ((LONGLONG) SectorCount) );
305 }
306
307 \f
308 BOOLEAN
309 FatLookupMcbEntry (
310 IN PVCB Vcb,
311 IN PLARGE_MCB Mcb,
312 IN VBO Vbo,
313 OUT PLBO Lbo,
314 OUT PULONG SectorCount OPTIONAL,
315 OUT PULONG Index OPTIONAL
316 )
317 {
318 BOOLEAN Results;
319 LONGLONG LiLbo;
320 LONGLONG LiSectorCount;
321 ULONG Remainder;
322
323 LiLbo = 0;
324 LiSectorCount = 0;
325
326 Remainder = Vbo & MCB_SCALE_MODULO;
327
328 Results = FsRtlLookupLargeMcbEntry( Mcb,
329 (Vbo >> MCB_SCALE_LOG2),
330 &LiLbo,
331 ARGUMENT_PRESENT(SectorCount) ? &LiSectorCount : NULL,
332 NULL,
333 NULL,
334 Index );
335
336 if ((ULONG) LiLbo != -1) {
337
338 *Lbo = (((LBO) LiLbo) << MCB_SCALE_LOG2);
339
340 if (Results) {
341
342 *Lbo += Remainder;
343 }
344
345 } else {
346
347 *Lbo = 0;
348 }
349
350 if (ARGUMENT_PRESENT(SectorCount)) {
351
352 *SectorCount = (ULONG) LiSectorCount;
353
354 if (*SectorCount) {
355
356 *SectorCount <<= MCB_SCALE_LOG2;
357
358 if (*SectorCount == 0) {
359
360 *SectorCount = (ULONG) -1;
361 }
362
363 if (Results) {
364
365 *SectorCount -= Remainder;
366 }
367 }
368
369 }
370
371 return Results;
372 }
373
374 //
375 // NOTE: Vbo/Lbn undefined if MCB is empty & return code false.
376 //
377
378 BOOLEAN
379 FatLookupLastMcbEntry (
380 IN PVCB Vcb,
381 IN PLARGE_MCB Mcb,
382 OUT PVBO Vbo,
383 OUT PLBO Lbo,
384 OUT PULONG Index
385 )
386
387 {
388 BOOLEAN Results;
389 LONGLONG LiVbo;
390 LONGLONG LiLbo;
391 ULONG LocalIndex;
392
393 PAGED_CODE();
394
395 LiVbo = LiLbo = 0;
396 LocalIndex = 0;
397
398 Results = FsRtlLookupLastLargeMcbEntryAndIndex( Mcb,
399 &LiVbo,
400 &LiLbo,
401 &LocalIndex );
402
403 *Vbo = ((VBO) LiVbo) << MCB_SCALE_LOG2;
404
405 if (((ULONG) LiLbo) != -1) {
406
407 *Lbo = ((LBO) LiLbo) << MCB_SCALE_LOG2;
408
409 *Lbo += (MCB_SCALE - 1);
410 *Vbo += (MCB_SCALE - 1);
411
412 } else {
413
414 *Lbo = 0;
415 }
416
417 if (Index) {
418 *Index = LocalIndex;
419 }
420
421 return Results;
422 }
423
424 \f
425 BOOLEAN
426 FatGetNextMcbEntry (
427 IN PVCB Vcb,
428 IN PLARGE_MCB Mcb,
429 IN ULONG RunIndex,
430 OUT PVBO Vbo,
431 OUT PLBO Lbo,
432 OUT PULONG SectorCount
433 )
434
435 {
436 BOOLEAN Results;
437 LONGLONG LiVbo;
438 LONGLONG LiLbo;
439 LONGLONG LiSectorCount;
440
441 PAGED_CODE();
442
443 LiVbo = LiLbo = 0;
444
445 Results = FsRtlGetNextLargeMcbEntry( Mcb,
446 RunIndex,
447 &LiVbo,
448 &LiLbo,
449 &LiSectorCount );
450
451 if (Results) {
452
453 *Vbo = ((VBO) LiVbo) << MCB_SCALE_LOG2;
454
455 if (((ULONG) LiLbo) != -1) {
456
457 *Lbo = ((LBO) LiLbo) << MCB_SCALE_LOG2;
458
459 } else {
460
461 *Lbo = 0;
462 }
463
464 *SectorCount = ((ULONG) LiSectorCount) << MCB_SCALE_LOG2;
465
466 if ((*SectorCount == 0) && (LiSectorCount != 0)) {
467 *SectorCount = (ULONG) -1; /* it overflowed */
468 }
469 }
470
471 return Results;
472 }
473
474 \f
475 VOID
476 FatRemoveMcbEntry (
477 IN PVCB Vcb,
478 IN PLARGE_MCB Mcb,
479 IN VBO Vbo,
480 IN ULONG SectorCount
481 )
482 {
483
484 if ((SectorCount) && (SectorCount != 0xFFFFFFFF)) {
485
486 SectorCount--;
487 SectorCount >>= MCB_SCALE_LOG2;
488 SectorCount++;
489 }
490
491 Vbo >>= MCB_SCALE_LOG2;
492
493 #if DBG
494 _SEH2_TRY {
495 #endif
496
497 FsRtlRemoveLargeMcbEntry( Mcb,
498 (LONGLONG) Vbo,
499 (LONGLONG) SectorCount);
500
501 #if DBG
502 } _SEH2_EXCEPT(FatBugCheckExceptionFilter( _SEH2_GetExceptionInformation() )) {
503
504 NOTHING;
505 } _SEH2_END;
506 #endif
507
508 }
509
510 \f
511 NTSTATUS
512 NTAPI
513 FatFsdFileSystemControl (
514 IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
515 IN PIRP Irp
516 )
517
518 /*++
519
520 Routine Description:
521
522 This routine implements the FSD part of FileSystem control operations
523
524 Arguments:
525
526 VolumeDeviceObject - Supplies the volume device object where the
527 file exists
528
529 Irp - Supplies the Irp being processed
530
531 Return Value:
532
533 NTSTATUS - The FSD status for the IRP
534
535 --*/
536
537 {
538 BOOLEAN Wait;
539 NTSTATUS Status;
540 PIRP_CONTEXT IrpContext = NULL;
541
542 BOOLEAN TopLevel;
543
544 DebugTrace(+1, Dbg,"FatFsdFileSystemControl\n", 0);
545
546 //
547 // Call the common FileSystem Control routine, with blocking allowed if
548 // synchronous. This opeation needs to special case the mount
549 // and verify suboperations because we know they are allowed to block.
550 // We identify these suboperations by looking at the file object field
551 // and seeing if its null.
552 //
553
554 if (IoGetCurrentIrpStackLocation(Irp)->FileObject == NULL) {
555
556 Wait = TRUE;
557
558 } else {
559
560 Wait = CanFsdWait( Irp );
561 }
562
563 FsRtlEnterFileSystem();
564
565 TopLevel = FatIsIrpTopLevel( Irp );
566
567 _SEH2_TRY {
568
569 PIO_STACK_LOCATION IrpSp;
570
571 IrpSp = IoGetCurrentIrpStackLocation( Irp );
572
573 //
574 // We need to made a special check here for the InvalidateVolumes
575 // FSCTL as that comes in with a FileSystem device object instead
576 // of a volume device object.
577 //
578
579 if (FatDeviceIsFatFsdo( IrpSp->DeviceObject) &&
580 (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
581 (IrpSp->MinorFunction == IRP_MN_USER_FS_REQUEST) &&
582 (IrpSp->Parameters.FileSystemControl.FsControlCode ==
583 FSCTL_INVALIDATE_VOLUMES)) {
584
585 Status = FatInvalidateVolumes( Irp );
586
587 } else {
588
589 IrpContext = FatCreateIrpContext( Irp, Wait );
590
591 Status = FatCommonFileSystemControl( IrpContext, Irp );
592 }
593
594 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
595
596 //
597 // We had some trouble trying to perform the requested
598 // operation, so we'll abort the I/O request with
599 // the error status that we get back from the
600 // execption code
601 //
602
603 Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
604 } _SEH2_END;
605
606 if (TopLevel) { IoSetTopLevelIrp( NULL ); }
607
608 FsRtlExitFileSystem();
609
610 //
611 // And return to our caller
612 //
613
614 DebugTrace(-1, Dbg, "FatFsdFileSystemControl -> %08lx\n", Status);
615
616 return Status;
617 }
618
619 \f
620 NTSTATUS
621 FatCommonFileSystemControl (
622 IN PIRP_CONTEXT IrpContext,
623 IN PIRP Irp
624 )
625
626 /*++
627
628 Routine Description:
629
630 This is the common routine for doing FileSystem control operations called
631 by both the fsd and fsp threads
632
633 Arguments:
634
635 Irp - Supplies the Irp to process
636
637 Return Value:
638
639 NTSTATUS - The return status for the operation
640
641 --*/
642
643 {
644 NTSTATUS Status;
645 PIO_STACK_LOCATION IrpSp;
646
647 //
648 // Get a pointer to the current Irp stack location
649 //
650
651 IrpSp = IoGetCurrentIrpStackLocation( Irp );
652
653 DebugTrace(+1, Dbg,"FatCommonFileSystemControl\n", 0);
654 DebugTrace( 0, Dbg,"Irp = %08lx\n", Irp);
655 DebugTrace( 0, Dbg,"MinorFunction = %08lx\n", IrpSp->MinorFunction);
656
657 //
658 // We know this is a file system control so we'll case on the
659 // minor function, and call a internal worker routine to complete
660 // the irp.
661 //
662
663 switch (IrpSp->MinorFunction) {
664
665 case IRP_MN_USER_FS_REQUEST:
666
667 Status = FatUserFsCtrl( IrpContext, Irp );
668 break;
669
670 case IRP_MN_MOUNT_VOLUME:
671
672 Status = FatMountVolume( IrpContext,
673 IrpSp->Parameters.MountVolume.DeviceObject,
674 IrpSp->Parameters.MountVolume.Vpb,
675 IrpSp->DeviceObject );
676
677 //
678 // Complete the request.
679 //
680 // We do this here because FatMountVolume can be called recursively,
681 // but the Irp is only to be completed once.
682 //
683 // NOTE: I don't think this is true anymore (danlo 3/15/1999). Probably
684 // an artifact of the old doublespace attempt.
685 //
686
687 FatCompleteRequest( IrpContext, Irp, Status );
688 break;
689
690 case IRP_MN_VERIFY_VOLUME:
691
692 Status = FatVerifyVolume( IrpContext, Irp );
693 break;
694
695 default:
696
697 DebugTrace( 0, Dbg, "Invalid FS Control Minor Function %08lx\n", IrpSp->MinorFunction);
698
699 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
700 Status = STATUS_INVALID_DEVICE_REQUEST;
701 break;
702 }
703
704 DebugTrace(-1, Dbg, "FatCommonFileSystemControl -> %08lx\n", Status);
705
706 return Status;
707 }
708
709 \f
710 //
711 // Local Support Routine
712 //
713
714 NTSTATUS
715 FatMountVolume (
716 IN PIRP_CONTEXT IrpContext,
717 IN PDEVICE_OBJECT TargetDeviceObject,
718 IN PVPB Vpb,
719 IN PDEVICE_OBJECT FsDeviceObject
720 )
721
722 /*++
723
724 Routine Description:
725
726 This routine performs the mount volume operation. It is responsible for
727 either completing of enqueuing the input Irp.
728
729 Its job is to verify that the volume denoted in the IRP is a Fat volume,
730 and create the VCB and root DCB structures. The algorithm it uses is
731 essentially as follows:
732
733 1. Create a new Vcb Structure, and initialize it enough to do cached
734 volume file I/O.
735
736 2. Read the disk and check if it is a Fat volume.
737
738 3. If it is not a Fat volume then free the cached volume file, delete
739 the VCB, and complete the IRP with STATUS_UNRECOGNIZED_VOLUME
740
741 4. Check if the volume was previously mounted and if it was then do a
742 remount operation. This involves reinitializing the cached volume
743 file, checking the dirty bit, resetting up the allocation support,
744 deleting the VCB, hooking in the old VCB, and completing the IRP.
745
746 5. Otherwise create a root DCB, create Fsp threads as necessary, and
747 complete the IRP.
748
749 Arguments:
750
751 TargetDeviceObject - This is where we send all of our requests.
752
753 Vpb - This gives us additional information needed to complete the mount.
754
755 Return Value:
756
757 NTSTATUS - The return status for the operation
758
759 --*/
760
761 {
762 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );
763 NTSTATUS Status;
764
765 PBCB BootBcb;
766 PPACKED_BOOT_SECTOR BootSector;
767
768 PBCB DirentBcb;
769 PDIRENT Dirent;
770 ULONG ByteOffset;
771
772 BOOLEAN MountNewVolume = FALSE;
773 BOOLEAN WeClearedVerifyRequiredBit = FALSE;
774 BOOLEAN DoARemount = FALSE;
775
776 PVCB OldVcb;
777 PVPB OldVpb;
778
779 PDEVICE_OBJECT RealDevice;
780 PVOLUME_DEVICE_OBJECT VolDo = NULL;
781 PVCB Vcb = NULL;
782 PFILE_OBJECT RootDirectoryFile = NULL;
783
784 PLIST_ENTRY Links;
785
786 IO_STATUS_BLOCK Iosb;
787 ULONG ChangeCount = 0;
788
789 DISK_GEOMETRY Geometry;
790
791 PARTITION_INFORMATION_EX PartitionInformation;
792 NTSTATUS StatusPartInfo;
793
794 DebugTrace(+1, Dbg, "FatMountVolume\n", 0);
795 DebugTrace( 0, Dbg, "TargetDeviceObject = %08lx\n", TargetDeviceObject);
796 DebugTrace( 0, Dbg, "Vpb = %08lx\n", Vpb);
797
798 ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
799 ASSERT( FatDeviceIsFatFsdo( FsDeviceObject));
800
801 //
802 // Verify that there is a disk here and pick up the change count.
803 //
804
805 Status = FatPerformDevIoCtrl( IrpContext,
806 IOCTL_DISK_CHECK_VERIFY,
807 TargetDeviceObject,
808 &ChangeCount,
809 sizeof(ULONG),
810 FALSE,
811 TRUE,
812 &Iosb );
813
814 if (!NT_SUCCESS( Status )) {
815
816 //
817 // If we will allow a raw mount then avoid sending the popup.
818 //
819 // Only send this on "true" disk devices to handle the accidental
820 // legacy of FAT. No other FS will throw a harderror on empty
821 // drives.
822 //
823 // Cmd should really handle this per 9x.
824 //
825
826 if (!FlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT ) &&
827 Vpb->RealDevice->DeviceType == FILE_DEVICE_DISK) {
828
829 FatNormalizeAndRaiseStatus( IrpContext, Status );
830 }
831
832 return Status;
833 }
834
835 if (Iosb.Information != sizeof(ULONG)) {
836
837 //
838 // Be safe about the count in case the driver didn't fill it in
839 //
840
841 ChangeCount = 0;
842 }
843
844 //
845 // If this is a CD class device, then check to see if there is a
846 // 'data track' or not. This is to avoid issuing paging reads which will
847 // fail later in the mount process (e.g. CD-DA or blank CD media)
848 //
849
850 if ((TargetDeviceObject->DeviceType == FILE_DEVICE_CD_ROM) &&
851 !FatScanForDataTrack( IrpContext, TargetDeviceObject)) {
852
853 return STATUS_UNRECOGNIZED_VOLUME;
854 }
855
856 //
857 // Ping the volume with a partition query and pick up the partition
858 // type. We'll check this later to avoid some scurrilous volumes.
859 //
860
861 StatusPartInfo = FatPerformDevIoCtrl( IrpContext,
862 IOCTL_DISK_GET_PARTITION_INFO_EX,
863 TargetDeviceObject,
864 &PartitionInformation,
865 sizeof(PARTITION_INFORMATION_EX),
866 FALSE,
867 TRUE,
868 &Iosb );
869
870 //
871 // Make sure we can wait.
872 //
873
874 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
875
876 //
877 // Do a quick check to see if there any Vcb's which can be removed.
878 //
879
880 FatScanForDismountedVcb( IrpContext );
881
882 //
883 // Initialize the Bcbs and our final state so that the termination
884 // handlers will know what to free or unpin
885 //
886
887 BootBcb = NULL;
888 DirentBcb = NULL;
889
890 Vcb = NULL;
891 VolDo = NULL;
892 MountNewVolume = FALSE;
893
894 _SEH2_TRY {
895
896 //
897 // Synchronize with FatCheckForDismount(), which modifies the vpb.
898 //
899
900 (VOID)FatAcquireExclusiveGlobal( IrpContext );
901
902 //
903 // Create a new volume device object. This will have the Vcb
904 // hanging off of its end, and set its alignment requirement
905 // from the device we talk to.
906 //
907
908 if (!NT_SUCCESS(Status = IoCreateDevice( FatData.DriverObject,
909 sizeof(VOLUME_DEVICE_OBJECT) - sizeof(DEVICE_OBJECT),
910 NULL,
911 FILE_DEVICE_DISK_FILE_SYSTEM,
912 0,
913 FALSE,
914 (PDEVICE_OBJECT *)&VolDo))) {
915
916 try_return( Status );
917 }
918
919 #ifdef _PNP_POWER_
920 //
921 // This driver doesn't talk directly to a device, and (at the moment)
922 // isn't otherwise concerned about power management.
923 //
924
925 VolDo->DeviceObject.DeviceObjectExtension->PowerControlNeeded = FALSE;
926 #endif
927
928 //
929 // Our alignment requirement is the larger of the processor alignment requirement
930 // already in the volume device object and that in the TargetDeviceObject
931 //
932
933 if (TargetDeviceObject->AlignmentRequirement > VolDo->DeviceObject.AlignmentRequirement) {
934
935 VolDo->DeviceObject.AlignmentRequirement = TargetDeviceObject->AlignmentRequirement;
936 }
937
938 //
939 // Initialize the overflow queue for the volume
940 //
941
942 VolDo->OverflowQueueCount = 0;
943 InitializeListHead( &VolDo->OverflowQueue );
944
945 VolDo->PostedRequestCount = 0;
946 KeInitializeSpinLock( &VolDo->OverflowQueueSpinLock );
947
948 //
949 // We must initialize the stack size in our device object before
950 // the following reads, because the I/O system has not done it yet.
951 // This must be done before we clear the device initializing flag
952 // otherwise a filter could attach and copy the wrong stack size into
953 // it's device object.
954 //
955
956 VolDo->DeviceObject.StackSize = (CCHAR)(TargetDeviceObject->StackSize + 1);
957
958 //
959 // We must also set the sector size correctly in our device object
960 // before clearing the device initializing flag.
961 //
962
963 Status = FatPerformDevIoCtrl( IrpContext,
964 IOCTL_DISK_GET_DRIVE_GEOMETRY,
965 TargetDeviceObject,
966 &Geometry,
967 sizeof( DISK_GEOMETRY ),
968 FALSE,
969 TRUE,
970 NULL );
971
972 VolDo->DeviceObject.SectorSize = (USHORT)Geometry.BytesPerSector;
973
974 //
975 // Indicate that this device object is now completely initialized
976 //
977
978 ClearFlag(VolDo->DeviceObject.Flags, DO_DEVICE_INITIALIZING);
979
980 //
981 // Now Before we can initialize the Vcb we need to set up the device
982 // object field in the Vpb to point to our new volume device object.
983 // This is needed when we create the virtual volume file's file object
984 // in initialize vcb.
985 //
986
987 Vpb->DeviceObject = (PDEVICE_OBJECT)VolDo;
988
989 //
990 // If the real device needs verification, temporarily clear the
991 // field.
992 //
993
994 RealDevice = Vpb->RealDevice;
995
996 if ( FlagOn(RealDevice->Flags, DO_VERIFY_VOLUME) ) {
997
998 ClearFlag(RealDevice->Flags, DO_VERIFY_VOLUME);
999
1000 WeClearedVerifyRequiredBit = TRUE;
1001 }
1002
1003 //
1004 // Initialize the new vcb
1005 //
1006
1007 FatInitializeVcb( IrpContext,
1008 &VolDo->Vcb,
1009 TargetDeviceObject,
1010 Vpb,
1011 FsDeviceObject);
1012 //
1013 // Get a reference to the Vcb hanging off the end of the device object
1014 //
1015
1016 Vcb = &VolDo->Vcb;
1017
1018 //
1019 // Read in the boot sector, and have the read be the minumum size
1020 // needed. We know we can wait.
1021 //
1022
1023 //
1024 // We need to commute errors on CD so that CDFS will get its crack. Audio
1025 // and even data media may not be universally readable on sector zero.
1026 //
1027
1028 _SEH2_TRY {
1029
1030 FatReadVolumeFile( IrpContext,
1031 Vcb,
1032 0, // Starting Byte
1033 sizeof(PACKED_BOOT_SECTOR),
1034 &BootBcb,
1035 (PVOID *)&BootSector );
1036
1037 } _SEH2_EXCEPT( Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM ?
1038 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
1039
1040 NOTHING;
1041 } _SEH2_END;
1042
1043 //
1044 // Call a routine to check the boot sector to see if it is fat
1045 //
1046
1047 if (BootBcb == NULL || !FatIsBootSectorFat( BootSector)) {
1048
1049 DebugTrace(0, Dbg, "Not a Fat Volume\n", 0);
1050
1051 //
1052 // Complete the request and return to our caller
1053 //
1054
1055 try_return( Status = STATUS_UNRECOGNIZED_VOLUME );
1056 }
1057
1058 //
1059 // Unpack the BPB. We used to do some sanity checking of the FATs at
1060 // this point, but authoring errors on third-party devices prevent
1061 // us from continuing to safeguard ourselves. We can only hope the
1062 // boot sector check is good enough.
1063 //
1064 // (read: digital cameras)
1065 //
1066 // Win9x does the same.
1067 //
1068
1069 FatUnpackBios( &Vcb->Bpb, &BootSector->PackedBpb );
1070
1071 //
1072 // Check if we have an OS/2 Boot Manager partition and treat it as an
1073 // unknown file system. We'll check the partition type in from the
1074 // partition table and we ensure that it has less than 0x80 sectors,
1075 // which is just a heuristic that will capture all real OS/2 BM partitions
1076 // and avoid the chance we'll discover partitions which erroneously
1077 // (but to this point, harmlessly) put down the OS/2 BM type.
1078 //
1079 // Note that this is only conceivable on good old MBR media.
1080 //
1081 // The OS/2 Boot Manager boot format mimics a FAT16 partition in sector
1082 // zero but does is not a real FAT16 file system. For example, the boot
1083 // sector indicates it has 2 FATs but only really has one, with the boot
1084 // manager code overlaying the second FAT. If we then set clean bits in
1085 // FAT[0] we'll corrupt that code.
1086 //
1087
1088 if (NT_SUCCESS( StatusPartInfo ) &&
1089 (PartitionInformation.PartitionStyle == PARTITION_STYLE_MBR &&
1090 PartitionInformation.Mbr.PartitionType == PARTITION_OS2BOOTMGR) &&
1091 (Vcb->Bpb.Sectors != 0 &&
1092 Vcb->Bpb.Sectors < 0x80)) {
1093
1094 DebugTrace( 0, Dbg, "OS/2 Boot Manager volume detected, volume not mounted. \n", 0 );
1095
1096 //
1097 // Complete the request and return to our caller
1098 //
1099
1100 try_return( Status = STATUS_UNRECOGNIZED_VOLUME );
1101 }
1102
1103 //
1104 // Verify that the sector size recorded in the Bpb matches what the
1105 // device currently reports it's sector size to be.
1106 //
1107
1108 if ( !NT_SUCCESS( Status) ||
1109 (Geometry.BytesPerSector != Vcb->Bpb.BytesPerSector)) {
1110
1111 try_return( Status = STATUS_UNRECOGNIZED_VOLUME );
1112 }
1113
1114 //
1115 // This is a fat volume, so extract the bpb, serial number. The
1116 // label we'll get later after we've created the root dcb.
1117 //
1118 // Note that the way data caching is done, we set neither the
1119 // direct I/O or Buffered I/O bit in the device object flags.
1120 //
1121
1122 if (Vcb->Bpb.Sectors != 0) { Vcb->Bpb.LargeSectors = 0; }
1123
1124 if (IsBpbFat32(&BootSector->PackedBpb)) {
1125
1126 CopyUchar4( &Vpb->SerialNumber, ((PPACKED_BOOT_SECTOR_EX)BootSector)->Id );
1127
1128 } else {
1129
1130 CopyUchar4( &Vpb->SerialNumber, BootSector->Id );
1131
1132 //
1133 // Allocate space for the stashed boot sector chunk. This only has meaning on
1134 // FAT12/16 volumes since this only is kept for the FSCTL_QUERY_FAT_BPB and it and
1135 // its users are a bit wierd, thinking that a BPB exists wholly in the first 0x24
1136 // bytes.
1137 //
1138
1139 Vcb->First0x24BytesOfBootSector =
1140 FsRtlAllocatePoolWithTag( PagedPool,
1141 0x24,
1142 TAG_STASHED_BPB );
1143
1144 //
1145 // Stash a copy of the first 0x24 bytes
1146 //
1147
1148 RtlCopyMemory( Vcb->First0x24BytesOfBootSector,
1149 BootSector,
1150 0x24 );
1151 }
1152
1153 //
1154 // Now unpin the boot sector, so when we set up allocation eveything
1155 // works.
1156 //
1157
1158 FatUnpinBcb( IrpContext, BootBcb );
1159
1160 //
1161 // Compute a number of fields for Vcb.AllocationSupport
1162 //
1163
1164 FatSetupAllocationSupport( IrpContext, Vcb );
1165
1166 //
1167 // Sanity check the FsInfo information for FAT32 volumes. Silently deal
1168 // with messed up information by effectively disabling FsInfo updates.
1169 //
1170
1171 if (FatIsFat32( Vcb )) {
1172
1173 if (Vcb->Bpb.FsInfoSector >= Vcb->Bpb.ReservedSectors) {
1174
1175 Vcb->Bpb.FsInfoSector = 0;
1176 }
1177 }
1178
1179 //
1180 // Create a root Dcb so we can read in the volume label. If this is FAT32, we can
1181 // discover corruption in the FAT chain.
1182 //
1183 // NOTE: this exception handler presumes that this is the only spot where we can
1184 // discover corruption in the mount process. If this ever changes, this handler
1185 // MUST be expanded. The reason we have this guy here is because we have to rip
1186 // the structures down now (in the finally below) and can't wait for the outer
1187 // exception handling to do it for us, at which point everything will have vanished.
1188 //
1189
1190 _SEH2_TRY {
1191
1192 FatCreateRootDcb( IrpContext, Vcb );
1193
1194 } _SEH2_EXCEPT (_SEH2_GetExceptionCode() == STATUS_FILE_CORRUPT_ERROR ? EXCEPTION_EXECUTE_HANDLER :
1195 EXCEPTION_CONTINUE_SEARCH) {
1196
1197 //
1198 // The volume needs to be dirtied, do it now. Note that at this point we have built
1199 // enough of the Vcb to pull this off.
1200 //
1201
1202 FatMarkVolume( IrpContext, Vcb, VolumeDirty );
1203
1204 //
1205 // Now keep bailing out ...
1206 //
1207
1208 FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
1209 } _SEH2_END;
1210
1211 FatLocateVolumeLabel( IrpContext,
1212 Vcb,
1213 &Dirent,
1214 &DirentBcb,
1215 #ifndef __REACTOS__
1216 &ByteOffset );
1217 #else
1218 (PVBO)&ByteOffset );
1219 #endif
1220
1221 if (Dirent != NULL) {
1222
1223 OEM_STRING OemString;
1224 UNICODE_STRING UnicodeString;
1225
1226 //
1227 // Compute the length of the volume name
1228 //
1229
1230 #ifndef __REACTOS__
1231 OemString.Buffer = &Dirent->FileName[0];
1232 #else
1233 OemString.Buffer = (PCHAR)&Dirent->FileName[0];
1234 #endif
1235 OemString.MaximumLength = 11;
1236
1237 for ( OemString.Length = 11;
1238 OemString.Length > 0;
1239 OemString.Length -= 1) {
1240
1241 if ( (Dirent->FileName[OemString.Length-1] != 0x00) &&
1242 (Dirent->FileName[OemString.Length-1] != 0x20) ) { break; }
1243 }
1244
1245 UnicodeString.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH;
1246 UnicodeString.Buffer = &Vcb->Vpb->VolumeLabel[0];
1247
1248 Status = RtlOemStringToCountedUnicodeString( &UnicodeString,
1249 &OemString,
1250 FALSE );
1251
1252 if ( !NT_SUCCESS( Status ) ) {
1253
1254 try_return( Status );
1255 }
1256
1257 Vpb->VolumeLabelLength = UnicodeString.Length;
1258
1259 } else {
1260
1261 Vpb->VolumeLabelLength = 0;
1262 }
1263
1264 //
1265 // Use the change count we noted initially *before* doing any work.
1266 // If something came along in the midst of this operation, we'll
1267 // verify and discover the problem.
1268 //
1269
1270 Vcb->ChangeCount = ChangeCount;
1271
1272 //
1273 // Now scan the list of previously mounted volumes and compare
1274 // serial numbers and volume labels off not currently mounted
1275 // volumes to see if we have a match.
1276 //
1277
1278 for (Links = FatData.VcbQueue.Flink;
1279 Links != &FatData.VcbQueue;
1280 Links = Links->Flink) {
1281
1282 OldVcb = CONTAINING_RECORD( Links, VCB, VcbLinks );
1283 OldVpb = OldVcb->Vpb;
1284
1285 //
1286 // Skip over ourselves since we're already in the VcbQueue
1287 //
1288
1289 if (OldVpb == Vpb) { continue; }
1290
1291 //
1292 // Check for a match:
1293 //
1294 // Serial Number, VolumeLabel and Bpb must all be the same.
1295 // Also the volume must have failed a verify before (ie.
1296 // VolumeNotMounted), and it must be in the same physical
1297 // drive than it was mounted in before.
1298 //
1299
1300 if ( (OldVpb->SerialNumber == Vpb->SerialNumber) &&
1301 (OldVcb->VcbCondition == VcbNotMounted) &&
1302 (OldVpb->RealDevice == RealDevice) &&
1303 (OldVpb->VolumeLabelLength == Vpb->VolumeLabelLength) &&
1304 (RtlEqualMemory(&OldVpb->VolumeLabel[0],
1305 &Vpb->VolumeLabel[0],
1306 Vpb->VolumeLabelLength)) &&
1307 (RtlEqualMemory(&OldVcb->Bpb,
1308 &Vcb->Bpb,
1309 IsBpbFat32(&Vcb->Bpb) ?
1310 sizeof(BIOS_PARAMETER_BLOCK) :
1311 FIELD_OFFSET(BIOS_PARAMETER_BLOCK,
1312 LargeSectorsPerFat) ))) {
1313
1314 DoARemount = TRUE;
1315
1316 break;
1317 }
1318 }
1319
1320 if ( DoARemount ) {
1321
1322 PVPB *IrpVpb;
1323
1324 DebugTrace(0, Dbg, "Doing a remount\n", 0);
1325 DebugTrace(0, Dbg, "Vcb = %08lx\n", Vcb);
1326 DebugTrace(0, Dbg, "Vpb = %08lx\n", Vpb);
1327 DebugTrace(0, Dbg, "OldVcb = %08lx\n", OldVcb);
1328 DebugTrace(0, Dbg, "OldVpb = %08lx\n", OldVpb);
1329
1330 //
1331 // Swap target device objects between the VCBs. That way
1332 // the old VCB will start using the new target device object,
1333 // and the new VCB will be torn down and deference the old
1334 // target device object.
1335 //
1336
1337 Vcb->TargetDeviceObject = OldVcb->TargetDeviceObject;
1338 OldVcb->TargetDeviceObject = TargetDeviceObject;
1339
1340 //
1341 // This is a remount, so link the old vpb in place
1342 // of the new vpb.
1343
1344 ASSERT( !FlagOn( OldVcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED ) );
1345
1346 OldVpb->RealDevice = Vpb->RealDevice;
1347 OldVpb->RealDevice->Vpb = OldVpb;
1348
1349 OldVcb->VcbCondition = VcbGood;
1350
1351 //
1352 // Use the new changecount.
1353 //
1354
1355 OldVcb->ChangeCount = Vcb->ChangeCount;
1356
1357 //
1358 // If the new VPB is the VPB referenced in the original Irp, set
1359 // that reference back to the old VPB.
1360 //
1361
1362 IrpVpb = &IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)->Parameters.MountVolume.Vpb;
1363
1364 if (*IrpVpb == Vpb) {
1365
1366 *IrpVpb = OldVpb;
1367 }
1368
1369 //
1370 // We do not want to touch this VPB again. It will get cleaned up when
1371 // the new VCB is cleaned up.
1372 //
1373
1374 ASSERT( Vcb->Vpb == Vpb );
1375
1376 Vpb = NULL;
1377
1378 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED );
1379 FatSetVcbCondition( Vcb, VcbBad );
1380
1381 //
1382 // Reinitialize the volume file cache and allocation support.
1383 //
1384
1385 {
1386 CC_FILE_SIZES FileSizes;
1387
1388 FileSizes.AllocationSize.QuadPart =
1389 FileSizes.FileSize.QuadPart = ( 0x40000 + 0x1000 );
1390 FileSizes.ValidDataLength = FatMaxLarge;
1391
1392 DebugTrace(0, Dbg, "Truncate and reinitialize the volume file\n", 0);
1393
1394 CcInitializeCacheMap( OldVcb->VirtualVolumeFile,
1395 &FileSizes,
1396 TRUE,
1397 &FatData.CacheManagerNoOpCallbacks,
1398 Vcb );
1399
1400 //
1401 // Redo the allocation support
1402 //
1403
1404 FatSetupAllocationSupport( IrpContext, OldVcb );
1405
1406 //
1407 // Get the state of the dirty bit.
1408 //
1409
1410 FatCheckDirtyBit( IrpContext, OldVcb );
1411
1412 //
1413 // Check for write protected media.
1414 //
1415
1416 if (FatIsMediaWriteProtected(IrpContext, TargetDeviceObject)) {
1417
1418 SetFlag( OldVcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
1419
1420 } else {
1421
1422 ClearFlag( OldVcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
1423 }
1424 }
1425
1426 //
1427 // Complete the request and return to our caller
1428 //
1429
1430 try_return( Status = STATUS_SUCCESS );
1431 }
1432
1433 DebugTrace(0, Dbg, "Mount a new volume\n", 0);
1434
1435 //
1436 // This is a new mount
1437 //
1438 // Create a blank ea data file fcb, just not for Fat32.
1439 //
1440
1441 if (!FatIsFat32(Vcb)) {
1442
1443 DIRENT TempDirent;
1444 PFCB EaFcb;
1445
1446 RtlZeroMemory( &TempDirent, sizeof(DIRENT) );
1447 RtlCopyMemory( &TempDirent.FileName[0], "EA DATA SF", 11 );
1448
1449 EaFcb = FatCreateFcb( IrpContext,
1450 Vcb,
1451 Vcb->RootDcb,
1452 0,
1453 0,
1454 &TempDirent,
1455 NULL,
1456 FALSE,
1457 TRUE );
1458
1459 //
1460 // Deny anybody who trys to open the file.
1461 //
1462
1463 SetFlag( EaFcb->FcbState, FCB_STATE_SYSTEM_FILE );
1464
1465 Vcb->EaFcb = EaFcb;
1466 }
1467
1468 //
1469 // Get the state of the dirty bit.
1470 //
1471
1472 FatCheckDirtyBit( IrpContext, Vcb );
1473
1474 //
1475 // Check for write protected media.
1476 //
1477
1478 if (FatIsMediaWriteProtected(IrpContext, TargetDeviceObject)) {
1479
1480 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
1481
1482 } else {
1483
1484 ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
1485 }
1486
1487 //
1488 // Lock volume in drive if we just mounted the boot drive.
1489 //
1490
1491 if (FlagOn(RealDevice->Flags, DO_SYSTEM_BOOT_PARTITION)) {
1492
1493 SetFlag(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE);
1494
1495 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) {
1496
1497 FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE );
1498 }
1499 }
1500
1501 //
1502 // Indicate to our termination handler that we have mounted
1503 // a new volume.
1504 //
1505
1506 MountNewVolume = TRUE;
1507
1508 //
1509 // Complete the request
1510 //
1511
1512 Status = STATUS_SUCCESS;
1513
1514 //
1515 // Ref the root dir stream object so we can send mount notification.
1516 //
1517
1518 RootDirectoryFile = Vcb->RootDcb->Specific.Dcb.DirectoryFile;
1519 ObReferenceObject( RootDirectoryFile );
1520
1521 //
1522 // Remove the extra reference to this target DO made on behalf of us
1523 // by the IO system. In the remount case, we permit regular Vcb
1524 // deletion to do this work.
1525 //
1526
1527 ObDereferenceObject( TargetDeviceObject );
1528
1529
1530 try_exit: NOTHING;
1531
1532 } _SEH2_FINALLY {
1533
1534 DebugUnwind( FatMountVolume );
1535
1536 FatUnpinBcb( IrpContext, BootBcb );
1537 FatUnpinBcb( IrpContext, DirentBcb );
1538
1539 //
1540 // Check if a volume was mounted. If not then we need to
1541 // mark the Vpb not mounted again.
1542 //
1543
1544 if ( !MountNewVolume ) {
1545
1546 if ( Vcb != NULL ) {
1547
1548 //
1549 // A VCB was created and initialized. we need to try to tear it down.
1550 //
1551
1552 FatCheckForDismount( IrpContext,
1553 Vcb,
1554 TRUE );
1555
1556 IrpContext->Vcb = NULL;
1557 } else if ( VolDo != NULL ) {
1558 //
1559 // The VCB was never initialized, so we need to delete the
1560 // device right here.
1561 //
1562
1563 IoDeleteDevice( &VolDo->DeviceObject );
1564 }
1565
1566 //
1567 // see if a remount failed.
1568 //
1569
1570 if (DoARemount && _SEH2_AbnormalTermination()) {
1571
1572 //
1573 // The remount failed. Try to tear down the old VCB as well.
1574 //
1575
1576 FatCheckForDismount( IrpContext,
1577 OldVcb,
1578 TRUE );
1579 }
1580 }
1581
1582 if ( WeClearedVerifyRequiredBit == TRUE ) {
1583
1584 SetFlag(RealDevice->Flags, DO_VERIFY_VOLUME);
1585 }
1586
1587 FatReleaseGlobal( IrpContext );
1588
1589 DebugTrace(-1, Dbg, "FatMountVolume -> %08lx\n", Status);
1590 } _SEH2_END;
1591
1592 //
1593 // Now send mount notification. Note that since this is outside of any
1594 // synchronization since the synchronous delivery of this may go to
1595 // folks that provoke re-entrance to the FS.
1596 //
1597
1598 if (RootDirectoryFile != NULL) {
1599
1600 FsRtlNotifyVolumeEvent( RootDirectoryFile, FSRTL_VOLUME_MOUNT );
1601 ObDereferenceObject( RootDirectoryFile );
1602 }
1603
1604 return Status;
1605 }
1606
1607 \f
1608 //
1609 // Local Support Routine
1610 //
1611
1612 NTSTATUS
1613 FatVerifyVolume (
1614 IN PIRP_CONTEXT IrpContext,
1615 IN PIRP Irp
1616 )
1617
1618 /*++
1619
1620 Routine Description:
1621
1622 This routine performs the verify volume operation by checking the volume
1623 label and serial number physically on the media with the the Vcb
1624 currently claiming to have the volume mounted. It is responsible for
1625 either completing or enqueuing the input Irp.
1626
1627 Regardless of whether the verify operation succeeds, the following
1628 operations are performed:
1629
1630 - Set Vcb->VirtualEaFile back to its virgin state.
1631 - Purge all cached data (flushing first if verify succeeds)
1632 - Mark all Fcbs as needing verification
1633
1634 If the volumes verifies correctly we also must:
1635
1636 - Check the volume dirty bit.
1637 - Reinitialize the allocation support
1638 - Flush any dirty data
1639
1640 If the volume verify fails, it may never be mounted again. If it is
1641 mounted again, it will happen as a remount operation. In preparation
1642 for that, and to leave the volume in a state that can be "lazy deleted"
1643 the following operations are performed:
1644
1645 - Set the Vcb condition to VcbNotMounted
1646 - Uninitialize the volume file cachemap
1647 - Tear down the allocation support
1648
1649 In the case of an abnormal termination we haven't determined the state
1650 of the volume, so we set the Device Object as needing verification again.
1651
1652 Arguments:
1653
1654 Irp - Supplies the Irp to process
1655
1656 Return Value:
1657
1658 NTSTATUS - If the verify operation completes, it will return either
1659 STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly. If an IO or
1660 other error is encountered, that status will be returned.
1661
1662 --*/
1663
1664 {
1665 NTSTATUS Status = STATUS_SUCCESS;
1666
1667 PIO_STACK_LOCATION IrpSp;
1668
1669 PDIRENT RootDirectory = NULL;
1670 PPACKED_BOOT_SECTOR BootSector = NULL;
1671
1672 BIOS_PARAMETER_BLOCK Bpb;
1673
1674 PVOLUME_DEVICE_OBJECT VolDo;
1675 PVCB Vcb;
1676 PVPB Vpb;
1677
1678 ULONG SectorSize;
1679 BOOLEAN ClearVerify = FALSE;
1680 BOOLEAN ReleaseEntireVolume = FALSE;
1681 BOOLEAN VerifyAlreadyDone = FALSE;
1682
1683 DISK_GEOMETRY DiskGeometry;
1684
1685 LBO RootDirectoryLbo;
1686 ULONG RootDirectorySize;
1687 BOOLEAN LabelFound;
1688
1689 ULONG ChangeCount = 0;
1690 IO_STATUS_BLOCK Iosb;
1691
1692 //
1693 // Get the current Irp stack location
1694 //
1695
1696 IrpSp = IoGetCurrentIrpStackLocation( Irp );
1697
1698 DebugTrace(+1, Dbg, "FatVerifyVolume\n", 0);
1699 DebugTrace( 0, Dbg, "DeviceObject = %08lx\n", IrpSp->Parameters.VerifyVolume.DeviceObject);
1700 DebugTrace( 0, Dbg, "Vpb = %08lx\n", IrpSp->Parameters.VerifyVolume.Vpb);
1701
1702 //
1703 // Save some references to make our life a little easier. Note the Vcb for the purposes
1704 // of exception handling.
1705 //
1706
1707 VolDo = (PVOLUME_DEVICE_OBJECT)IrpSp->Parameters.VerifyVolume.DeviceObject;
1708
1709 Vpb = IrpSp->Parameters.VerifyVolume.Vpb;
1710 IrpContext->Vcb = Vcb = &VolDo->Vcb;
1711
1712 //
1713 // If we cannot wait then enqueue the irp to the fsp and
1714 // return the status to our caller.
1715 //
1716
1717 if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
1718
1719 DebugTrace(0, Dbg, "Cannot wait for verify.\n", 0);
1720
1721 Status = FatFsdPostRequest( IrpContext, Irp );
1722
1723 DebugTrace(-1, Dbg, "FatVerifyVolume -> %08lx\n", Status );
1724 return Status;
1725 }
1726
1727 //
1728 // We are serialized at this point allowing only one thread to
1729 // actually perform the verify operation. Any others will just
1730 // wait and then no-op when checking if the volume still needs
1731 // verification.
1732 //
1733
1734 (VOID)FatAcquireExclusiveGlobal( IrpContext );
1735 (VOID)FatAcquireExclusiveVcb( IrpContext, Vcb );
1736
1737 _SEH2_TRY {
1738
1739 BOOLEAN AllowRawMount = BooleanFlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT );
1740
1741 //
1742 // Mark ourselves as verifying this volume so that recursive I/Os
1743 // will be able to complete.
1744 //
1745
1746 ASSERT( Vcb->VerifyThread == NULL );
1747 Vcb->VerifyThread = KeGetCurrentThread();
1748
1749 //
1750 // Check if the real device still needs to be verified. If it doesn't
1751 // then obviously someone beat us here and already did the work
1752 // so complete the verify irp with success. Otherwise reenable
1753 // the real device and get to work.
1754 //
1755
1756 if (!FlagOn(Vpb->RealDevice->Flags, DO_VERIFY_VOLUME)) {
1757
1758 DebugTrace(0, Dbg, "RealDevice has already been verified\n", 0);
1759
1760 VerifyAlreadyDone = TRUE;
1761 try_return( Status = STATUS_SUCCESS );
1762 }
1763
1764 //
1765 // Ping the volume with a partition query to make Jeff happy.
1766 //
1767
1768 {
1769 PARTITION_INFORMATION_EX PartitionInformation;
1770
1771 (VOID) FatPerformDevIoCtrl( IrpContext,
1772 IOCTL_DISK_GET_PARTITION_INFO_EX,
1773 Vcb->TargetDeviceObject,
1774 &PartitionInformation,
1775 sizeof(PARTITION_INFORMATION_EX),
1776 FALSE,
1777 TRUE,
1778 &Iosb );
1779 }
1780
1781 //
1782 // Verify that there is a disk here and pick up the change count.
1783 //
1784
1785 Status = FatPerformDevIoCtrl( IrpContext,
1786 IOCTL_DISK_CHECK_VERIFY,
1787 Vcb->TargetDeviceObject,
1788 &ChangeCount,
1789 sizeof(ULONG),
1790 FALSE,
1791 TRUE,
1792 &Iosb );
1793
1794 if (!NT_SUCCESS( Status )) {
1795
1796 //
1797 // If we will allow a raw mount then return WRONG_VOLUME to
1798 // allow the volume to be mounted by raw.
1799 //
1800
1801 if (AllowRawMount) {
1802
1803 try_return( Status = STATUS_WRONG_VOLUME );
1804 }
1805
1806 FatNormalizeAndRaiseStatus( IrpContext, Status );
1807 }
1808
1809 if (Iosb.Information != sizeof(ULONG)) {
1810
1811 //
1812 // Be safe about the count in case the driver didn't fill it in
1813 //
1814
1815 ChangeCount = 0;
1816 }
1817
1818 //
1819 // Whatever happens we will have verified this volume at this change
1820 // count, so record that fact.
1821 //
1822
1823 Vcb->ChangeCount = ChangeCount;
1824
1825 //
1826 // If this is a CD class device, then check to see if there is a
1827 // 'data track' or not. This is to avoid issuing paging reads which will
1828 // fail later in the mount process (e.g. CD-DA or blank CD media)
1829 //
1830
1831 if ((Vcb->TargetDeviceObject->DeviceType == FILE_DEVICE_CD_ROM) &&
1832 !FatScanForDataTrack( IrpContext, Vcb->TargetDeviceObject)) {
1833
1834 try_return( Status = STATUS_WRONG_VOLUME);
1835 }
1836
1837 //
1838 // Some devices can change sector sizes on the fly. Obviously, it
1839 // isn't the same volume if that happens.
1840 //
1841
1842 Status = FatPerformDevIoCtrl( IrpContext,
1843 IOCTL_DISK_GET_DRIVE_GEOMETRY,
1844 Vcb->TargetDeviceObject,
1845 &DiskGeometry,
1846 sizeof( DISK_GEOMETRY ),
1847 FALSE,
1848 TRUE,
1849 NULL );
1850
1851 if (!NT_SUCCESS( Status )) {
1852
1853 //
1854 // If we will allow a raw mount then return WRONG_VOLUME to
1855 // allow the volume to be mounted by raw.
1856 //
1857
1858 if (AllowRawMount) {
1859
1860 try_return( Status = STATUS_WRONG_VOLUME );
1861 }
1862
1863 FatNormalizeAndRaiseStatus( IrpContext, Status );
1864 }
1865
1866 //
1867 // Read in the boot sector
1868 //
1869
1870 SectorSize = (ULONG)Vcb->Bpb.BytesPerSector;
1871
1872 if (SectorSize != DiskGeometry.BytesPerSector) {
1873
1874 try_return( Status = STATUS_WRONG_VOLUME );
1875 }
1876
1877 BootSector = FsRtlAllocatePoolWithTag(NonPagedPoolCacheAligned,
1878 (ULONG) ROUND_TO_PAGES( SectorSize ),
1879 TAG_VERIFY_BOOTSECTOR);
1880
1881 //
1882 // If this verify is on behalf of a DASD open, allow a RAW mount.
1883 //
1884
1885 if (!FatPerformVerifyDiskRead( IrpContext,
1886 Vcb,
1887 BootSector,
1888 0,
1889 SectorSize,
1890 AllowRawMount )) {
1891
1892 try_return( Status = STATUS_WRONG_VOLUME );
1893 }
1894
1895 //
1896 // Call a routine to check the boot sector to see if it is fat.
1897 // If it is not fat then mark the vcb as not mounted tell our
1898 // caller its the wrong volume
1899 //
1900
1901 if (!FatIsBootSectorFat( BootSector )) {
1902
1903 DebugTrace(0, Dbg, "Not a Fat Volume\n", 0);
1904
1905 try_return( Status = STATUS_WRONG_VOLUME );
1906 }
1907
1908 //
1909 // This is a fat volume, so extract serial number and see if it is
1910 // ours.
1911 //
1912
1913 {
1914 ULONG SerialNumber;
1915
1916 if (IsBpbFat32(&BootSector->PackedBpb)) {
1917 CopyUchar4( &SerialNumber, ((PPACKED_BOOT_SECTOR_EX)BootSector)->Id );
1918 } else {
1919 CopyUchar4( &SerialNumber, BootSector->Id );
1920 }
1921
1922 if (SerialNumber != Vpb->SerialNumber) {
1923
1924 DebugTrace(0, Dbg, "Not our serial number\n", 0);
1925
1926 try_return( Status = STATUS_WRONG_VOLUME );
1927 }
1928 }
1929
1930 //
1931 // Make sure the Bpbs are not different. We have to zero out our
1932 // stack version of the Bpb since unpacking leaves holes.
1933 //
1934
1935 RtlZeroMemory( &Bpb, sizeof(BIOS_PARAMETER_BLOCK) );
1936
1937 FatUnpackBios( &Bpb, &BootSector->PackedBpb );
1938 if (Bpb.Sectors != 0) { Bpb.LargeSectors = 0; }
1939
1940 if ( !RtlEqualMemory( &Bpb,
1941 &Vcb->Bpb,
1942 IsBpbFat32(&Bpb) ?
1943 sizeof(BIOS_PARAMETER_BLOCK) :
1944 FIELD_OFFSET(BIOS_PARAMETER_BLOCK,
1945 LargeSectorsPerFat) )) {
1946
1947 DebugTrace(0, Dbg, "Bpb is different\n", 0);
1948
1949 try_return( Status = STATUS_WRONG_VOLUME );
1950 }
1951
1952 //
1953 // Check the volume label. We do this by trying to locate the
1954 // volume label, making two strings one for the saved volume label
1955 // and the other for the new volume label and then we compare the
1956 // two labels.
1957 //
1958
1959 if (FatRootDirectorySize(&Bpb) > 0) {
1960
1961 RootDirectorySize = FatRootDirectorySize(&Bpb);
1962
1963 } else {
1964
1965 RootDirectorySize = FatBytesPerCluster(&Bpb);
1966 }
1967
1968 RootDirectory = FsRtlAllocatePoolWithTag( NonPagedPoolCacheAligned,
1969 (ULONG) ROUND_TO_PAGES( RootDirectorySize ),
1970 TAG_VERIFY_ROOTDIR);
1971
1972 if (!IsBpbFat32(&BootSector->PackedBpb)) {
1973
1974 //
1975 // The Fat12/16 case is simple -- read the root directory in and
1976 // search it.
1977 //
1978
1979 RootDirectoryLbo = FatRootDirectoryLbo(&Bpb);
1980
1981 if (!FatPerformVerifyDiskRead( IrpContext,
1982 Vcb,
1983 RootDirectory,
1984 RootDirectoryLbo,
1985 RootDirectorySize,
1986 AllowRawMount )) {
1987
1988 try_return( Status = STATUS_WRONG_VOLUME );
1989 }
1990
1991 Status = FatSearchBufferForLabel(IrpContext, Vpb,
1992 RootDirectory, RootDirectorySize,
1993 &LabelFound);
1994
1995 if (!NT_SUCCESS(Status)) {
1996
1997 try_return( Status );
1998 }
1999
2000 if (!LabelFound && Vpb->VolumeLabelLength > 0) {
2001
2002 try_return( Status = STATUS_WRONG_VOLUME );
2003 }
2004
2005 } else {
2006
2007 ULONG RootDirectoryCluster;
2008
2009 RootDirectoryCluster = Bpb.RootDirFirstCluster;
2010
2011 while (RootDirectoryCluster != FAT_CLUSTER_LAST) {
2012
2013 RootDirectoryLbo = FatGetLboFromIndex(Vcb, RootDirectoryCluster);
2014
2015 if (!FatPerformVerifyDiskRead( IrpContext,
2016 Vcb,
2017 RootDirectory,
2018 RootDirectoryLbo,
2019 RootDirectorySize,
2020 AllowRawMount )) {
2021
2022 try_return( Status = STATUS_WRONG_VOLUME );
2023 }
2024
2025 Status = FatSearchBufferForLabel(IrpContext, Vpb,
2026 RootDirectory, RootDirectorySize,
2027 &LabelFound);
2028
2029 if (!NT_SUCCESS(Status)) {
2030
2031 try_return( Status );
2032 }
2033
2034 if (LabelFound) {
2035
2036 //
2037 // Found a matching label.
2038 //
2039
2040 break;
2041 }
2042
2043 //
2044 // Set ourselves up for the next loop iteration.
2045 //
2046
2047 FatVerifyLookupFatEntry( IrpContext, Vcb,
2048 RootDirectoryCluster,
2049 &RootDirectoryCluster );
2050
2051 switch (FatInterpretClusterType(Vcb, RootDirectoryCluster)) {
2052
2053 case FatClusterAvailable:
2054 case FatClusterReserved:
2055 case FatClusterBad:
2056
2057 //
2058 // Bail all the way out if we have a bad root.
2059 //
2060
2061 FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
2062 break;
2063
2064 default:
2065
2066 break;
2067 }
2068
2069 }
2070
2071 if (RootDirectoryCluster == FAT_CLUSTER_LAST &&
2072 Vpb->VolumeLabelLength > 0) {
2073
2074 //
2075 // Should have found a label, didn't find any.
2076 //
2077
2078 try_return( Status = STATUS_WRONG_VOLUME );
2079 }
2080 }
2081
2082
2083 try_exit: NOTHING;
2084
2085 //
2086 // Note that we have previously acquired the Vcb to serialize
2087 // the EA file stuff the marking all the Fcbs as NeedToBeVerified.
2088 //
2089 // Put the Ea file back in a virgin state.
2090 //
2091
2092 FatCloseEaFile( IrpContext, Vcb, (BOOLEAN)(Status == STATUS_SUCCESS) );
2093
2094 //
2095 // Mark all Fcbs as needing verification, but only if we really have
2096 // to do it.
2097 //
2098
2099 if (!VerifyAlreadyDone) {
2100
2101 FatMarkFcbCondition( IrpContext, Vcb->RootDcb, FcbNeedsToBeVerified, TRUE );
2102 }
2103
2104 //
2105 // If the verify didn't succeed, get the volume ready for a
2106 // remount or eventual deletion.
2107 //
2108
2109 if (Vcb->VcbCondition == VcbNotMounted) {
2110
2111 //
2112 // If the volume was already in an unmounted state, just bail
2113 // and make sure we return STATUS_WRONG_VOLUME.
2114 //
2115
2116 Status = STATUS_WRONG_VOLUME;
2117
2118 } else if ( Status == STATUS_WRONG_VOLUME ) {
2119
2120 //
2121 // Grab everything so we can safely transition the volume state without
2122 // having a thread stumble into the torn-down allocation engine.
2123 //
2124
2125 FatAcquireExclusiveVolume( IrpContext, Vcb );
2126 ReleaseEntireVolume = TRUE;
2127
2128 //
2129 // Get rid of any cached data, without flushing
2130 //
2131
2132 FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, NoFlush );
2133
2134 //
2135 // Uninitialize the volume file cache map. Note that we cannot
2136 // do a "FatSyncUninit" because of deadlock problems. However,
2137 // since this FileObject is referenced by us, and thus included
2138 // in the Vpb residual count, it is OK to do a normal CcUninit.
2139 //
2140
2141 CcUninitializeCacheMap( Vcb->VirtualVolumeFile,
2142 &FatLargeZero,
2143 NULL );
2144
2145 FatTearDownAllocationSupport( IrpContext, Vcb );
2146
2147 Vcb->VcbCondition = VcbNotMounted;
2148
2149 ClearVerify = TRUE;
2150
2151 } else if (!VerifyAlreadyDone) {
2152
2153 //
2154 // Grab everything so we can safely transition the volume state without
2155 // having a thread stumble into the torn-down allocation engine.
2156 //
2157
2158 FatAcquireExclusiveVolume( IrpContext, Vcb );
2159 ReleaseEntireVolume = TRUE;
2160
2161 //
2162 // Get rid of any cached data, flushing first.
2163 //
2164 // Future work (and for bonus points, around the other flush points)
2165 // could address the possibility that the dirent filesize hasn't been
2166 // updated yet, causing us to fail the re-verification of a file in
2167 // DetermineAndMark. This is pretty subtle and very very uncommon.
2168 //
2169
2170 FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush );
2171
2172 //
2173 // Flush and Purge the volume file.
2174 //
2175
2176 (VOID)FatFlushFat( IrpContext, Vcb );
2177 CcPurgeCacheSection( &Vcb->SectionObjectPointers, NULL, 0, FALSE );
2178
2179 //
2180 // Redo the allocation support with newly paged stuff.
2181 //
2182
2183 FatTearDownAllocationSupport( IrpContext, Vcb );
2184 FatSetupAllocationSupport( IrpContext, Vcb );
2185
2186 FatCheckDirtyBit( IrpContext, Vcb );
2187
2188 //
2189 // Check for write protected media.
2190 //
2191
2192 if (FatIsMediaWriteProtected(IrpContext, Vcb->TargetDeviceObject)) {
2193
2194 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
2195
2196 } else {
2197
2198 ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
2199 }
2200
2201 ClearVerify = TRUE;
2202 }
2203
2204 if (ClearVerify) {
2205
2206 //
2207 // Mark the device as no longer needing verification.
2208 //
2209
2210 ClearFlag( Vpb->RealDevice->Flags, DO_VERIFY_VOLUME );
2211 }
2212
2213 } _SEH2_FINALLY {
2214
2215 DebugUnwind( FatVerifyVolume );
2216
2217 //
2218 // Free any buffer we may have allocated
2219 //
2220
2221 if ( BootSector != NULL ) { ExFreePool( BootSector ); }
2222 if ( RootDirectory != NULL ) { ExFreePool( RootDirectory ); }
2223
2224 //
2225 // Show that we are done with this volume.
2226 //
2227
2228 ASSERT( Vcb->VerifyThread == KeGetCurrentThread() );
2229 Vcb->VerifyThread = NULL;
2230
2231 if (ReleaseEntireVolume) {
2232
2233 FatReleaseVolume( IrpContext, Vcb );
2234 }
2235
2236 FatReleaseVcb( IrpContext, Vcb );
2237 FatReleaseGlobal( IrpContext );
2238
2239 //
2240 // If this was not an abnormal termination, complete the irp.
2241 //
2242
2243 if (!_SEH2_AbnormalTermination()) {
2244
2245 FatCompleteRequest( IrpContext, Irp, Status );
2246 }
2247
2248 DebugTrace(-1, Dbg, "FatVerifyVolume -> %08lx\n", Status);
2249 } _SEH2_END;
2250
2251 return Status;
2252 }
2253
2254 \f
2255 //
2256 // Local Support Routine
2257 //
2258
2259 BOOLEAN
2260 FatIsBootSectorFat (
2261 IN PPACKED_BOOT_SECTOR BootSector
2262 )
2263
2264 /*++
2265
2266 Routine Description:
2267
2268 This routine checks if the boot sector is for a fat file volume.
2269
2270 Arguments:
2271
2272 BootSector - Supplies the packed boot sector to check
2273
2274 Return Value:
2275
2276 BOOLEAN - TRUE if the volume is Fat and FALSE otherwise.
2277
2278 --*/
2279
2280 {
2281 BOOLEAN Result;
2282 BIOS_PARAMETER_BLOCK Bpb;
2283
2284 DebugTrace(+1, Dbg, "FatIsBootSectorFat, BootSector = %08lx\n", BootSector);
2285
2286 //
2287 // The result is true unless we decide that it should be false
2288 //
2289
2290 Result = TRUE;
2291
2292 //
2293 // Unpack the bios and then test everything
2294 //
2295
2296 FatUnpackBios( &Bpb, &BootSector->PackedBpb );
2297 if (Bpb.Sectors != 0) { Bpb.LargeSectors = 0; }
2298
2299 if ((BootSector->Jump[0] != 0xe9) &&
2300 (BootSector->Jump[0] != 0xeb) &&
2301 (BootSector->Jump[0] != 0x49)) {
2302
2303 Result = FALSE;
2304
2305 //
2306 // Enforce some sanity on the sector size (easy check)
2307 //
2308
2309 } else if ((Bpb.BytesPerSector != 128) &&
2310 (Bpb.BytesPerSector != 256) &&
2311 (Bpb.BytesPerSector != 512) &&
2312 (Bpb.BytesPerSector != 1024) &&
2313 (Bpb.BytesPerSector != 2048) &&
2314 (Bpb.BytesPerSector != 4096)) {
2315
2316 Result = FALSE;
2317
2318 //
2319 // Likewise on the clustering.
2320 //
2321
2322 } else if ((Bpb.SectorsPerCluster != 1) &&
2323 (Bpb.SectorsPerCluster != 2) &&
2324 (Bpb.SectorsPerCluster != 4) &&
2325 (Bpb.SectorsPerCluster != 8) &&
2326 (Bpb.SectorsPerCluster != 16) &&
2327 (Bpb.SectorsPerCluster != 32) &&
2328 (Bpb.SectorsPerCluster != 64) &&
2329 (Bpb.SectorsPerCluster != 128)) {
2330
2331 Result = FALSE;
2332
2333 //
2334 // Likewise on the reserved sectors (must reflect at least the boot sector!)
2335 //
2336
2337 } else if (Bpb.ReservedSectors == 0) {
2338
2339 Result = FALSE;
2340
2341 //
2342 // No FATs? Wrong ...
2343 //
2344
2345 } else if (Bpb.Fats == 0) {
2346
2347 Result = FALSE;
2348
2349 //
2350 // Prior to DOS 3.2 might contains value in both of Sectors and
2351 // Sectors Large.
2352 //
2353
2354 } else if ((Bpb.Sectors == 0) && (Bpb.LargeSectors == 0)) {
2355
2356 Result = FALSE;
2357
2358 //
2359 // Check that FAT32 (SectorsPerFat == 0) claims some FAT space and
2360 // is of a version we recognize, currently Version 0.0.
2361 //
2362
2363 } else if (Bpb.SectorsPerFat == 0 && ( Bpb.LargeSectorsPerFat == 0 ||
2364 Bpb.FsVersion != 0 )) {
2365
2366 Result = FALSE;
2367
2368 } else if ((Bpb.Media != 0xf0) &&
2369 (Bpb.Media != 0xf8) &&
2370 (Bpb.Media != 0xf9) &&
2371 (Bpb.Media != 0xfb) &&
2372 (Bpb.Media != 0xfc) &&
2373 (Bpb.Media != 0xfd) &&
2374 (Bpb.Media != 0xfe) &&
2375 (Bpb.Media != 0xff) &&
2376 (!FatData.FujitsuFMR || ((Bpb.Media != 0x00) &&
2377 (Bpb.Media != 0x01) &&
2378 (Bpb.Media != 0xfa)))) {
2379
2380 Result = FALSE;
2381
2382 //
2383 // If this isn't FAT32, then there better be a claimed root directory
2384 // size here ...
2385 //
2386
2387 } else if (Bpb.SectorsPerFat != 0 && Bpb.RootEntries == 0) {
2388
2389 Result = FALSE;
2390
2391 //
2392 // If this is FAT32 (i.e., extended BPB), look for and refuse to mount
2393 // mirror-disabled volumes. If we did, we would need to only write to
2394 // the FAT# indicated in the ActiveFat field. The only user of this is
2395 // the FAT->FAT32 converter after the first pass of protected mode work
2396 // (booting into realmode) and NT should absolutely not be attempting
2397 // to mount such an in-transition volume.
2398 //
2399
2400 } else if (Bpb.SectorsPerFat == 0 && Bpb.MirrorDisabled) {
2401
2402 Result = FALSE;
2403 }
2404
2405 DebugTrace(-1, Dbg, "FatIsBootSectorFat -> %08lx\n", Result);
2406
2407 return Result;
2408 }
2409
2410 \f
2411 //
2412 // Local Support Routine
2413 //
2414
2415 BOOLEAN
2416 FatIsMediaWriteProtected (
2417 IN PIRP_CONTEXT IrpContext,
2418 IN PDEVICE_OBJECT TargetDeviceObject
2419 )
2420
2421 /*++
2422
2423 Routine Description:
2424
2425 This routine determines if the target media is write protected.
2426
2427 Arguments:
2428
2429 TargetDeviceObject - The target of the query
2430
2431 Return Value:
2432
2433 NTSTATUS - The return status for the operation
2434
2435 --*/
2436
2437 {
2438 PIRP Irp;
2439 KEVENT Event;
2440 NTSTATUS Status;
2441 IO_STATUS_BLOCK Iosb;
2442
2443 //
2444 // Query the partition table
2445 //
2446
2447 KeInitializeEvent( &Event, NotificationEvent, FALSE );
2448
2449 //
2450 // See if the media is write protected. On success or any kind
2451 // of error (possibly illegal device function), assume it is
2452 // writeable, and only complain if he tells us he is write protected.
2453 //
2454
2455 Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_IS_WRITABLE,
2456 TargetDeviceObject,
2457 NULL,
2458 0,
2459 NULL,
2460 0,
2461 FALSE,
2462 &Event,
2463 &Iosb );
2464
2465 //
2466 // Just return FALSE in the unlikely event we couldn't allocate an Irp.
2467 //
2468
2469 if ( Irp == NULL ) {
2470
2471 return FALSE;
2472 }
2473
2474 SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME );
2475
2476 Status = IoCallDriver( TargetDeviceObject, Irp );
2477
2478 if ( Status == STATUS_PENDING ) {
2479
2480 (VOID) KeWaitForSingleObject( &Event,
2481 Executive,
2482 KernelMode,
2483 FALSE,
2484 (PLARGE_INTEGER)NULL );
2485
2486 Status = Iosb.Status;
2487 }
2488
2489 return (BOOLEAN)(Status == STATUS_MEDIA_WRITE_PROTECTED);
2490 }
2491
2492 \f
2493 //
2494 // Local Support Routine
2495 //
2496
2497 NTSTATUS
2498 FatUserFsCtrl (
2499 IN PIRP_CONTEXT IrpContext,
2500 IN PIRP Irp
2501 )
2502
2503 /*++
2504
2505 Routine Description:
2506
2507 This is the common routine for implementing the user's requests made
2508 through NtFsControlFile.
2509
2510 Arguments:
2511
2512 Irp - Supplies the Irp being processed
2513
2514 Return Value:
2515
2516 NTSTATUS - The return status for the operation
2517
2518 --*/
2519
2520 {
2521 NTSTATUS Status;
2522 ULONG FsControlCode;
2523
2524 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
2525
2526 //
2527 // Save some references to make our life a little easier
2528 //
2529
2530 FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
2531
2532 DebugTrace(+1, Dbg,"FatUserFsCtrl...\n", 0);
2533 DebugTrace( 0, Dbg,"FsControlCode = %08lx\n", FsControlCode);
2534
2535 //
2536 // Some of these Fs Controls use METHOD_NEITHER buffering. If the previous mode
2537 // of the caller was userspace and this is a METHOD_NEITHER, we have the choice
2538 // of realy buffering the request through so we can possibly post, or making the
2539 // request synchronous. Since the former was not done by design, do the latter.
2540 //
2541
2542 if (Irp->RequestorMode != KernelMode && (FsControlCode & 3) == METHOD_NEITHER) {
2543
2544 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
2545 }
2546
2547 //
2548 // Case on the control code.
2549 //
2550
2551 switch ( FsControlCode ) {
2552
2553 case FSCTL_REQUEST_OPLOCK_LEVEL_1:
2554 case FSCTL_REQUEST_OPLOCK_LEVEL_2:
2555 case FSCTL_REQUEST_BATCH_OPLOCK:
2556 case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
2557 case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
2558 case FSCTL_OPLOCK_BREAK_NOTIFY:
2559 case FSCTL_OPLOCK_BREAK_ACK_NO_2:
2560 case FSCTL_REQUEST_FILTER_OPLOCK :
2561
2562 Status = FatOplockRequest( IrpContext, Irp );
2563 break;
2564
2565 case FSCTL_LOCK_VOLUME:
2566
2567 Status = FatLockVolume( IrpContext, Irp );
2568 break;
2569
2570 case FSCTL_UNLOCK_VOLUME:
2571
2572 Status = FatUnlockVolume( IrpContext, Irp );
2573 break;
2574
2575 case FSCTL_DISMOUNT_VOLUME:
2576
2577 Status = FatDismountVolume( IrpContext, Irp );
2578 break;
2579
2580 case FSCTL_MARK_VOLUME_DIRTY:
2581
2582 Status = FatDirtyVolume( IrpContext, Irp );
2583 break;
2584
2585 case FSCTL_IS_VOLUME_DIRTY:
2586
2587 Status = FatIsVolumeDirty( IrpContext, Irp );
2588 break;
2589
2590 case FSCTL_IS_VOLUME_MOUNTED:
2591
2592 Status = FatIsVolumeMounted( IrpContext, Irp );
2593 break;
2594
2595 case FSCTL_IS_PATHNAME_VALID:
2596 Status = FatIsPathnameValid( IrpContext, Irp );
2597 break;
2598
2599 case FSCTL_QUERY_RETRIEVAL_POINTERS:
2600 Status = FatQueryRetrievalPointers( IrpContext, Irp );
2601 break;
2602
2603 case FSCTL_QUERY_FAT_BPB:
2604 Status = FatQueryBpb( IrpContext, Irp );
2605 break;
2606
2607 case FSCTL_FILESYSTEM_GET_STATISTICS:
2608 Status = FatGetStatistics( IrpContext, Irp );
2609 break;
2610
2611 case FSCTL_GET_VOLUME_BITMAP:
2612 Status = FatGetVolumeBitmap( IrpContext, Irp );
2613 break;
2614
2615 case FSCTL_GET_RETRIEVAL_POINTERS:
2616 Status = FatGetRetrievalPointers( IrpContext, Irp );
2617 break;
2618
2619 case FSCTL_MOVE_FILE:
2620 Status = FatMoveFile( IrpContext, Irp );
2621 break;
2622
2623 case FSCTL_ALLOW_EXTENDED_DASD_IO:
2624 Status = FatAllowExtendedDasdIo( IrpContext, Irp );
2625 break;
2626
2627 default :
2628
2629 DebugTrace(0, Dbg, "Invalid control code -> %08lx\n", FsControlCode );
2630
2631 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
2632 Status = STATUS_INVALID_DEVICE_REQUEST;
2633 break;
2634 }
2635
2636 DebugTrace(-1, Dbg, "FatUserFsCtrl -> %08lx\n", Status );
2637 return Status;
2638 }
2639
2640
2641 \f
2642 //
2643 // Local support routine
2644 //
2645
2646 NTSTATUS
2647 FatOplockRequest (
2648 IN PIRP_CONTEXT IrpContext,
2649 IN PIRP Irp
2650 )
2651
2652 /*++
2653
2654 Routine Description:
2655
2656 This is the common routine to handle oplock requests made via the
2657 NtFsControlFile call.
2658
2659 Arguments:
2660
2661 Irp - Supplies the Irp being processed
2662
2663 Return Value:
2664
2665 NTSTATUS - The return status for the operation
2666
2667 --*/
2668
2669 {
2670 NTSTATUS Status;
2671 ULONG FsControlCode;
2672 PFCB Fcb;
2673 PVCB Vcb;
2674 PCCB Ccb;
2675
2676 ULONG OplockCount = 0;
2677
2678 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
2679
2680 BOOLEAN AcquiredVcb = FALSE;
2681 BOOLEAN AcquiredFcb = FALSE;
2682
2683 //
2684 // Save some references to make our life a little easier
2685 //
2686
2687 FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
2688
2689 DebugTrace(+1, Dbg, "FatOplockRequest...\n", 0);
2690 DebugTrace( 0, Dbg, "FsControlCode = %08lx\n", FsControlCode);
2691
2692 //
2693 // We only permit oplock requests on files.
2694 //
2695
2696 if ( FatDecodeFileObject( IrpSp->FileObject,
2697 &Vcb,
2698 &Fcb,
2699 &Ccb ) != UserFileOpen ) {
2700
2701 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
2702 DebugTrace(-1, Dbg, "FatOplockRequest -> STATUS_INVALID_PARAMETER\n", 0);
2703 return STATUS_INVALID_PARAMETER;
2704 }
2705
2706 //
2707 // Make this a waitable Irpcontext so we don't fail to acquire
2708 // the resources.
2709 //
2710
2711 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
2712
2713 //
2714 // Use a try finally to free the Fcb/Vcb
2715 //
2716
2717 _SEH2_TRY {
2718
2719 //
2720 // Switch on the function control code. We grab the Fcb exclusively
2721 // for oplock requests, shared for oplock break acknowledgement.
2722 //
2723
2724 switch ( FsControlCode ) {
2725
2726 case FSCTL_REQUEST_OPLOCK_LEVEL_1:
2727 case FSCTL_REQUEST_OPLOCK_LEVEL_2:
2728 case FSCTL_REQUEST_BATCH_OPLOCK:
2729 case FSCTL_REQUEST_FILTER_OPLOCK :
2730
2731 FatAcquireSharedVcb( IrpContext, Fcb->Vcb );
2732 AcquiredVcb = TRUE;
2733 FatAcquireExclusiveFcb( IrpContext, Fcb );
2734 AcquiredFcb = TRUE;
2735
2736 if (FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2) {
2737
2738 OplockCount = (ULONG) FsRtlAreThereCurrentFileLocks( &Fcb->Specific.Fcb.FileLock );
2739
2740 } else {
2741
2742 OplockCount = Fcb->UncleanCount;
2743 }
2744
2745 break;
2746
2747 case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
2748 case FSCTL_OPBATCH_ACK_CLOSE_PENDING :
2749 case FSCTL_OPLOCK_BREAK_NOTIFY:
2750 case FSCTL_OPLOCK_BREAK_ACK_NO_2:
2751
2752 FatAcquireSharedFcb( IrpContext, Fcb );
2753 AcquiredFcb = TRUE;
2754 break;
2755
2756 default:
2757
2758 FatBugCheck( FsControlCode, 0, 0 );
2759 }
2760
2761 //
2762 // Call the FsRtl routine to grant/acknowledge oplock.
2763 //
2764
2765 Status = FsRtlOplockFsctrl( &Fcb->Specific.Fcb.Oplock,
2766 Irp,
2767 OplockCount );
2768
2769 //
2770 // Set the flag indicating if Fast I/O is possible
2771 //
2772
2773 Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb );
2774
2775 } _SEH2_FINALLY {
2776
2777 DebugUnwind( FatOplockRequest );
2778
2779 //
2780 // Release all of our resources
2781 //
2782
2783 if (AcquiredVcb) {
2784
2785 FatReleaseVcb( IrpContext, Fcb->Vcb );
2786 }
2787
2788 if (AcquiredFcb) {
2789
2790 FatReleaseFcb( IrpContext, Fcb );
2791 }
2792
2793 if (!_SEH2_AbnormalTermination()) {
2794
2795 FatCompleteRequest( IrpContext, FatNull, 0 );
2796 }
2797
2798 DebugTrace(-1, Dbg, "FatOplockRequest -> %08lx\n", Status );
2799 } _SEH2_END;
2800
2801 return Status;
2802 }
2803
2804 \f
2805 //
2806 // Local Support Routine
2807 //
2808
2809 NTSTATUS
2810 FatLockVolume (
2811 IN PIRP_CONTEXT IrpContext,
2812 IN PIRP Irp
2813 )
2814
2815 /*++
2816
2817 Routine Description:
2818
2819 This routine performs the lock volume operation. It is responsible for
2820 either completing of enqueuing the input Irp.
2821
2822 Arguments:
2823
2824 Irp - Supplies the Irp to process
2825
2826 Return Value:
2827
2828 NTSTATUS - The return status for the operation
2829
2830 --*/
2831
2832 {
2833 NTSTATUS Status;
2834
2835 PIO_STACK_LOCATION IrpSp;
2836
2837 PVCB Vcb;
2838 PFCB Fcb;
2839 PCCB Ccb;
2840
2841 IrpSp = IoGetCurrentIrpStackLocation( Irp );
2842
2843 DebugTrace(+1, Dbg, "FatLockVolume...\n", 0);
2844
2845 //
2846 // Decode the file object, the only type of opens we accept are
2847 // user volume opens.
2848 //
2849
2850 if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
2851
2852 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
2853
2854 DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
2855 return STATUS_INVALID_PARAMETER;
2856 }
2857
2858 if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
2859
2860 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
2861
2862 DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
2863 return STATUS_INVALID_PARAMETER;
2864 }
2865
2866 //
2867 // Send our notification so that folks that like to hold handles on
2868 // volumes can get out of the way.
2869 //
2870
2871 FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_LOCK );
2872
2873 //
2874 // Acquire exclusive access to the Vcb and enqueue the Irp if we
2875 // didn't get access.
2876 //
2877
2878 if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {
2879
2880 DebugTrace( 0, Dbg, "Cannot acquire Vcb\n", 0);
2881
2882 Status = FatFsdPostRequest( IrpContext, Irp );
2883
2884 DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", Status);
2885 return Status;
2886 }
2887
2888 _SEH2_TRY {
2889
2890 Status = FatLockVolumeInternal( IrpContext, Vcb, IrpSp->FileObject );
2891
2892 } _SEH2_FINALLY {
2893
2894 //
2895 // Since we drop and release the vcb while trying to punch the volume
2896 // down, it may be the case that we decide the operation should not
2897 // continue if the user raced a CloeseHandle() with us (and it finished
2898 // the cleanup) while we were waiting for our closes to finish.
2899 //
2900 // In this case, we will have been raised out of the acquire logic with
2901 // STATUS_FILE_CLOSED, and the volume will not be held.
2902 //
2903
2904 if (!_SEH2_AbnormalTermination() || ExIsResourceAcquiredExclusiveLite( &Vcb->Resource )) {
2905
2906 FatReleaseVcb( IrpContext, Vcb );
2907 }
2908
2909 if (!NT_SUCCESS( Status ) || _SEH2_AbnormalTermination()) {
2910
2911 //
2912 // The volume lock will be failing.
2913 //
2914
2915 FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_LOCK_FAILED );
2916 }
2917 } _SEH2_END;
2918
2919 FatCompleteRequest( IrpContext, Irp, Status );
2920
2921 DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", Status);
2922
2923 return Status;
2924 }
2925
2926 \f
2927 //
2928 // Local Support Routine
2929 //
2930
2931 NTSTATUS
2932 FatUnlockVolume (
2933 IN PIRP_CONTEXT IrpContext,
2934 IN PIRP Irp
2935 )
2936
2937 /*++
2938
2939 Routine Description:
2940
2941 This routine performs the unlock volume operation. It is responsible for
2942 either completing of enqueuing the input Irp.
2943
2944 Arguments:
2945
2946 Irp - Supplies the Irp to process
2947
2948 Return Value:
2949
2950 NTSTATUS - The return status for the operation
2951
2952 --*/
2953
2954 {
2955 NTSTATUS Status;
2956
2957 PIO_STACK_LOCATION IrpSp;
2958
2959 PVCB Vcb;
2960 PFCB Fcb;
2961 PCCB Ccb;
2962
2963 IrpSp = IoGetCurrentIrpStackLocation( Irp );
2964
2965 DebugTrace(+1, Dbg, "FatUnlockVolume...\n", 0);
2966
2967 //
2968 // Decode the file object, the only type of opens we accept are
2969 // user volume opens.
2970 //
2971
2972 if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
2973
2974 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
2975
2976 DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
2977 return STATUS_INVALID_PARAMETER;
2978 }
2979
2980 if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
2981
2982 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
2983
2984 DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
2985 return STATUS_INVALID_PARAMETER;
2986 }
2987
2988 Status = FatUnlockVolumeInternal( IrpContext, Vcb, IrpSp->FileObject );
2989
2990 //
2991 // Send notification that the volume is avaliable.
2992 //
2993
2994 if (NT_SUCCESS( Status )) {
2995
2996 FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_UNLOCK );
2997 }
2998
2999 FatCompleteRequest( IrpContext, Irp, Status );
3000
3001 DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", Status);
3002
3003 return Status;
3004 }
3005
3006 \f
3007 NTSTATUS
3008 FatLockVolumeInternal (
3009 IN PIRP_CONTEXT IrpContext,
3010 IN PVCB Vcb,
3011 IN PFILE_OBJECT FileObject OPTIONAL
3012 )
3013
3014 /*++
3015
3016 Routine Description:
3017
3018 This routine performs the actual lock volume operation. It will be called
3019 by anyone wishing to try to protect the volume for a long duration. PNP
3020 operations are such a user.
3021
3022 The volume must be held exclusive by the caller.
3023
3024 Arguments:
3025
3026 Vcb - The volume being locked.
3027
3028 FileObject - File corresponding to the handle locking the volume. If this
3029 is not specified, a system lock is assumed.
3030
3031 Return Value:
3032
3033 NTSTATUS - The return status for the operation
3034
3035 --*/
3036
3037 {
3038 NTSTATUS Status = STATUS_SUCCESS;
3039 KIRQL SavedIrql;
3040 ULONG RemainingUserReferences = (FileObject? 1: 0);
3041
3042 ASSERT( ExIsResourceAcquiredExclusiveLite( &Vcb->Resource ) &&
3043 !ExIsResourceAcquiredExclusiveLite( &FatData.Resource ));
3044 //
3045 // Go synchronous for the rest of the lock operation. It may be
3046 // reasonable to try to revisit this in the future, but for now
3047 // the purge below expects to be able to wait.
3048 //
3049 // We know it is OK to leave the flag up given how we're used at
3050 // the moment.
3051 //
3052
3053 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
3054
3055 //
3056 // If there are any open handles, this will fail.
3057 //
3058
3059 if (!FatIsHandleCountZero( IrpContext, Vcb )) {
3060
3061 return STATUS_ACCESS_DENIED;
3062 }
3063
3064 //
3065 // Force Mm to get rid of its referenced file objects.
3066 //
3067
3068 FatFlushFat( IrpContext, Vcb );
3069
3070 FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush );
3071
3072 FatCloseEaFile( IrpContext, Vcb, TRUE );
3073
3074 //
3075 // Now back out of our synchronization and wait for the lazy writer
3076 // to finish off any lazy closes that could have been outstanding.
3077 //
3078 // Since we flushed, we know that the lazy writer will issue all
3079 // possible lazy closes in the next tick - if we hadn't, an otherwise
3080 // unopened file with a large amount of dirty data could have hung
3081 // around for a while as the data trickled out to the disk.
3082 //
3083 // This is even more important now since we send notification to
3084 // alert other folks that this style of check is about to happen so
3085 // that they can close their handles. We don't want to enter a fast
3086 // race with the lazy writer tearing down his references to the file.
3087 //
3088
3089 FatReleaseVcb( IrpContext, Vcb );
3090
3091 Status = CcWaitForCurrentLazyWriterActivity();
3092
3093 FatAcquireExclusiveVcb( IrpContext, Vcb );
3094
3095 if (!NT_SUCCESS( Status )) {
3096
3097 return Status;
3098 }
3099
3100 //
3101 // Now rundown the delayed closes one last time. We appear to be able
3102 // to have additional collisions.
3103 //
3104
3105 FatFspClose( Vcb );
3106
3107 //
3108 // Check if the Vcb is already locked, or if the open file count
3109 // is greater than 1 (which implies that someone else also is
3110 // currently using the volume, or a file on the volume), and that the
3111 // VPB reference count only includes our residual and the handle (as
3112 // appropriate).
3113 //
3114 // We used to only check for the vpb refcount. This is unreliable since
3115 // the vpb refcount is dropped immediately before final close, meaning
3116 // that even though we had a good refcount, the close was inflight and
3117 // subsequent operations could get confused. Especially if the PNP path
3118 // was the lock caller, we delete the VCB with an outstanding opencount!
3119 //
3120
3121 IoAcquireVpbSpinLock( &SavedIrql );
3122
3123 if (!FlagOn(Vcb->Vpb->Flags, VPB_LOCKED) &&
3124 (Vcb->Vpb->ReferenceCount <= 2 + RemainingUserReferences) &&
3125 (Vcb->OpenFileCount == (CLONG)( FileObject? 1: 0 ))) {
3126
3127 SetFlag(Vcb->Vpb->Flags, VPB_LOCKED);
3128 SetFlag(Vcb->VcbState, VCB_STATE_FLAG_LOCKED);
3129 Vcb->FileObjectWithVcbLocked = FileObject;
3130
3131 } else {
3132
3133 Status = STATUS_ACCESS_DENIED;
3134 }
3135
3136 IoReleaseVpbSpinLock( SavedIrql );
3137
3138 //
3139 // If we successully locked the volume, see if it is clean now.
3140 //
3141
3142 if (NT_SUCCESS( Status ) &&
3143 FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ) &&
3144 !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ) &&
3145 !CcIsThereDirtyData(Vcb->Vpb)) {
3146
3147 FatMarkVolume( IrpContext, Vcb, VolumeClean );
3148 ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY );
3149 }
3150
3151 ASSERT( !NT_SUCCESS(Status) || (Vcb->OpenFileCount == (CLONG)( FileObject? 1: 0 )));
3152
3153 return Status;
3154 }
3155
3156 \f
3157 NTSTATUS
3158 FatUnlockVolumeInternal (
3159 IN PIRP_CONTEXT IrpContext,
3160 IN PVCB Vcb,
3161 IN PFILE_OBJECT FileObject OPTIONAL
3162 )
3163
3164 /*++
3165
3166 Routine Description:
3167
3168 This routine performs the actual unlock volume operation.
3169
3170 The volume must be held exclusive by the caller.
3171
3172 Arguments:
3173
3174 Vcb - The volume being locked.
3175
3176 FileObject - File corresponding to the handle locking the volume. If this
3177 is not specified, a system lock is assumed.
3178
3179 Return Value:
3180
3181 NTSTATUS - The return status for the operation
3182
3183 Attempting to remove a system lock that did not exist is OK.
3184
3185 --*/
3186
3187 {
3188 KIRQL SavedIrql;
3189 NTSTATUS Status = STATUS_NOT_LOCKED;
3190
3191 IoAcquireVpbSpinLock( &SavedIrql );
3192
3193 if (FlagOn(Vcb->Vpb->Flags, VPB_LOCKED) && FileObject == Vcb->FileObjectWithVcbLocked) {
3194
3195 //
3196 // This one locked it, unlock the volume
3197 //
3198
3199 ClearFlag( Vcb->Vpb->Flags, VPB_LOCKED );
3200 ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_LOCKED );
3201 Vcb->FileObjectWithVcbLocked = NULL;
3202
3203 Status = STATUS_SUCCESS;
3204 }
3205
3206 IoReleaseVpbSpinLock( SavedIrql );
3207
3208 return Status;
3209 }
3210
3211 \f
3212 //
3213 // Local Support Routine
3214 //
3215
3216 NTSTATUS
3217 FatDismountVolume (
3218 IN PIRP_CONTEXT IrpContext,
3219 IN PIRP Irp
3220 )
3221
3222 /*++
3223
3224 Routine Description:
3225
3226 This routine performs the dismount volume operation. It is responsible for
3227 either completing of enqueuing the input Irp.
3228
3229 Arguments:
3230
3231 Irp - Supplies the Irp to process
3232
3233 Return Value:
3234
3235 NTSTATUS - The return status for the operation
3236
3237 --*/
3238
3239 {
3240 PIO_STACK_LOCATION IrpSp;
3241 NTSTATUS Status;
3242 BOOLEAN VcbHeld = FALSE;
3243
3244 PVCB Vcb;
3245 PFCB Fcb;
3246 PCCB Ccb;
3247
3248 IrpSp = IoGetCurrentIrpStackLocation( Irp );
3249
3250 DebugTrace(+1, Dbg, "FatDismountVolume...\n", 0);
3251
3252 //
3253 // Decode the file object, the only type of opens we accept are
3254 // user volume opens on media that is not boot/paging and is not
3255 // already dismounted ... (but we need to check that stuff while
3256 // synchronized)
3257 //
3258
3259 if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
3260
3261 Status = STATUS_INVALID_PARAMETER;
3262 goto fn_return;
3263 }
3264
3265 if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
3266
3267 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
3268
3269 DebugTrace(-1, Dbg, "FatDismountVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
3270 return STATUS_INVALID_PARAMETER;
3271 }
3272
3273 //
3274 // Make some unsynchronized checks to see if this operation is possible.
3275 // We will repeat the appropriate ones inside synchronization, but it is
3276 // good to avoid bogus notifications.
3277 //
3278
3279 if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE )) {
3280
3281 Status = STATUS_ACCESS_DENIED;
3282 goto fn_return;
3283 }
3284
3285 if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) {
3286
3287 Status = STATUS_VOLUME_DISMOUNTED;
3288 goto fn_return;
3289 }
3290
3291 //
3292 // A bit of historical comment is in order.
3293 //
3294 // In all versions prior to NT5, we only permitted dismount if the volume had
3295 // previously been locked. Now we must permit a forced dismount, meaning that
3296 // we grab ahold of the whole kit-n-kaboodle - regardless of activity, open
3297 // handles, etc. - to flush and invalidate the volume.
3298 //
3299 // Previously, dismount assumed that lock had come along earlier and done some
3300 // of the work that we are now going to do - i.e., flush, tear down the eas. All
3301 // we had to do here is flush the device out and kill off as many of the orphan
3302 // fcbs as possible. This now changes.
3303 //
3304 // In fact, everything is a forced dismount now. This changes one interesting
3305 // aspect, which is that it used to be the case that the handle used to dismount
3306 // could come back, read, and induce a verify/remount. This is just not possible
3307 // now. The point of forced dismount is that very shortly someone will come along
3308 // and be destructive to the possibility of using the media further - format, eject,
3309 // etc. By using this path, callers are expected to tolerate the consequences.
3310 //
3311 // Note that the volume can still be successfully unlocked by this handle.
3312 //
3313
3314 //
3315 // Send notification.
3316 //
3317
3318 FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_DISMOUNT );
3319
3320 //
3321 // Force ourselves to wait and grab everything.
3322 //
3323
3324 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
3325 (VOID)FatAcquireExclusiveGlobal( IrpContext );
3326
3327 _SEH2_TRY {
3328
3329 //
3330 // Guess what? This can raise if a cleanup on the fileobject we
3331 // got races in ahead of us.
3332 //
3333
3334 FatAcquireExclusiveVolume( IrpContext, Vcb );
3335 VcbHeld = TRUE;
3336
3337 if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) {
3338
3339 try_return( Status = STATUS_VOLUME_DISMOUNTED );
3340 }
3341
3342 FatFlushAndCleanVolume( IrpContext, Irp, Vcb, FlushAndInvalidate );
3343
3344 //
3345 // We defer the physical dismount until this handle is closed, per symmetric
3346 // implemntation in the other FS. This permits a dismounter to issue IOCTL
3347 // through this handle and perform device manipulation without racing with
3348 // creates attempting to mount the volume again.
3349 //
3350 // Raise a flag to tell the cleanup path to complete the dismount.
3351 //
3352
3353 SetFlag( Ccb->Flags, CCB_FLAG_COMPLETE_DISMOUNT );
3354
3355 //
3356 // Indicate that the volume was dismounted so that we may return the
3357 // correct error code when operations are attempted via open handles.
3358 //
3359
3360 Vcb->VcbCondition = VcbBad;
3361 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED );
3362
3363 Status = STATUS_SUCCESS;
3364
3365 try_exit: NOTHING;
3366
3367 } _SEH2_FINALLY {
3368
3369 if (VcbHeld) {
3370
3371 FatReleaseVolume( IrpContext, Vcb );
3372 }
3373
3374 FatReleaseGlobal( IrpContext );
3375
3376 //
3377 // I do not believe it is possible to raise, but for completeness
3378 // notice and send notification of failure. We absolutely
3379 // cannot have raised in CheckForDismount.
3380 //
3381 // We decline to call an attempt to dismount a dismounted volume
3382 // a failure to do so.
3383 //
3384
3385 if ((!NT_SUCCESS( Status ) && Status != STATUS_VOLUME_DISMOUNTED)
3386 || _SEH2_AbnormalTermination()) {
3387
3388 FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_DISMOUNT_FAILED );
3389 }
3390 } _SEH2_END;
3391
3392 fn_return:
3393
3394 FatCompleteRequest( IrpContext, Irp, Status );
3395 DebugTrace(-1, Dbg, "FatDismountVolume -> %08lx\n", Status);
3396 return Status;
3397 }
3398
3399 \f
3400 //
3401 // Local Support Routine
3402 //
3403
3404 NTSTATUS
3405 FatDirtyVolume (
3406 IN PIRP_CONTEXT IrpContext,
3407 IN PIRP Irp
3408 )
3409
3410 /*++
3411
3412 Routine Description:
3413
3414 This routine marks the volume as dirty.
3415
3416 Arguments:
3417
3418 Irp - Supplies the Irp to process
3419
3420 Return Value:
3421
3422 NTSTATUS - The return status for the operation
3423
3424 --*/
3425
3426 {
3427 PIO_STACK_LOCATION IrpSp;
3428
3429 PVCB Vcb;
3430 PFCB Fcb;
3431 PCCB Ccb;
3432
3433 IrpSp = IoGetCurrentIrpStackLocation( Irp );
3434
3435 DebugTrace(+1, Dbg, "FatDirtyVolume...\n", 0);
3436
3437 //
3438 // Decode the file object, the only type of opens we accept are
3439 // user volume opens.
3440 //
3441
3442 if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
3443
3444 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
3445
3446 DebugTrace(-1, Dbg, "FatDirtyVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
3447 return STATUS_INVALID_PARAMETER;
3448 }
3449
3450 if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
3451
3452 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
3453
3454 DebugTrace(-1, Dbg, "FatDirtyVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
3455 return STATUS_INVALID_PARAMETER;
3456 }
3457
3458
3459 //
3460 // Disable popups, we will just return any error.
3461 //
3462
3463 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS);
3464
3465 //
3466 // Verify the Vcb. We want to make sure we don't dirty some
3467 // random chunk of media that happens to be in the drive now.
3468 //
3469
3470 FatVerifyVcb( IrpContext, Vcb );
3471
3472 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY );
3473
3474 FatMarkVolume( IrpContext, Vcb, VolumeDirty );
3475
3476 FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
3477
3478 DebugTrace(-1, Dbg, "FatDirtyVolume -> STATUS_SUCCESS\n", 0);
3479
3480 return STATUS_SUCCESS;
3481 }
3482
3483 \f
3484 //
3485 // Local Support Routine
3486 //
3487
3488 NTSTATUS
3489 FatIsVolumeDirty (
3490 IN PIRP_CONTEXT IrpContext,
3491 IN PIRP Irp
3492 )
3493
3494 /*++
3495
3496 Routine Description:
3497
3498 This routine determines if a volume is currently dirty.
3499
3500 Arguments:
3501
3502 Irp - Supplies the Irp to process
3503
3504 Return Value:
3505
3506 NTSTATUS - The return status for the operation
3507
3508 --*/
3509
3510 {
3511 PIO_STACK_LOCATION IrpSp;
3512
3513 TYPE_OF_OPEN TypeOfOpen;
3514 PVCB Vcb;
3515 PFCB Fcb;
3516 PCCB Ccb;
3517
3518 PULONG VolumeState;
3519
3520 //
3521 // Get the current stack location and extract the output
3522 // buffer information.
3523 //
3524
3525 IrpSp = IoGetCurrentIrpStackLocation( Irp );
3526
3527 //
3528 // Get a pointer to the output buffer. Look at the system buffer field in the
3529 // irp first. Then the Irp Mdl.
3530 //
3531
3532 if (Irp->AssociatedIrp.SystemBuffer != NULL) {
3533
3534 VolumeState = Irp->AssociatedIrp.SystemBuffer;
3535
3536 } else if (Irp->MdlAddress != NULL) {
3537
3538 VolumeState = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, LowPagePriority );
3539
3540 if (VolumeState == NULL) {
3541
3542 FatCompleteRequest( IrpContext, Irp, STATUS_INSUFFICIENT_RESOURCES );
3543 return STATUS_INSUFFICIENT_RESOURCES;
3544 }
3545
3546 } else {
3547
3548 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_USER_BUFFER );
3549 return STATUS_INVALID_USER_BUFFER;
3550 }
3551
3552 //
3553 // Make sure the output buffer is large enough and then initialize
3554 // the answer to be that the volume isn't dirty.
3555 //
3556
3557 if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG)) {
3558
3559 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
3560 return STATUS_INVALID_PARAMETER;
3561 }
3562
3563 *VolumeState = 0;
3564
3565 //
3566 // Decode the file object
3567 //
3568
3569 TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb );
3570
3571 if (TypeOfOpen != UserVolumeOpen) {
3572
3573 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
3574 return STATUS_INVALID_PARAMETER;
3575 }
3576
3577 if (Vcb->VcbCondition != VcbGood) {
3578
3579 FatCompleteRequest( IrpContext, Irp, STATUS_VOLUME_DISMOUNTED );
3580 return STATUS_VOLUME_DISMOUNTED;
3581 }
3582
3583 //
3584 // Disable PopUps, we want to return any error.
3585 //
3586
3587 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS);
3588
3589 //
3590 // Verify the Vcb. We want to make double sure that this volume
3591 // is around so that we know our information is good.
3592 //
3593
3594 FatVerifyVcb( IrpContext, Vcb );
3595
3596 //
3597 // Now set the returned information. We can avoid probing the disk since
3598 // we know our internal state is in sync.
3599 //
3600
3601 if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY | VCB_STATE_FLAG_MOUNTED_DIRTY) ) {
3602
3603 SetFlag( *VolumeState, VOLUME_IS_DIRTY );
3604 }
3605
3606 Irp->IoStatus.Information = sizeof( ULONG );
3607
3608 FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
3609 return STATUS_SUCCESS;
3610 }
3611
3612 \f
3613 //
3614 // Local Support Routine
3615 //
3616
3617 NTSTATUS
3618 FatIsVolumeMounted (
3619 IN PIRP_CONTEXT IrpContext,
3620 IN PIRP Irp
3621 )
3622
3623 /*++
3624
3625 Routine Description:
3626
3627 This routine determines if a volume is currently mounted.
3628
3629 Arguments:
3630
3631 Irp - Supplies the Irp to process
3632
3633 Return Value:
3634
3635 NTSTATUS - The return status for the operation
3636
3637 --*/
3638
3639 {
3640 NTSTATUS Status;
3641
3642 PIO_STACK_LOCATION IrpSp;
3643
3644 PVCB Vcb = NULL;
3645 PFCB Fcb;
3646 PCCB Ccb;
3647
3648 IrpSp = IoGetCurrentIrpStackLocation( Irp );
3649
3650 Status = STATUS_SUCCESS;
3651
3652 DebugTrace(+1, Dbg, "FatIsVolumeMounted...\n", 0);
3653
3654 //
3655 // Decode the file object.
3656 //
3657
3658 (VOID)FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb );
3659
3660 ASSERT( Vcb != NULL );
3661
3662 //
3663 // Disable PopUps, we want to return any error.
3664 //
3665
3666 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS);
3667
3668 //
3669 // Verify the Vcb.
3670 //
3671
3672 FatVerifyVcb( IrpContext, Vcb );
3673
3674 FatCompleteRequest( IrpContext, Irp, Status );
3675
3676 DebugTrace(-1, Dbg, "FatIsVolumeMounted -> %08lx\n", Status);
3677
3678 return Status;
3679 }
3680
3681 \f
3682 //
3683 // Local Support Routine
3684 //
3685
3686 NTSTATUS
3687 FatIsPathnameValid (
3688 IN PIRP_CONTEXT IrpContext,
3689 IN PIRP Irp
3690 )
3691
3692 /*++
3693
3694 Routine Description:
3695
3696 This routine determines if a pathname is a-priori illegal by inspecting
3697 the the characters used. It is required to be correct on a FALSE return.
3698
3699 N.B.: current implementation is intentioanlly a no-op. This may change
3700 in the future. A careful reader of the previous implementation of this
3701 FSCTL in FAT would discover that it violated the requirement stated above
3702 and could return FALSE for a valid (createable) pathname.
3703
3704 Arguments:
3705
3706 Irp - Supplies the Irp to process
3707
3708 Return Value:
3709
3710 NTSTATUS - The return status for the operation
3711
3712 --*/
3713
3714 {
3715 DebugTrace(+1, Dbg, "FatIsPathnameValid...\n", 0);
3716
3717 FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
3718
3719 DebugTrace(-1, Dbg, "FatIsPathnameValid -> %08lx\n", STATUS_SUCCESS);
3720
3721 return STATUS_SUCCESS;
3722 }
3723
3724 \f
3725 //
3726 // Local Support Routine
3727 //
3728
3729 NTSTATUS
3730 FatQueryBpb (
3731 IN PIRP_CONTEXT IrpContext,
3732 IN PIRP Irp
3733 )
3734
3735 /*++
3736
3737 Routine Description:
3738
3739 This routine simply returns the first 0x24 bytes of sector 0.
3740
3741 Arguments:
3742
3743 Irp - Supplies the Irp to process
3744
3745 Return Value:
3746
3747 NTSTATUS - The return status for the operation
3748
3749 --*/
3750
3751 {
3752 PIO_STACK_LOCATION IrpSp;
3753
3754 PVCB Vcb;
3755
3756 PFSCTL_QUERY_FAT_BPB_BUFFER BpbBuffer;
3757
3758 IrpSp = IoGetCurrentIrpStackLocation( Irp );
3759
3760 DebugTrace(+1, Dbg, "FatQueryBpb...\n", 0);
3761
3762 //
3763 // Get the Vcb. If we didn't keep the information needed for this call,
3764 // we had a reason ...
3765 //
3766
3767 Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb;
3768
3769 if (Vcb->First0x24BytesOfBootSector == NULL) {
3770
3771 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
3772 DebugTrace(-1, Dbg, "FatQueryBpb -> %08lx\n", STATUS_INVALID_DEVICE_REQUEST );
3773 return STATUS_INVALID_DEVICE_REQUEST;
3774 }
3775
3776 //
3777 // Extract the buffer
3778 //
3779
3780 BpbBuffer = (PFSCTL_QUERY_FAT_BPB_BUFFER)Irp->AssociatedIrp.SystemBuffer;
3781
3782 //
3783 // Make sure the buffer is big enough.
3784 //
3785
3786 if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < 0x24) {
3787
3788 FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
3789 DebugTrace(-1, Dbg, "FatQueryBpb -> %08lx\n", STATUS_BUFFER_TOO_SMALL );
3790 return STATUS_BUFFER_TOO_SMALL;
3791 }
3792
3793 //
3794 // Fill in the output buffer
3795 //
3796
3797 RtlCopyMemory( BpbBuffer->First0x24BytesOfBootSector,
3798 Vcb->First0x24BytesOfBootSector,
3799 0x24 );
3800
3801 Irp->IoStatus.Information = 0x24;
3802
3803 FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
3804 DebugTrace(-1, Dbg, "FatQueryBpb -> %08lx\n", STATUS_SUCCESS);
3805 return STATUS_SUCCESS;
3806 }
3807
3808 \f
3809 //
3810 // Local Support Routine
3811 //
3812
3813 NTSTATUS
3814 FatInvalidateVolumes (
3815 IN PIRP Irp
3816 )
3817
3818 /*++
3819
3820 Routine Description:
3821
3822 This routine searches for all the volumes mounted on the same real device
3823 of the current DASD handle, and marks them all bad. The only operation
3824 that can be done on such handles is cleanup and close.
3825
3826 Arguments:
3827
3828 Irp - Supplies the Irp to process
3829
3830 Return Value:
3831
3832 NTSTATUS - The return status for the operation
3833
3834 --*/
3835
3836 {
3837 NTSTATUS Status;
3838 IRP_CONTEXT IrpContext;
3839 PIO_STACK_LOCATION IrpSp;
3840
3841 LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0};
3842
3843 HANDLE Handle;
3844
3845 PLIST_ENTRY Links;
3846
3847 PFILE_OBJECT FileToMarkBad;
3848 PDEVICE_OBJECT DeviceToMarkBad;
3849
3850 IrpSp = IoGetCurrentIrpStackLocation( Irp );
3851
3852 DebugTrace(+1, Dbg, "FatInvalidateVolumes...\n", 0);
3853
3854 //
3855 // Check for the correct security access.
3856 // The caller must have the SeTcbPrivilege.
3857 //
3858
3859 if (!SeSinglePrivilegeCheck(TcbPrivilege, Irp->RequestorMode)) {
3860
3861 FatCompleteRequest( FatNull, Irp, STATUS_PRIVILEGE_NOT_HELD );
3862
3863 DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", STATUS_PRIVILEGE_NOT_HELD);
3864 return STATUS_PRIVILEGE_NOT_HELD;
3865 }
3866
3867 //
3868 // Try to get a pointer to the device object from the handle passed in.
3869 //
3870
3871 #if defined(_WIN64)
3872 if (IoIs32bitProcess( Irp )) {
3873
3874 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(UINT32)) {
3875
3876 FatCompleteRequest( FatNull, Irp, STATUS_INVALID_PARAMETER );
3877
3878 DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", STATUS_INVALID_PARAMETER);
3879 return STATUS_INVALID_PARAMETER;
3880 }
3881
3882 Handle = (HANDLE) LongToHandle( (*(PUINT32)Irp->AssociatedIrp.SystemBuffer) );
3883 } else {
3884 #endif
3885 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE)) {
3886
3887 FatCompleteRequest( FatNull, Irp, STATUS_INVALID_PARAMETER );
3888
3889 DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", STATUS_INVALID_PARAMETER);
3890 return STATUS_INVALID_PARAMETER;
3891 }
3892
3893 Handle = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
3894 #if defined(_WIN64)
3895 }
3896 #endif
3897
3898
3899 Status = ObReferenceObjectByHandle( Handle,
3900 0,
3901 *IoFileObjectType,
3902 KernelMode,
3903 #ifndef __REACTOS__
3904 &FileToMarkBad,
3905 #else
3906 (PVOID *)&FileToMarkBad,
3907 #endif
3908 NULL );
3909
3910 if (!NT_SUCCESS(Status)) {
3911
3912 FatCompleteRequest( FatNull, Irp, Status );
3913
3914 DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", Status);
3915 return Status;
3916
3917 } else {
3918
3919 //
3920 // We only needed the pointer, not a reference.
3921 //
3922
3923 ObDereferenceObject( FileToMarkBad );
3924
3925 //
3926 // Grab the DeviceObject from the FileObject.
3927 //
3928
3929 DeviceToMarkBad = FileToMarkBad->DeviceObject;
3930 }
3931
3932 RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );
3933
3934 SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT );
3935 IrpContext.MajorFunction = IrpSp->MajorFunction;
3936 IrpContext.MinorFunction = IrpSp->MinorFunction;
3937
3938 FatAcquireExclusiveGlobal( &IrpContext );
3939
3940 //
3941 // First acquire the FatData resource shared, then walk through all the
3942 // mounted VCBs looking for candidates to mark BAD.
3943 //
3944 // On volumes we mark bad, check for dismount possibility (which is
3945 // why we have to get the next link early).
3946 //
3947
3948 Links = FatData.VcbQueue.Flink;
3949
3950 while (Links != &FatData.VcbQueue) {
3951
3952 PVCB ExistingVcb;
3953
3954 ExistingVcb = CONTAINING_RECORD(Links, VCB, VcbLinks);
3955
3956 Links = Links->Flink;
3957
3958 //
3959 // If we get a match, mark the volume Bad, and also check to
3960 // see if the volume should go away.
3961 //
3962
3963 if (ExistingVcb->Vpb->RealDevice == DeviceToMarkBad) {
3964
3965 BOOLEAN VcbDeleted;
3966
3967 VcbDeleted = FALSE;
3968
3969 //
3970 // Here we acquire the Vcb exclusive and try to purge
3971 // all the open files. The idea is to have as little as
3972 // possible stale data visible and to hasten the volume
3973 // going away.
3974 //
3975
3976 (VOID)FatAcquireExclusiveVcb( &IrpContext, ExistingVcb );
3977
3978 if (ExistingVcb->Vpb == DeviceToMarkBad->Vpb) {
3979
3980 KIRQL OldIrql;
3981
3982 IoAcquireVpbSpinLock( &OldIrql );
3983
3984 if (FlagOn( DeviceToMarkBad->Vpb->Flags, VPB_MOUNTED )) {
3985
3986 PVPB NewVpb;
3987
3988 NewVpb = ExistingVcb->SwapVpb;
3989 ExistingVcb->SwapVpb = NULL;
3990 SetFlag( ExistingVcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED );
3991
3992 RtlZeroMemory( NewVpb, sizeof( VPB ) );
3993 NewVpb->Type = IO_TYPE_VPB;
3994 NewVpb->Size = sizeof( VPB );
3995 NewVpb->RealDevice = DeviceToMarkBad;
3996 NewVpb->Flags = FlagOn( DeviceToMarkBad->Vpb->Flags, VPB_REMOVE_PENDING );
3997
3998 DeviceToMarkBad->Vpb = NewVpb;
3999 }
4000
4001 ASSERT( DeviceToMarkBad->Vpb->DeviceObject == NULL );
4002
4003 IoReleaseVpbSpinLock( OldIrql );
4004 }
4005
4006 FatSetVcbCondition( ExistingVcb, VcbBad );
4007
4008 //
4009 // Process the root directory, if it is present.
4010 //
4011
4012 if (ExistingVcb->RootDcb != NULL) {
4013
4014 FatMarkFcbCondition( &IrpContext, ExistingVcb->RootDcb, FcbBad, TRUE );
4015
4016 //
4017 // Purging the file objects on this volume could result in the memory manager
4018 // dereferencing it's file pointer which could be the last reference and
4019 // trigger object deletion and VCB deletion. Protect against that here by
4020 // temporarily biasing the file count, and later checking for dismount.
4021 //
4022
4023 ExistingVcb->OpenFileCount += 1;
4024
4025 FatPurgeReferencedFileObjects( &IrpContext,
4026 ExistingVcb->RootDcb,
4027 NoFlush );
4028
4029 ExistingVcb->OpenFileCount -= 1;
4030
4031 VcbDeleted = FatCheckForDismount( &IrpContext, ExistingVcb, FALSE );
4032 }
4033
4034 //
4035 // Drop the resource.
4036 //
4037
4038 if (VcbDeleted != TRUE) {
4039 FatReleaseVcb( &IrpContext, ExistingVcb );
4040 }
4041 }
4042 }
4043
4044 FatReleaseGlobal( &IrpContext );
4045
4046 FatCompleteRequest( FatNull, Irp, STATUS_SUCCESS );
4047
4048 DebugTrace(-1, Dbg, "FatInvalidateVolumes -> STATUS_SUCCESS\n", 0);
4049
4050 return STATUS_SUCCESS;
4051 }
4052
4053 \f
4054 //
4055 // Local Support routine
4056 //
4057
4058 BOOLEAN
4059 FatPerformVerifyDiskRead (
4060 IN PIRP_CONTEXT IrpContext,
4061 IN PVCB Vcb,
4062 IN PVOID Buffer,
4063 IN LBO Lbo,
4064 IN ULONG NumberOfBytesToRead,
4065 IN BOOLEAN ReturnOnError
4066 )
4067
4068 /*++
4069
4070 Routine Description:
4071
4072 This routine is used to read in a range of bytes from the disk. It
4073 bypasses all of the caching and regular I/O logic, and builds and issues
4074 the requests itself. It does this operation overriding the verify
4075 volume flag in the device object.
4076
4077 Arguments:
4078
4079 Vcb - Supplies the target device object for this operation.
4080
4081 Buffer - Supplies the buffer that will recieve the results of this operation
4082
4083 Lbo - Supplies the byte offset of where to start reading
4084
4085 NumberOfBytesToRead - Supplies the number of bytes to read, this must
4086 be in multiple of bytes units acceptable to the disk driver.
4087
4088 ReturnOnError - Indicates that we should return on an error, instead
4089 of raising.
4090
4091 Return Value:
4092
4093 BOOLEAN - TRUE if the operation succeded, FALSE otherwise.
4094
4095 --*/
4096
4097 {
4098 KEVENT Event;
4099 PIRP Irp;
4100 LARGE_INTEGER ByteOffset;
4101 NTSTATUS Status;
4102 IO_STATUS_BLOCK Iosb;
4103
4104 DebugTrace(0, Dbg, "FatPerformVerifyDiskRead, Lbo = %08lx\n", Lbo );
4105
4106 //
4107 // Initialize the event we're going to use
4108 //
4109
4110 KeInitializeEvent( &Event, NotificationEvent, FALSE );
4111
4112 //
4113 // Build the irp for the operation and also set the overrride flag
4114 //
4115
4116 ByteOffset.QuadPart = Lbo;
4117
4118 Irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ,
4119 Vcb->TargetDeviceObject,
4120 Buffer,
4121 NumberOfBytesToRead,
4122 &ByteOffset,
4123 &Event,
4124 &Iosb );
4125
4126 if ( Irp == NULL ) {
4127
4128 FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
4129 }
4130
4131 SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME );
4132
4133 //
4134 // Call the device to do the read and wait for it to finish.
4135 //
4136
4137 Status = IoCallDriver( Vcb->TargetDeviceObject, Irp );
4138
4139 if (Status == STATUS_PENDING) {
4140
4141 (VOID)KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL );
4142
4143 Status = Iosb.Status;
4144 }
4145
4146 ASSERT( Status != STATUS_VERIFY_REQUIRED );
4147
4148 //
4149 // Special case this error code because this probably means we used
4150 // the wrong sector size and we want to reject STATUS_WRONG_VOLUME.
4151 //
4152
4153 if (Status == STATUS_INVALID_PARAMETER) {
4154
4155 return FALSE;
4156 }
4157
4158 //
4159 // If it doesn't succeed then either return or raise the error.
4160 //
4161
4162 if (!NT_SUCCESS(Status)) {
4163
4164 if (ReturnOnError) {
4165
4166 return FALSE;
4167
4168 } else {
4169
4170 FatNormalizeAndRaiseStatus( IrpContext, Status );
4171 }
4172 }
4173
4174 //
4175 // And return to our caller
4176 //
4177
4178 return TRUE;
4179 }
4180
4181 \f
4182 //
4183 // Local Support Routine
4184 //
4185
4186 NTSTATUS
4187 FatQueryRetrievalPointers (
4188 IN PIRP_CONTEXT IrpContext,
4189 IN PIRP Irp
4190 )
4191
4192 /*++
4193
4194 Routine Description:
4195
4196 This routine performs the query retrieval pointers operation.
4197 It returns the retrieval pointers for the specified input
4198 file from the start of the file to the request map size specified
4199 in the input buffer.
4200
4201 Arguments:
4202
4203 Irp - Supplies the Irp to process
4204
4205 Return Value:
4206
4207 NTSTATUS - The return status for the operation
4208
4209 --*/
4210
4211 {
4212 NTSTATUS Status;
4213
4214 PIO_STACK_LOCATION IrpSp;
4215
4216 PVCB Vcb;
4217 PFCB Fcb;
4218 PCCB Ccb;
4219
4220 PLARGE_INTEGER RequestedMapSize;
4221 PLARGE_INTEGER *MappingPairs;
4222
4223 ULONG Index;
4224 ULONG i;
4225 ULONG SectorCount;
4226 LBO Lbo;
4227 ULONG Vbo;
4228 ULONG MapSize;
4229
4230 //
4231 // Get the current stack location
4232 //
4233
4234 IrpSp = IoGetCurrentIrpStackLocation( Irp );
4235
4236 //
4237 // Decode the file object and ensure that it is the paging file
4238 //
4239 // Only Kernel mode clients may query retrieval pointer information about
4240 // a file. Ensure that this is the case for this caller.
4241 //
4242
4243 (VOID)FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb );
4244
4245 if (Irp->RequestorMode != KernelMode ||
4246 Fcb == NULL ||
4247 !FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) ) {
4248
4249 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
4250 return STATUS_INVALID_PARAMETER;
4251 }
4252
4253 //
4254 // Extract the input and output buffer information. The input contains
4255 // the requested size of the mappings in terms of VBO. The output
4256 // parameter will receive a pointer to nonpaged pool where the mapping
4257 // pairs are stored.
4258 //
4259
4260 ASSERT( IrpSp->Parameters.FileSystemControl.InputBufferLength == sizeof(LARGE_INTEGER) );
4261 ASSERT( IrpSp->Parameters.FileSystemControl.OutputBufferLength == sizeof(PVOID) );
4262
4263 RequestedMapSize = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
4264 MappingPairs = Irp->UserBuffer;
4265
4266 //
4267 // Acquire exclusive access to the Fcb
4268 //
4269
4270 if (!FatAcquireExclusiveFcb( IrpContext, Fcb )) {
4271
4272 return FatFsdPostRequest( IrpContext, Irp );
4273 }
4274
4275 _SEH2_TRY {
4276
4277 //
4278 // Verify the Fcb is still OK
4279 //
4280
4281 FatVerifyFcb( IrpContext, Fcb );
4282
4283 //
4284 // Check if the mapping the caller requested is too large
4285 //
4286
4287 if ((*RequestedMapSize).QuadPart > Fcb->Header.FileSize.QuadPart) {
4288
4289 try_return( Status = STATUS_INVALID_PARAMETER );
4290 }
4291
4292 //
4293 // Now get the index for the mcb entry that will contain the
4294 // callers request and allocate enough pool to hold the
4295 // output mapping pairs
4296 //
4297
4298 (VOID)FatLookupMcbEntry( Fcb->Vcb, &Fcb->Mcb, RequestedMapSize->LowPart - 1, &Lbo, NULL, &Index );
4299
4300 *MappingPairs = FsRtlAllocatePoolWithTag( NonPagedPool,
4301 (Index + 2) * (2 * sizeof(LARGE_INTEGER)),
4302 TAG_OUTPUT_MAPPINGPAIRS );
4303
4304 //
4305 // Now copy over the mapping pairs from the mcb
4306 // to the output buffer. We store in [sector count, lbo]
4307 // mapping pairs and end with a zero sector count.
4308 //
4309
4310 MapSize = RequestedMapSize->LowPart;
4311
4312 for (i = 0; i <= Index; i += 1) {
4313
4314 #ifndef __REACTOS__
4315 (VOID)FatGetNextMcbEntry( Fcb->Vcb, &Fcb->Mcb, i, &Vbo, &Lbo, &SectorCount );
4316 #else
4317 (VOID)FatGetNextMcbEntry( Fcb->Vcb, &Fcb->Mcb, i, (PVBO)&Vbo, &Lbo, &SectorCount );
4318 #endif
4319
4320 if (SectorCount > MapSize) {
4321 SectorCount = MapSize;
4322 }
4323
4324 (*MappingPairs)[ i*2 + 0 ].QuadPart = SectorCount;
4325 (*MappingPairs)[ i*2 + 1 ].QuadPart = Lbo;
4326
4327 MapSize -= SectorCount;
4328 }
4329
4330 (*MappingPairs)[ i*2 + 0 ].QuadPart = 0;
4331
4332 Status = STATUS_SUCCESS;
4333
4334 try_exit: NOTHING;
4335 } _SEH2_FINALLY {
4336
4337 DebugUnwind( FatQueryRetrievalPointers );
4338
4339 //
4340 // Release all of our resources
4341 //
4342
4343 FatReleaseFcb( IrpContext, Fcb );
4344
4345 //
4346 // If this is an abnormal termination then undo our work, otherwise
4347 // complete the irp
4348 //
4349
4350 if (!_SEH2_AbnormalTermination()) {
4351
4352 FatCompleteRequest( IrpContext, Irp, Status );
4353 }
4354 } _SEH2_END;
4355
4356 return Status;
4357 }
4358
4359 \f
4360 //
4361 // Local Support Routine
4362 //
4363
4364 NTSTATUS
4365 FatGetStatistics (
4366 IN PIRP_CONTEXT IrpContext,
4367 IN PIRP Irp
4368 )
4369
4370 /*++
4371
4372 Routine Description:
4373
4374 This routine returns the filesystem performance counters from the
4375 appropriate VCB.
4376
4377 Arguments:
4378
4379 Irp - Supplies the Irp to process
4380
4381 Return Value:
4382
4383 NTSTATUS - The return status for the operation
4384
4385 --*/
4386
4387 {
4388 PIO_STACK_LOCATION IrpSp;
4389 NTSTATUS Status;
4390 PVCB Vcb;
4391
4392 PFILE_SYSTEM_STATISTICS Buffer;
4393 ULONG BufferLength;
4394 ULONG StatsSize;
4395 ULONG BytesToCopy;
4396
4397 IrpSp = IoGetCurrentIrpStackLocation( Irp );
4398
4399 DebugTrace(+1, Dbg, "FatGetStatistics...\n", 0);
4400
4401 //
4402 // Extract the buffer
4403 //
4404
4405 Buffer = Irp->AssociatedIrp.SystemBuffer;
4406 BufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
4407
4408 //
4409 // Get a pointer to the output buffer.
4410 //
4411
4412 Buffer = Irp->AssociatedIrp.SystemBuffer;
4413
4414 //
4415 // Make sure the buffer is big enough for at least the common part.
4416 //
4417
4418 if (BufferLength < sizeof(FILESYSTEM_STATISTICS)) {
4419
4420 FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
4421
4422 DebugTrace(-1, Dbg, "FatGetStatistics -> %08lx\n", STATUS_BUFFER_TOO_SMALL );
4423
4424 return STATUS_BUFFER_TOO_SMALL;
4425 }
4426
4427 //
4428 // Now see how many bytes we can copy.
4429 //
4430
4431 StatsSize = sizeof(FILE_SYSTEM_STATISTICS) * KeNumberProcessors;
4432
4433 if (BufferLength < StatsSize) {
4434
4435 BytesToCopy = BufferLength;
4436 Status = STATUS_BUFFER_OVERFLOW;
4437
4438 } else {
4439
4440 BytesToCopy = StatsSize;
4441 Status = STATUS_SUCCESS;
4442 }
4443
4444 //
4445 // Get the Vcb.
4446 //
4447
4448 Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb;
4449
4450 //
4451 // Fill in the output buffer
4452 //
4453
4454 RtlCopyMemory( Buffer, Vcb->Statistics, BytesToCopy );
4455
4456 Irp->IoStatus.Information = BytesToCopy;
4457
4458 FatCompleteRequest( IrpContext, Irp, Status );
4459
4460 DebugTrace(-1, Dbg, "FatGetStatistics -> %08lx\n", Status);
4461
4462 return Status;
4463 }
4464 \f
4465 //
4466 // Local Support Routine
4467 //
4468
4469 NTSTATUS
4470 FatGetVolumeBitmap(
4471 IN PIRP_CONTEXT IrpContext,
4472 IN PIRP Irp
4473 )
4474
4475 /*++
4476
4477 Routine Description:
4478
4479 This routine returns the volume allocation bitmap.
4480
4481 Input = the STARTING_LCN_INPUT_BUFFER data structure is passed in
4482 through the input buffer.
4483 Output = the VOLUME_BITMAP_BUFFER data structure is returned through
4484 the output buffer.
4485
4486 We return as much as the user buffer allows starting the specified input
4487 LCN (trucated to a byte). If there is no input buffer, we start at zero.
4488
4489 Arguments:
4490
4491 Irp - Supplies the Irp being processed.
4492
4493 Return Value:
4494
4495 NTSTATUS - The return status for the operation.
4496
4497 --*/
4498 {
4499 NTSTATUS Status;
4500 PIO_STACK_LOCATION IrpSp;
4501
4502 PVCB Vcb;
4503 PFCB Fcb;
4504 PCCB Ccb;
4505
4506 ULONG BytesToCopy;
4507 ULONG TotalClusters;
4508 ULONG DesiredClusters;
4509 ULONG StartingCluster;
4510 ULONG InputBufferLength;
4511 ULONG OutputBufferLength;
4512 LARGE_INTEGER StartingLcn;
4513 PVOLUME_BITMAP_BUFFER OutputBuffer;
4514
4515 //
4516 // Get the current Irp stack location and save some references.
4517 //
4518
4519 IrpSp = IoGetCurrentIrpStackLocation( Irp );
4520
4521 DebugTrace(+1, Dbg, "FatGetVolumeBitmap, FsControlCode = %08lx\n",
4522 IrpSp->Parameters.FileSystemControl.FsControlCode);
4523
4524 //
4525 // Extract and decode the file object and check for type of open.
4526 //
4527
4528 if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
4529
4530 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
4531 return STATUS_INVALID_PARAMETER;
4532 }
4533
4534 if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
4535
4536 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
4537
4538 DebugTrace(-1, Dbg, "FatGetVolumeBitmap -> %08lx\n", STATUS_INVALID_PARAMETER);
4539 return STATUS_INVALID_PARAMETER;
4540 }
4541
4542 InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
4543 OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
4544
4545 OutputBuffer = (PVOLUME_BITMAP_BUFFER)FatMapUserBuffer( IrpContext, Irp );
4546
4547 //
4548 // Check for a minimum length on the input and output buffers.
4549 //
4550
4551 if ((InputBufferLength < sizeof(STARTING_LCN_INPUT_BUFFER)) ||
4552 (OutputBufferLength < sizeof(VOLUME_BITMAP_BUFFER))) {
4553
4554 FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
4555 return STATUS_BUFFER_TOO_SMALL;
4556 }
4557
4558 //
4559 // Check if a starting cluster was specified.
4560 //
4561
4562 TotalClusters = Vcb->AllocationSupport.NumberOfClusters;
4563
4564 //
4565 // Check for valid buffers
4566 //
4567
4568 _SEH2_TRY {
4569
4570 if (Irp->RequestorMode != KernelMode) {
4571
4572 ProbeForRead( IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
4573 InputBufferLength,
4574 sizeof(UCHAR) );
4575
4576 ProbeForWrite( OutputBuffer, OutputBufferLength, sizeof(UCHAR) );
4577 }
4578
4579 StartingLcn = ((PSTARTING_LCN_INPUT_BUFFER)IrpSp->Parameters.FileSystemControl.Type3InputBuffer)->StartingLcn;
4580
4581 } _SEH2_EXCEPT( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) {
4582
4583 Status = _SEH2_GetExceptionCode();
4584
4585 FatRaiseStatus( IrpContext,
4586 FsRtlIsNtstatusExpected(Status) ?
4587 Status : STATUS_INVALID_USER_BUFFER );
4588 } _SEH2_END;
4589
4590 if (StartingLcn.HighPart || StartingLcn.LowPart >= TotalClusters) {
4591
4592 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
4593 return STATUS_INVALID_PARAMETER;
4594
4595 } else {
4596
4597 StartingCluster = StartingLcn.LowPart & ~7;
4598 }
4599
4600 //
4601 // Acquire exclusive access to the Vcb and enqueue the Irp if we
4602 // didn't get access.
4603 //
4604
4605 if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {
4606
4607 DebugTrace( 0, Dbg, "Cannot acquire Vcb\n", 0);
4608
4609 ASSERT( Irp->RequestorMode == KernelMode );
4610
4611 Status = FatFsdPostRequest( IrpContext, Irp );
4612
4613 DebugTrace(-1, Dbg, "FatGetVolumeBitmap -> %08lx\n", Status);
4614 return Status;
4615 }
4616
4617 //
4618 // Only return what will fit in the user buffer.
4619 //
4620
4621 OutputBufferLength -= FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer);
4622 DesiredClusters = TotalClusters - StartingCluster;
4623
4624 if (OutputBufferLength < (DesiredClusters + 7) / 8) {
4625
4626 BytesToCopy = OutputBufferLength;
4627 Status = STATUS_BUFFER_OVERFLOW;
4628
4629 } else {
4630
4631 BytesToCopy = (DesiredClusters + 7) / 8;
4632 Status = STATUS_SUCCESS;
4633 }
4634
4635 //
4636 // Use try/finally for cleanup.
4637 //
4638
4639 _SEH2_TRY {
4640
4641 _SEH2_TRY {
4642
4643 //
4644 // Verify the Vcb is still OK
4645 //
4646
4647 FatQuickVerifyVcb( IrpContext, Vcb );
4648
4649 //
4650 // Fill in the fixed part of the output buffer
4651 //
4652
4653 OutputBuffer->StartingLcn.QuadPart = StartingCluster;
4654 OutputBuffer->BitmapSize.QuadPart = DesiredClusters;
4655
4656 if (Vcb->NumberOfWindows == 1) {
4657
4658 //
4659 // Just copy the volume bitmap into the user buffer.
4660 //
4661
4662 ASSERT( Vcb->FreeClusterBitMap.Buffer != NULL );
4663
4664 RtlCopyMemory( &OutputBuffer->Buffer[0],
4665 (PUCHAR)Vcb->FreeClusterBitMap.Buffer + StartingCluster/8,
4666 BytesToCopy );
4667 } else {
4668
4669 //
4670 // Call out to analyze the FAT. We must bias by two to account for
4671 // the zero base of this API and FAT's physical reality of starting
4672 // the file heap at cluster 2.
4673 //
4674 // Note that the end index is inclusive - we need to subtract one to
4675 // calculcate it.
4676 //
4677 // I.e.: StartingCluster 0 for one byte of bitmap means a start cluster
4678 // of 2 and end cluster of 9, a run of eight clusters.
4679 //
4680
4681 FatExamineFatEntries( IrpContext,
4682 Vcb,
4683 StartingCluster + 2,
4684 StartingCluster + BytesToCopy * 8 + 2 - 1,
4685 FALSE,
4686 NULL,
4687 (PULONG)&OutputBuffer->Buffer[0] );
4688 }
4689
4690 } _SEH2_EXCEPT( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) {
4691
4692 Status = _SEH2_GetExceptionCode();
4693
4694 FatRaiseStatus( IrpContext,
4695 FsRtlIsNtstatusExpected(Status) ?
4696 Status : STATUS_INVALID_USER_BUFFER );
4697 } _SEH2_END;
4698
4699 } _SEH2_FINALLY {
4700
4701 FatReleaseVcb( IrpContext, Vcb );
4702 } _SEH2_END;
4703
4704 Irp->IoStatus.Information = FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer) +
4705 BytesToCopy;
4706
4707 FatCompleteRequest( IrpContext, Irp, Status );
4708
4709 DebugTrace(-1, Dbg, "FatGetVolumeBitmap -> VOID\n", 0);
4710
4711 return Status;
4712 }
4713
4714 \f
4715 //
4716 // Local Support Routine
4717 //
4718
4719 NTSTATUS
4720 FatGetRetrievalPointers (
4721 IN PIRP_CONTEXT IrpContext,
4722 IN PIRP Irp
4723 )
4724
4725 /*++
4726
4727 Routine Description:
4728
4729 This routine scans the MCB and builds an extent list. The first run in
4730 the output extent list will start at the begining of the contiguous
4731 run specified by the input parameter.
4732
4733 Input = STARTING_VCN_INPUT_BUFFER;
4734 Output = RETRIEVAL_POINTERS_BUFFER.
4735
4736 Arguments:
4737
4738 Irp - Supplies the Irp being processed.
4739
4740 Return Value:
4741
4742 NTSTATUS - The return status for the operation.
4743
4744 --*/
4745 {
4746 NTSTATUS Status;
4747 PIO_STACK_LOCATION IrpSp;
4748
4749 PVCB Vcb;
4750 PFCB FcbOrDcb;
4751 PCCB Ccb;
4752 TYPE_OF_OPEN TypeOfOpen;
4753
4754 ULONG Index;
4755 ULONG ClusterShift;
4756 ULONG AllocationSize;
4757
4758 ULONG Run;
4759 ULONG RunCount;
4760 ULONG StartingRun;
4761 LARGE_INTEGER StartingVcn;
4762
4763 ULONG InputBufferLength;
4764 ULONG OutputBufferLength;
4765
4766 PRETRIEVAL_POINTERS_BUFFER OutputBuffer;
4767
4768 BOOLEAN FcbLocked;
4769
4770 //
4771 // Get the current Irp stack location and save some references.
4772 //
4773
4774 IrpSp = IoGetCurrentIrpStackLocation( Irp );
4775
4776 DebugTrace(+1, Dbg, "FatGetRetrievalPointers, FsControlCode = %08lx\n",
4777 IrpSp->Parameters.FileSystemControl.FsControlCode);
4778
4779 //
4780 // Extract and decode the file object and check for type of open.
4781 //
4782
4783 TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &FcbOrDcb, &Ccb );
4784
4785 if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen)) {
4786
4787 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
4788 return STATUS_INVALID_PARAMETER;
4789 }
4790
4791 //
4792 // Get the input and output buffer lengths and pointers.
4793 // Initialize some variables.
4794 //
4795
4796 InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
4797 OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
4798
4799 OutputBuffer = (PRETRIEVAL_POINTERS_BUFFER)FatMapUserBuffer( IrpContext, Irp );
4800
4801 //
4802 // Check for a minimum length on the input and ouput buffers.
4803 //
4804
4805 if ((InputBufferLength < sizeof(STARTING_VCN_INPUT_BUFFER)) ||
4806 (OutputBufferLength < sizeof(RETRIEVAL_POINTERS_BUFFER))) {
4807
4808 FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
4809 return STATUS_BUFFER_TOO_SMALL;
4810 }
4811
4812 //
4813 // Acquire the Fcb and enqueue the Irp if we didn't get access. Go for
4814 // shared on read-only media so we can allow prototype XIP to get
4815 // recursive, as well as recognizing this is safe anyway.
4816 //
4817
4818 if (FlagOn( FcbOrDcb->Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED )) {
4819
4820 FcbLocked = FatAcquireSharedFcb( IrpContext, FcbOrDcb );
4821
4822 } else {
4823
4824 FcbLocked = FatAcquireExclusiveFcb( IrpContext, FcbOrDcb );
4825 }
4826
4827 if (!FcbLocked) {
4828
4829 DebugTrace( 0, Dbg, "Cannot acquire Vcb\n", 0);
4830
4831 ASSERT( Irp->RequestorMode == KernelMode );
4832
4833 Status = FatFsdPostRequest( IrpContext, Irp );
4834
4835 DebugTrace(-1, Dbg, "FatGetRetrievalPointers -> %08lx\n", Status);
4836 return Status;
4837 }
4838
4839 _SEH2_TRY {
4840
4841 //
4842 // Verify the Fcb is still OK
4843 //
4844
4845 FatVerifyFcb( IrpContext, FcbOrDcb );
4846
4847 //
4848 // If we haven't yet set the correct AllocationSize, do so.
4849 //
4850
4851 if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
4852
4853 FatLookupFileAllocationSize( IrpContext, FcbOrDcb );
4854
4855 //
4856 // If this is a non-root directory, we have a bit more to
4857 // do since it has not gone through FatOpenDirectoryFile().
4858 //
4859
4860 if (NodeType(FcbOrDcb) == FAT_NTC_DCB ||
4861 (NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB && FatIsFat32(Vcb))) {
4862
4863 FcbOrDcb->Header.FileSize.LowPart =
4864 FcbOrDcb->Header.AllocationSize.LowPart;
4865 }
4866 }
4867
4868 //
4869 // Check if a starting cluster was specified.
4870 //
4871
4872 ClusterShift = Vcb->AllocationSupport.LogOfBytesPerCluster;
4873 AllocationSize = FcbOrDcb->Header.AllocationSize.LowPart;
4874
4875 _SEH2_TRY {
4876
4877 if (Irp->RequestorMode != KernelMode) {
4878
4879 ProbeForRead( IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
4880 InputBufferLength,
4881 sizeof(UCHAR) );
4882
4883 ProbeForWrite( OutputBuffer, OutputBufferLength, sizeof(UCHAR) );
4884 }
4885
4886 StartingVcn = ((PSTARTING_VCN_INPUT_BUFFER)IrpSp->Parameters.FileSystemControl.Type3InputBuffer)->StartingVcn;
4887
4888 } _SEH2_EXCEPT( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) {
4889
4890 Status = _SEH2_GetExceptionCode();
4891
4892 FatRaiseStatus( IrpContext,
4893 FsRtlIsNtstatusExpected(Status) ?
4894 Status : STATUS_INVALID_USER_BUFFER );
4895 } _SEH2_END;
4896
4897 if (StartingVcn.HighPart ||
4898 StartingVcn.LowPart >= (AllocationSize >> ClusterShift)) {
4899
4900 try_return( Status = STATUS_END_OF_FILE );
4901
4902 } else {
4903
4904 //
4905 // If we don't find the run, something is very wrong.
4906 //
4907
4908 LBO Lbo;
4909
4910 if (!FatLookupMcbEntry( FcbOrDcb->Vcb, &FcbOrDcb->Mcb,
4911 StartingVcn.LowPart << ClusterShift,
4912 &Lbo,
4913 NULL,
4914 &StartingRun)) {
4915
4916 FatBugCheck( (ULONG_PTR)FcbOrDcb, (ULONG_PTR)&FcbOrDcb->Mcb, StartingVcn.LowPart );
4917 }
4918 }
4919
4920 //
4921 // Now go fill in the ouput buffer with run information
4922 //
4923
4924 RunCount = FsRtlNumberOfRunsInLargeMcb( &FcbOrDcb->Mcb );
4925
4926 for (Index = 0, Run = StartingRun; Run < RunCount; Index++, Run++) {
4927
4928 ULONG Vcn;
4929 LBO Lbo;
4930 ULONG ByteLength;
4931
4932 //
4933 // Check for an exhausted output buffer.
4934 //
4935
4936 if ((ULONG)FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[Index+1]) > OutputBufferLength) {
4937
4938
4939 //
4940 // We've run out of space, so we won't be storing as many runs to the
4941 // user's buffer as we had originally planned. We need to return the
4942 // number of runs that we did have room for.
4943 //
4944
4945 _SEH2_TRY {
4946
4947 OutputBuffer->ExtentCount = Index;
4948
4949 } _SEH2_EXCEPT( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) {
4950
4951 Status = _SEH2_GetExceptionCode();
4952
4953 FatRaiseStatus( IrpContext,
4954 FsRtlIsNtstatusExpected(Status) ?
4955 Status : STATUS_INVALID_USER_BUFFER );
4956 } _SEH2_END;
4957
4958 Irp->IoStatus.Information = FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[Index]);
4959 try_return( Status = STATUS_BUFFER_OVERFLOW );
4960 }
4961
4962 //
4963 // Get the extent. If it's not there or malformed, something is very wrong.
4964 //
4965
4966 #ifndef __REACTOS__
4967 if (!FatGetNextMcbEntry(Vcb, &FcbOrDcb->Mcb, Run, &Vcn, &Lbo, &ByteLength)) {
4968 #else
4969 if (!FatGetNextMcbEntry(Vcb, &FcbOrDcb->Mcb, Run, (PVBO)&Vcn, &Lbo, &ByteLength)) {
4970 #endif
4971 FatBugCheck( (ULONG_PTR)FcbOrDcb, (ULONG_PTR)&FcbOrDcb->Mcb, Run );
4972 }
4973
4974 //
4975 // Fill in the next array element.
4976 //
4977
4978 _SEH2_TRY {
4979
4980 OutputBuffer->Extents[Index].NextVcn.QuadPart = (Vcn + ByteLength) >> ClusterShift;
4981 OutputBuffer->Extents[Index].Lcn.QuadPart = FatGetIndexFromLbo( Vcb, Lbo ) - 2;
4982
4983 //
4984 // If this is the first run, fill in the starting Vcn
4985 //
4986
4987 if (Index == 0) {
4988 OutputBuffer->ExtentCount = RunCount - StartingRun;
4989 OutputBuffer->StartingVcn.QuadPart = Vcn >> ClusterShift;
4990 }
4991
4992 } _SEH2_EXCEPT( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) {
4993
4994 Status = _SEH2_GetExceptionCode();
4995
4996 FatRaiseStatus( IrpContext,
4997 FsRtlIsNtstatusExpected(Status) ?
4998 Status : STATUS_INVALID_USER_BUFFER );
4999 } _SEH2_END;
5000 }
5001
5002 //
5003 // We successfully retrieved extent info to the end of the allocation.
5004 //
5005
5006 Irp->IoStatus.Information = FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[Index]);
5007 Status = STATUS_SUCCESS;
5008
5009 try_exit: NOTHING;
5010
5011 } _SEH2_FINALLY {
5012
5013 DebugUnwind( FatGetRetrievalPointers );
5014
5015 //
5016 // Release resources
5017 //
5018
5019 FatReleaseFcb( IrpContext, FcbOrDcb );
5020
5021 //
5022 // If nothing raised then complete the irp.
5023 //
5024
5025 if (!_SEH2_AbnormalTermination()) {
5026
5027 FatCompleteRequest( IrpContext, Irp, Status );
5028 }
5029
5030 DebugTrace(-1, Dbg, "FatGetRetrievalPointers -> VOID\n", 0);
5031 } _SEH2_END;
5032
5033 return Status;
5034 }
5035
5036 \f
5037 //
5038 // Local Support Routine
5039 //
5040
5041 NTSTATUS
5042 FatMoveFile (
5043 IN PIRP_CONTEXT IrpContext,
5044 IN PIRP Irp
5045 )
5046
5047 /*++
5048
5049 Routine Description:
5050
5051 Routine moves a file to the requested Starting Lcn from Starting Vcn for the length
5052 of cluster count. These values are passed in through the the input buffer as a
5053 MOVE_DATA structure.
5054
5055 The call must be made with a DASD handle. The file to move is passed in as a
5056 parameter.
5057
5058 Arguments:
5059
5060 Irp - Supplies the Irp being processed.
5061
5062 Return Value:
5063
5064 NTSTATUS - The return status for the operation.
5065
5066 --*/
5067
5068 {
5069 NTSTATUS Status;
5070 PIO_STACK_LOCATION IrpSp;
5071
5072 PFILE_OBJECT FileObject;
5073 TYPE_OF_OPEN TypeOfOpen;
5074 PVCB Vcb;
5075 PFCB FcbOrDcb;
5076 PCCB Ccb;
5077
5078 ULONG InputBufferLength;
5079 PMOVE_FILE_DATA InputBuffer;
5080
5081 ULONG ClusterShift;
5082 ULONG MaxClusters;
5083
5084 ULONG FileOffset;
5085
5086 LBO TargetLbo;
5087 ULONG TargetCluster;
5088 LARGE_INTEGER LargeSourceLbo;
5089 LARGE_INTEGER LargeTargetLbo;
5090
5091 ULONG ByteCount;
5092 ULONG BytesToWrite;
5093 ULONG BytesToReallocate;
5094 #ifndef __REACTOS__
5095 ULONG TargetAllocation;
5096 #endif
5097
5098 ULONG FirstSpliceSourceCluster;
5099 ULONG FirstSpliceTargetCluster;
5100 ULONG SecondSpliceSourceCluster;
5101 ULONG SecondSpliceTargetCluster;
5102
5103 LARGE_MCB SourceMcb;
5104 LARGE_MCB TargetMcb;
5105
5106 KEVENT StackEvent;
5107
5108 PVOID Buffer;
5109 ULONG BufferSize;
5110
5111 BOOLEAN SourceMcbInitialized = FALSE;
5112 BOOLEAN TargetMcbInitialized = FALSE;
5113
5114 BOOLEAN FcbAcquired = FALSE;
5115 BOOLEAN EventArmed = FALSE;
5116 BOOLEAN DiskSpaceAllocated = FALSE;
5117
5118 PDIRENT Dirent;
5119 PBCB DirentBcb = NULL;
5120
5121 #if defined(_WIN64)
5122 MOVE_FILE_DATA LocalMoveFileData;
5123 PMOVE_FILE_DATA32 MoveFileData32;
5124 #endif
5125
5126 ULONG LocalAbnormalTermination = 0;
5127
5128 //
5129 // Get the current Irp stack location and save some references.
5130 //
5131
5132 IrpSp = IoGetCurrentIrpStackLocation( Irp );
5133
5134 DebugTrace(+1, Dbg, "FatMoveFile, FsControlCode = %08lx\n",
5135 IrpSp->Parameters.FileSystemControl.FsControlCode);
5136
5137 //
5138 // Force WAIT to true. We have a handle in the input buffer which can only
5139 // be referenced within the originating process.
5140 //
5141
5142 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
5143
5144 //
5145 // Extract and decode the file object and check for type of open.
5146 //
5147
5148 if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &FcbOrDcb, &Ccb ) != UserVolumeOpen) {
5149
5150 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
5151
5152 DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
5153 return STATUS_INVALID_PARAMETER;
5154 }
5155
5156 if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
5157
5158 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
5159
5160 DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
5161 return STATUS_INVALID_PARAMETER;
5162 }
5163
5164 InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
5165 InputBuffer = (PMOVE_FILE_DATA)Irp->AssociatedIrp.SystemBuffer;
5166
5167 //
5168 // Do a quick check on the input buffer.
5169 //
5170
5171 #if defined(_WIN64)
5172 if (IoIs32bitProcess( Irp )) {
5173
5174 if (InputBuffer == NULL || InputBufferLength < sizeof(MOVE_FILE_DATA32)) {
5175
5176 FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
5177 return STATUS_BUFFER_TOO_SMALL;
5178 }
5179
5180 MoveFileData32 = (PMOVE_FILE_DATA32) InputBuffer;
5181
5182 LocalMoveFileData.FileHandle = (HANDLE) LongToHandle( MoveFileData32->FileHandle );
5183 LocalMoveFileData.StartingVcn = MoveFileData32->StartingVcn;
5184 LocalMoveFileData.StartingLcn = MoveFileData32->StartingLcn;
5185 LocalMoveFileData.ClusterCount = MoveFileData32->ClusterCount;
5186
5187 InputBuffer = &LocalMoveFileData;
5188
5189 } else {
5190 #endif
5191 if (InputBuffer == NULL || InputBufferLength < sizeof(MOVE_FILE_DATA)) {
5192
5193 FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
5194 return STATUS_BUFFER_TOO_SMALL;
5195 }
5196 #if defined(_WIN64)
5197 }
5198 #endif
5199
5200 MaxClusters = Vcb->AllocationSupport.NumberOfClusters;
5201 TargetCluster = InputBuffer->StartingLcn.LowPart + 2;
5202
5203 if (InputBuffer->StartingVcn.HighPart ||
5204 InputBuffer->StartingLcn.HighPart ||
5205 (TargetCluster < 2) ||
5206 (TargetCluster + InputBuffer->ClusterCount < TargetCluster) ||
5207 (TargetCluster + InputBuffer->ClusterCount > MaxClusters + 2) ||
5208 (InputBuffer->StartingVcn.LowPart >= MaxClusters)) {
5209
5210 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
5211
5212 DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
5213 return STATUS_INVALID_PARAMETER;
5214 }
5215
5216 //
5217 // Try to get a pointer to the file object from the handle passed in.
5218 //
5219
5220 Status = ObReferenceObjectByHandle( InputBuffer->FileHandle,
5221 0,
5222 *IoFileObjectType,
5223 Irp->RequestorMode,
5224 #ifndef __REACTOS__
5225 &FileObject,
5226 #else
5227 (PVOID *)&FileObject,
5228 #endif
5229 NULL );
5230
5231 if (!NT_SUCCESS(Status)) {
5232
5233 FatCompleteRequest( IrpContext, Irp, Status );
5234
5235 DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", Status);
5236 return Status;
5237 }
5238
5239 //
5240 // There are three basic ways this could be an invalid attempt, so
5241 // we need to
5242 //
5243 // - check that this file object is opened on the same volume as the
5244 // DASD handle used to call this routine.
5245 //
5246 // - extract and decode the file object and check for type of open.
5247 //
5248 // - if this is a directory, verify that it's not the root and that
5249 // we are not trying to move the first cluster. We cannot move the
5250 // first cluster because sub-directories have this cluster number
5251 // in them and there is no safe way to simultaneously update them
5252 // all.
5253 //
5254 // We'll allow movefile on the root dir if its fat32, since the root dir
5255 // is a real chained file there.
5256 //
5257
5258 if (FileObject->Vpb != Vcb->Vpb) {
5259
5260 ObDereferenceObject( FileObject );
5261 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
5262
5263 DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
5264 return STATUS_INVALID_PARAMETER;
5265 }
5266
5267 TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &FcbOrDcb, &Ccb );
5268
5269 if ((TypeOfOpen != UserFileOpen &&
5270 TypeOfOpen != UserDirectoryOpen) ||
5271
5272 ((TypeOfOpen == UserDirectoryOpen) &&
5273 ((NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB && !FatIsFat32(Vcb)) ||
5274 (InputBuffer->StartingVcn.QuadPart == 0)))) {
5275
5276 ObDereferenceObject( FileObject );
5277 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
5278
5279 DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
5280 return STATUS_INVALID_PARAMETER;
5281 }
5282
5283 //
5284 // Indicate we're getting to parents of this fcb by their child, and that
5285 // this is a sufficient assertion of our ability to by synchronized
5286 // with respect to the parent directory going away.
5287 //
5288 // The defrag path is an example of one which arrives at an Fcb by
5289 // a means which would be unreasonable to duplicate in the assertion
5290 // code. See FatOpenDirectoryFile.
5291 //
5292
5293 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_PARENT_BY_CHILD );
5294
5295 ClusterShift = Vcb->AllocationSupport.LogOfBytesPerCluster;
5296
5297 _SEH2_TRY {
5298
5299 //
5300 // Initialize our state variables and the event.
5301 //
5302
5303 FileOffset = InputBuffer->StartingVcn.LowPart << ClusterShift;
5304
5305 ByteCount = InputBuffer->ClusterCount << ClusterShift;
5306
5307 TargetLbo = FatGetLboFromIndex( Vcb, TargetCluster );
5308 LargeTargetLbo.QuadPart = TargetLbo;
5309
5310 Buffer = NULL;
5311
5312 //
5313 // Do a quick check on parameters here
5314 //
5315
5316 if (FileOffset + ByteCount < FileOffset) {
5317
5318 try_return( Status = STATUS_INVALID_PARAMETER );
5319 }
5320
5321 KeInitializeEvent( &StackEvent, NotificationEvent, FALSE );
5322
5323 //
5324 // Initialize two MCBs we will be using
5325 //
5326
5327 FsRtlInitializeLargeMcb( &SourceMcb, PagedPool );
5328 SourceMcbInitialized = TRUE;
5329
5330 FsRtlInitializeLargeMcb( &TargetMcb, PagedPool );
5331 TargetMcbInitialized = TRUE;
5332
5333 //
5334 // Ok, now if this is a directory open we need to switch to the internal
5335 // stream fileobject since it is set up for caching. The top-level
5336 // fileobject has no section object pointers in order prevent folks from
5337 // mapping it.
5338 //
5339
5340 if (TypeOfOpen == UserDirectoryOpen) {
5341
5342 PFILE_OBJECT DirStreamFileObject;
5343
5344 //
5345 // Open the stream fileobject if neccesary. We must acquire the Fcb
5346 // now to synchronize with other operations (such as dismount ripping
5347 // apart the allocator).
5348 //
5349
5350 (VOID)FatAcquireExclusiveFcb( IrpContext, FcbOrDcb );
5351 FcbAcquired = TRUE;
5352
5353 FatVerifyFcb( IrpContext, FcbOrDcb );
5354
5355 FatOpenDirectoryFile( IrpContext, FcbOrDcb );
5356 DirStreamFileObject = FcbOrDcb->Specific.Dcb.DirectoryFile;
5357
5358 //
5359 // Transfer our reference to the internal stream and proceed. Note that
5360 // if we dereferenced first, the user could sneak a teardown through since
5361 // we'd have no references.
5362 //
5363
5364 ObReferenceObject( DirStreamFileObject );
5365 ObDereferenceObject( FileObject );
5366 FileObject = DirStreamFileObject;
5367 }
5368
5369 //
5370 // Determine the size of the buffer we will use to move data.
5371 //
5372
5373 BufferSize = FAT_DEFAULT_DEFRAG_CHUNK_IN_BYTES;
5374
5375 if (BufferSize < (ULONG)(1 << ClusterShift)) {
5376
5377 BufferSize = (1 << ClusterShift);
5378 }
5379
5380 while (ByteCount) {
5381
5382 VBO TempVbo;
5383 LBO TempLbo;
5384 ULONG TempByteCount;
5385
5386 //
5387 // We must throttle our writes.
5388 //
5389
5390 CcCanIWrite( FileObject,
5391 BufferSize,
5392 TRUE,
5393 FALSE );
5394
5395 //
5396 // Aqcuire file resource exclusive to freeze FileSize and block
5397 // user non-cached I/O. Verify the integrity of the fcb - the
5398 // media may have changed (or been dismounted) on us.
5399 //
5400
5401 if (FcbAcquired == FALSE) {
5402
5403 (VOID)FatAcquireExclusiveFcb( IrpContext, FcbOrDcb );
5404 FcbAcquired = TRUE;
5405
5406 FatVerifyFcb( IrpContext, FcbOrDcb );
5407 }
5408
5409 //
5410 // Allocate our buffer, if we need to.
5411 //
5412
5413 if (Buffer == NULL) {
5414
5415 Buffer = FsRtlAllocatePoolWithTag( NonPagedPool,
5416 BufferSize,
5417 TAG_DEFRAG_BUFFER );
5418 }
5419
5420 //
5421 // Analyzes the range of file allocation we are moving
5422 // and determines the actual amount of allocation to be
5423 // moved and how much needs to be written. In addition
5424 // it guarantees that the Mcb in the file is large enough
5425 // so that later MCB operations cannot fail.
5426 //
5427
5428 FatComputeMoveFileParameter( IrpContext,
5429 FcbOrDcb,
5430 BufferSize,
5431 FileOffset,
5432 &ByteCount,
5433 &BytesToReallocate,
5434 &BytesToWrite,
5435 &LargeSourceLbo );
5436
5437 //
5438 // If ByteCount comes back zero, break here.
5439 //
5440
5441 if (ByteCount == 0) {
5442 break;
5443 }
5444
5445 //
5446 // At this point (before actually doing anything with the disk
5447 // meta data), calculate the FAT splice clusters and build an
5448 // MCB describing the space to be deallocated.
5449 //
5450
5451 FatComputeMoveFileSplicePoints( IrpContext,
5452 FcbOrDcb,
5453 FileOffset,
5454 TargetCluster,
5455 BytesToReallocate,
5456 &FirstSpliceSourceCluster,
5457 &FirstSpliceTargetCluster,
5458 &SecondSpliceSourceCluster,
5459 &SecondSpliceTargetCluster,
5460 &SourceMcb );
5461
5462 //
5463 // Now attempt to allocate the new disk storage using the
5464 // Target Lcn as a hint.
5465 //
5466
5467 TempByteCount = BytesToReallocate;
5468 FatAllocateDiskSpace( IrpContext,
5469 Vcb,
5470 TargetCluster,
5471 &TempByteCount,
5472 TRUE,
5473 &TargetMcb );
5474
5475 DiskSpaceAllocated = TRUE;
5476
5477 //
5478 // If we didn't get EXACTLY what we wanted, return immediately.
5479 //
5480
5481 if ((FsRtlNumberOfRunsInLargeMcb( &TargetMcb ) != 1) ||
5482 !FatGetNextMcbEntry( Vcb, &TargetMcb, 0, &TempVbo, &TempLbo, &TempByteCount ) ||
5483 (FatGetIndexFromLbo( Vcb, TempLbo) != TargetCluster ) ||
5484 (TempByteCount != BytesToReallocate)) {
5485
5486 //
5487 // It would be nice if we could be more specific, but such is life.
5488 //
5489 try_return( Status = STATUS_INVALID_PARAMETER );
5490 }
5491
5492 #if DBG
5493 //
5494 // We are going to attempt a move, note it.
5495 //
5496
5497 if (FatMoveFileDebug) {
5498 DbgPrint("%lx: Vcn 0x%lx, Lcn 0x%lx, Count 0x%lx.\n",
5499 PsGetCurrentThread(),
5500 FileOffset >> ClusterShift,
5501 TargetCluster,
5502 BytesToReallocate >> ClusterShift );
5503 }
5504 #endif
5505
5506 //
5507 // Now attempt to commit the new allocation to disk. If this
5508 // raises, the allocation will be deallocated.
5509 //
5510
5511 FatFlushFatEntries( IrpContext,
5512 Vcb,
5513 TargetCluster,
5514 BytesToReallocate >> ClusterShift );
5515
5516 //
5517 // Aqcuire both resources exclusive now, guaranteeing that NOBODY
5518 // is in either the read or write paths.
5519 //
5520
5521 ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE );
5522
5523 //
5524 // This is the first part of some tricky synchronization.
5525 //
5526 // Set the Event pointer in the FCB. Any paging I/O will block on
5527 // this event (if set in FCB) after acquiring the PagingIo resource.
5528 //
5529 // This is how I keep ALL I/O out of this path without holding the
5530 // PagingIo resource exclusive for an extended time.
5531 //
5532
5533 FcbOrDcb->MoveFileEvent = &StackEvent;
5534 EventArmed = TRUE;
5535
5536 ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource );
5537
5538 //
5539 // Now write out the data, but only if we have to. We don't have
5540 // to copy any file data if the range being reallocated is wholly
5541 // beyond valid data length.
5542 //
5543
5544 if (BytesToWrite) {
5545
5546 PIRP IoIrp;
5547 KEVENT IoEvent;
5548 IO_STATUS_BLOCK Iosb;
5549
5550 KeInitializeEvent( &IoEvent,
5551 NotificationEvent,
5552 FALSE );
5553
5554 ASSERT( LargeTargetLbo.QuadPart >= Vcb->AllocationSupport.FileAreaLbo);
5555
5556 //
5557 // Read in the data that is being moved.
5558 //
5559
5560 IoIrp = IoBuildSynchronousFsdRequest( IRP_MJ_READ,
5561 Vcb->TargetDeviceObject,
5562 Buffer,
5563 BytesToWrite,
5564 &LargeSourceLbo,
5565 &IoEvent,
5566 &Iosb );
5567
5568 if (IoIrp == NULL) {
5569
5570 FatRaiseStatus( IrpContext,
5571 STATUS_INSUFFICIENT_RESOURCES );
5572 }
5573
5574 Status = IoCallDriver( Vcb->TargetDeviceObject,
5575 IoIrp );
5576
5577 if (Status == STATUS_PENDING) {
5578
5579 (VOID)KeWaitForSingleObject( &IoEvent,
5580 Executive,
5581 KernelMode,
5582 FALSE,
5583 (PLARGE_INTEGER)NULL );
5584
5585 Status = Iosb.Status;
5586 }
5587
5588 if (!NT_SUCCESS( Status )) {
5589
5590 FatNormalizeAndRaiseStatus( IrpContext,
5591 Status );
5592 }
5593
5594 //
5595 // Write the data to its new location.
5596 //
5597
5598 KeInitializeEvent( &IoEvent, NotificationEvent, FALSE );
5599
5600 IoIrp = IoBuildSynchronousFsdRequest( IRP_MJ_WRITE,
5601 Vcb->TargetDeviceObject,
5602 Buffer,
5603 BytesToWrite,
5604 &LargeTargetLbo,
5605 &IoEvent,
5606 &Iosb );
5607
5608 if (!IoIrp) {
5609 FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
5610 }
5611
5612 //
5613 // Set a flag indicating that we want to write through any
5614 // cache on the controller. This eliminates the need for
5615 // an explicit flush-device after the write.
5616 //
5617
5618 SetFlag( IoGetNextIrpStackLocation(IoIrp)->Flags, SL_WRITE_THROUGH );
5619
5620 Status = IoCallDriver( Vcb->TargetDeviceObject, IoIrp );
5621
5622 if (Status == STATUS_PENDING) {
5623 (VOID)KeWaitForSingleObject( &IoEvent, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL );
5624 Status = Iosb.Status;
5625 }
5626
5627 if (!NT_SUCCESS(Status)) {
5628 FatNormalizeAndRaiseStatus( IrpContext, Status );
5629 }
5630 }
5631
5632 //
5633 // Now that the file data has been moved successfully, we'll go
5634 // to fix up the links in the FAT table and perhaps change the
5635 // entry in the parent directory.
5636 //
5637 // First we'll do the second splice and commit it. At that point,
5638 // while the volume is in an inconsistent state, the file is
5639 // still OK.
5640 //
5641
5642 FatSetFatEntry( IrpContext,
5643 Vcb,
5644 SecondSpliceSourceCluster,
5645 (FAT_ENTRY)SecondSpliceTargetCluster );
5646
5647 FatFlushFatEntries( IrpContext, Vcb, SecondSpliceSourceCluster, 1 );
5648
5649 //
5650 // Now do the first splice OR update the dirent in the parent
5651 // and flush the respective object. After this flush the file
5652 // now points to the new allocation.
5653 //
5654
5655 if (FirstSpliceSourceCluster == 0) {
5656
5657 ASSERT( NodeType(FcbOrDcb) == FAT_NTC_FCB );
5658
5659 //
5660 // We are moving the first cluster of the file, so we need
5661 // to update our parent directory.
5662 //
5663
5664 FatGetDirentFromFcbOrDcb( IrpContext, FcbOrDcb, &Dirent, &DirentBcb );
5665 Dirent->FirstClusterOfFile = (USHORT)FirstSpliceTargetCluster;
5666
5667 if (FatIsFat32(Vcb)) {
5668
5669 Dirent->FirstClusterOfFileHi =
5670 (USHORT)(FirstSpliceTargetCluster >> 16);
5671
5672 }
5673
5674 FatSetDirtyBcb( IrpContext, DirentBcb, Vcb, TRUE );
5675
5676 FatUnpinBcb( IrpContext, DirentBcb );
5677 DirentBcb = NULL;
5678
5679 FatFlushDirentForFile( IrpContext, FcbOrDcb );
5680
5681 FcbOrDcb->FirstClusterOfFile = FirstSpliceTargetCluster;
5682
5683 } else {
5684
5685 FatSetFatEntry( IrpContext,
5686 Vcb,
5687 FirstSpliceSourceCluster,
5688 (FAT_ENTRY)FirstSpliceTargetCluster );
5689
5690 FatFlushFatEntries( IrpContext, Vcb, FirstSpliceSourceCluster, 1 );
5691 }
5692
5693 //
5694 // This was successfully committed. We no longer want to free
5695 // this allocation on error.
5696 //
5697
5698 DiskSpaceAllocated = FALSE;
5699
5700 //
5701 // Now we just have to free the orphaned space. We don't have
5702 // to commit this right now as the integrity of the file doesn't
5703 // depend on it.
5704 //
5705
5706 FatDeallocateDiskSpace( IrpContext, Vcb, &SourceMcb );
5707
5708 FatUnpinRepinnedBcbs( IrpContext );
5709
5710 Status = FatHijackIrpAndFlushDevice( IrpContext,
5711 Irp,
5712 Vcb->TargetDeviceObject );
5713
5714 if (!NT_SUCCESS(Status)) {
5715 FatNormalizeAndRaiseStatus( IrpContext, Status );
5716 }
5717
5718 //
5719 // Finally we must replace the old MCB extent information with
5720 // the new. If this fails from pool allocation, we fix it in
5721 // the finally clause by resetting the file's Mcb.
5722 //
5723
5724 FatRemoveMcbEntry( Vcb, &FcbOrDcb->Mcb,
5725 FileOffset,
5726 BytesToReallocate );
5727
5728 FatAddMcbEntry( Vcb, &FcbOrDcb->Mcb,
5729 FileOffset,
5730 TargetLbo,
5731 BytesToReallocate );
5732
5733 //
5734 // Now this is the second part of the tricky synchronization.
5735 //
5736 // We drop the paging I/O here and signal the notification
5737 // event which allows all waiters (present or future) to proceed.
5738 // Then we block again on the PagingIo exclusive. When
5739 // we have it, we again know that there can be nobody in the
5740 // read/write path and thus nobody touching the event, so we
5741 // NULL the pointer to it and then drop the PagingIo resource.
5742 //
5743 // This combined with our synchronization before the write above
5744 // guarantees that while we were moving the allocation, there
5745 // was no other I/O to this file and because we do not hold
5746 // the paging resource across a flush, we are not exposed to
5747 // a deadlock.
5748 //
5749
5750 KeSetEvent( &StackEvent, 0, FALSE );
5751
5752 ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE );
5753
5754 FcbOrDcb->MoveFileEvent = NULL;
5755 EventArmed = FALSE;
5756
5757 ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource );
5758
5759 //
5760 // Release the resources and let anyone else access the file before
5761 // looping back.
5762 //
5763
5764 FatReleaseFcb( IrpContext, FcbOrDcb );
5765 FcbAcquired = FALSE;
5766
5767 //
5768 // Advance the state variables.
5769 //
5770
5771 TargetCluster += BytesToReallocate >> ClusterShift;
5772
5773 FileOffset += BytesToReallocate;
5774 TargetLbo += BytesToReallocate;
5775 ByteCount -= BytesToReallocate;
5776
5777 LargeTargetLbo.QuadPart += BytesToReallocate;
5778
5779 //
5780 // Clear the two Mcbs
5781 //
5782
5783 FatRemoveMcbEntry( Vcb, &SourceMcb, 0, 0xFFFFFFFF );
5784 FatRemoveMcbEntry( Vcb, &TargetMcb, 0, 0xFFFFFFFF );
5785
5786 //
5787 // Make the event blockable again.
5788 //
5789
5790 KeClearEvent( &StackEvent );
5791 }
5792
5793 Status = STATUS_SUCCESS;
5794
5795 try_exit: NOTHING;
5796
5797 } _SEH2_FINALLY {
5798
5799 DebugUnwind( FatMoveFile );
5800
5801 LocalAbnormalTermination |= _SEH2_AbnormalTermination();
5802
5803 ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_PARENT_BY_CHILD );
5804
5805 //
5806 // Free the data buffer, if it was allocated.
5807 //
5808
5809 if (Buffer != NULL) {
5810
5811 ExFreePool( Buffer );
5812 }
5813
5814 //
5815 // Use a nested try-finally for cleanup if our unpinrepinned
5816 // encounters write-through errors. This may even be a re-raise.
5817 //
5818
5819 _SEH2_TRY {
5820
5821 //
5822 // If we have some new allocation hanging around, remove it. The
5823 // pages needed to do this are guaranteed to be resident because
5824 // we have already repinned them.
5825 //
5826
5827 if (DiskSpaceAllocated) {
5828 FatDeallocateDiskSpace( IrpContext, Vcb, &TargetMcb );
5829 FatUnpinRepinnedBcbs( IrpContext );
5830 }
5831
5832 } _SEH2_FINALLY {
5833
5834 LocalAbnormalTermination |= _SEH2_AbnormalTermination();
5835
5836 //
5837 // Check on the directory Bcb
5838 //
5839
5840 if (DirentBcb != NULL) {
5841 FatUnpinBcb( IrpContext, DirentBcb );
5842 }
5843
5844 //
5845 // Uninitialize our MCBs
5846 //
5847
5848 if (SourceMcbInitialized) {
5849 FsRtlUninitializeLargeMcb( &SourceMcb );
5850 }
5851
5852 if (TargetMcbInitialized) {
5853 FsRtlUninitializeLargeMcb( &TargetMcb );
5854 }
5855
5856 //
5857 // If this is an abnormal termination then presumably something
5858 // bad happened. Set the Allocation size to unknown and clear
5859 // the Mcb, but only if we still own the Fcb.
5860 //
5861 // It is important to make sure we use a 64bit form of -1. This is
5862 // what will convince the fastIO path that it cannot extend the file
5863 // in the cache until we have picked up the mapping pairs again.
5864 //
5865 // Also, we have to do this while owning PagingIo or we can tear the
5866 // Mcb down in the midst of the noncached IO path looking up extents
5867 // (after we drop it and let them all in).
5868 //
5869
5870 if (LocalAbnormalTermination && FcbAcquired) {
5871
5872 if (FcbOrDcb->FirstClusterOfFile == 0) {
5873
5874 FcbOrDcb->Header.AllocationSize.QuadPart = 0;
5875 } else {
5876
5877 FcbOrDcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT;
5878 }
5879
5880 FatRemoveMcbEntry( Vcb, &FcbOrDcb->Mcb, 0, 0xFFFFFFFF );
5881 }
5882
5883 //
5884 // If we broke out of the loop with the Event armed, defuse it
5885 // in the same way we do it after a write.
5886 //
5887
5888 if (EventArmed) {
5889 KeSetEvent( &StackEvent, 0, FALSE );
5890 ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE );
5891 FcbOrDcb->MoveFileEvent = NULL;
5892 ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource );
5893 }
5894
5895 //
5896 // Finally release the main file resource.
5897 //
5898
5899 if (FcbAcquired) {
5900 FatReleaseFcb( IrpContext, FcbOrDcb );
5901 }
5902
5903 //
5904 // Now dereference the fileobject. If the user was a wacko they could have
5905 // tried to nail us by closing the handle right after they threw this move
5906 // down, so we had to keep the fileobject referenced across the entire
5907 // operation.
5908 //
5909
5910 ObDereferenceObject( FileObject );
5911
5912 } _SEH2_END;
5913 } _SEH2_END;
5914
5915 //
5916 // Complete the irp if we terminated normally.
5917 //
5918
5919 FatCompleteRequest( IrpContext, Irp, Status );
5920
5921 return Status;
5922 }
5923
5924 \f
5925 //
5926 // Local Support Routine
5927 //
5928
5929 VOID
5930 FatComputeMoveFileParameter (
5931 IN PIRP_CONTEXT IrpContext,
5932 IN PFCB FcbOrDcb,
5933 IN ULONG BufferSize,
5934 IN ULONG FileOffset,
5935 IN OUT PULONG ByteCount,
5936 OUT PULONG BytesToReallocate,
5937 OUT PULONG BytesToWrite,
5938 OUT PLARGE_INTEGER SourceLbo
5939 )
5940
5941 /*++
5942
5943 Routine Description:
5944
5945 This is a helper routine for FatMoveFile that analyses the range of
5946 file allocation we are moving and determines the actual amount
5947 of allocation to be moved and how much needs to be written.
5948
5949 Arguments:
5950
5951 FcbOrDcb - Supplies the file and its various sizes.
5952
5953 BufferSize - Supplies the size of the buffer we are using to store the data
5954 being moved.
5955
5956 FileOffset - Supplies the beginning Vbo of the reallocation zone.
5957
5958 ByteCount - Supplies the request length to reallocate. This will
5959 be bounded by allocation size on return.
5960
5961 BytesToReallocate - Receives ByteCount bounded by the file allocation size
5962 and buffer size.
5963
5964 BytesToWrite - Receives BytesToReallocate bounded by ValidDataLength.
5965
5966 SourceLbo - Receives the logical byte offset of the source data on the volume.
5967
5968 Return Value:
5969
5970 VOID
5971
5972 --*/
5973
5974 {
5975 ULONG ClusterSize;
5976
5977 ULONG AllocationSize;
5978 ULONG ValidDataLength;
5979 ULONG ClusterAlignedVDL;
5980 LBO RunLbo;
5981 ULONG RunByteCount;
5982 ULONG RunIndex;
5983 BOOLEAN RunAllocated;
5984 BOOLEAN RunEndOnMax;
5985
5986
5987 //
5988 // If we haven't yet set the correct AllocationSize, do so.
5989 //
5990
5991 if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
5992
5993 FatLookupFileAllocationSize( IrpContext, FcbOrDcb );
5994
5995 //
5996 // If this is a non-root directory, we have a bit more to
5997 // do since it has not gone through FatOpenDirectoryFile().
5998 //
5999
6000 if (NodeType(FcbOrDcb) == FAT_NTC_DCB ||
6001 (NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB && FatIsFat32(FcbOrDcb->Vcb))) {
6002
6003 FcbOrDcb->Header.FileSize.LowPart =
6004 FcbOrDcb->Header.AllocationSize.LowPart;
6005 }
6006 }
6007
6008 //
6009 // Get the number of bytes left to write and ensure that it does
6010 // not extend beyond allocation size. We return here if FileOffset
6011 // is beyond AllocationSize which can happn on a truncation.
6012 //
6013
6014 AllocationSize = FcbOrDcb->Header.AllocationSize.LowPart;
6015 ValidDataLength = FcbOrDcb->Header.ValidDataLength.LowPart;
6016
6017 if (FileOffset + *ByteCount > AllocationSize) {
6018
6019 if (FileOffset >= AllocationSize) {
6020 *ByteCount = 0;
6021 *BytesToReallocate = 0;
6022 *BytesToWrite = 0;
6023
6024 return;
6025 }
6026
6027 *ByteCount = AllocationSize - FileOffset;
6028 }
6029
6030 //
6031 // If there is more than our max, then reduce the byte count for this
6032 // pass to our maximum. We must also align the file offset to a
6033 // buffer size byte boundary.
6034 //
6035
6036 if ((FileOffset & (BufferSize - 1)) + *ByteCount > BufferSize) {
6037
6038 *BytesToReallocate = BufferSize - (FileOffset & (BufferSize - 1));
6039
6040 } else {
6041
6042 *BytesToReallocate = *ByteCount;
6043 }
6044
6045 //
6046 // Find where this data exists on the volume.
6047 //
6048
6049 FatLookupFileAllocation( IrpContext,
6050 FcbOrDcb,
6051 FileOffset,
6052 &RunLbo,
6053 &RunByteCount,
6054 &RunAllocated,
6055 &RunEndOnMax,
6056 &RunIndex );
6057
6058 ASSERT( RunAllocated );
6059
6060 //
6061 // Limit this run to the contiguous length.
6062 //
6063
6064 if (RunByteCount < *BytesToReallocate) {
6065
6066 *BytesToReallocate = RunByteCount;
6067 }
6068
6069 //
6070 // Set the starting offset of the source.
6071 //
6072
6073 SourceLbo->QuadPart = RunLbo;
6074
6075 //
6076 // We may be able to skip some (or all) of the write
6077 // if allocation size is significantly greater than valid data length.
6078 //
6079
6080 ClusterSize = 1 << FcbOrDcb->Vcb->AllocationSupport.LogOfBytesPerCluster;
6081
6082 ASSERT( ClusterSize <= BufferSize );
6083
6084 ClusterAlignedVDL = (ValidDataLength + (ClusterSize - 1)) & ~(ClusterSize - 1);
6085
6086 if ((NodeType(FcbOrDcb) == FAT_NTC_FCB) &&
6087 (FileOffset + *BytesToReallocate > ClusterAlignedVDL)) {
6088
6089 if (FileOffset > ClusterAlignedVDL) {
6090
6091 *BytesToWrite = 0;
6092
6093 } else {
6094
6095 *BytesToWrite = ClusterAlignedVDL - FileOffset;
6096 }
6097
6098 } else {
6099
6100 *BytesToWrite = *BytesToReallocate;
6101 }
6102 }
6103
6104 \f
6105 //
6106 // Local Support Routine
6107 //
6108
6109 VOID
6110 FatComputeMoveFileSplicePoints (
6111 IN PIRP_CONTEXT IrpContext,
6112 IN PFCB FcbOrDcb,
6113 IN ULONG FileOffset,
6114 IN ULONG TargetCluster,
6115 IN ULONG BytesToReallocate,
6116 OUT PULONG FirstSpliceSourceCluster,
6117 OUT PULONG FirstSpliceTargetCluster,
6118 OUT PULONG SecondSpliceSourceCluster,
6119 OUT PULONG SecondSpliceTargetCluster,
6120 IN OUT PLARGE_MCB SourceMcb
6121 )
6122
6123 /*++
6124
6125 Routine Description:
6126
6127 This is a helper routine for FatMoveFile that analyzes the range of
6128 file allocation we are moving and generates the splice points in the
6129 FAT table.
6130
6131 Arguments:
6132
6133 FcbOrDcb - Supplies the file and thus Mcb.
6134
6135 FileOffset - Supplies the beginning Vbo of the reallocation zone.
6136
6137 TargetCluster - Supplies the beginning cluster of the reallocation target.
6138
6139 BytesToReallocate - Suppies the length of the reallocation zone.
6140
6141 FirstSpliceSourceCluster - Receives the last cluster in previous allocation
6142 or zero if we are reallocating from VBO 0.
6143
6144 FirstSpliceTargetCluster - Receives the target cluster (i.e. new allocation)
6145
6146 SecondSpliceSourceCluster - Receives the final target cluster.
6147
6148 SecondSpliceTargetCluster - Receives the first cluster of the remaining
6149 source allocation or FAT_CLUSTER_LAST if the reallocation zone
6150 extends to the end of the file.
6151
6152 SourceMcb - This supplies an MCB that will be filled in with run
6153 information describing the file allocation being replaced. The Mcb
6154 must be initialized by the caller.
6155
6156 Return Value:
6157
6158 VOID
6159
6160 --*/
6161
6162 {
6163 VBO SourceVbo;
6164 LBO SourceLbo;
6165 ULONG SourceIndex;
6166 ULONG SourceBytesInRun;
6167 ULONG SourceBytesRemaining;
6168
6169 ULONG SourceMcbVbo;
6170 ULONG SourceMcbBytesInRun;
6171
6172 PVCB Vcb;
6173
6174 Vcb = FcbOrDcb->Vcb;
6175
6176 //
6177 // Get information on the final cluster in the previous allocation and
6178 // prepare to enumerate it in the follow loop.
6179 //
6180
6181 if (FileOffset == 0) {
6182
6183 SourceIndex = 0;
6184 *FirstSpliceSourceCluster = 0;
6185 FatGetNextMcbEntry( Vcb, &FcbOrDcb->Mcb,
6186 0,
6187 &SourceVbo,
6188 &SourceLbo,
6189 &SourceBytesInRun );
6190
6191 } else {
6192
6193 FatLookupMcbEntry( Vcb, &FcbOrDcb->Mcb,
6194 FileOffset-1,
6195 &SourceLbo,
6196 &SourceBytesInRun,
6197 &SourceIndex);
6198
6199 *FirstSpliceSourceCluster = FatGetIndexFromLbo( Vcb, SourceLbo );
6200
6201 if (SourceBytesInRun == 1) {
6202
6203 SourceIndex += 1;
6204 FatGetNextMcbEntry( Vcb, &FcbOrDcb->Mcb,
6205 SourceIndex,
6206 &SourceVbo,
6207 &SourceLbo,
6208 &SourceBytesInRun);
6209
6210 } else {
6211
6212 SourceVbo = FileOffset;
6213 SourceLbo += 1;
6214 SourceBytesInRun -= 1;
6215 }
6216 }
6217
6218 //
6219 // At this point the variables:
6220 //
6221 // - SourceIndex - SourceLbo - SourceBytesInRun -
6222 //
6223 // all correctly decribe the allocation to be removed. In the loop
6224 // below we will start here and continue enumerating the Mcb runs
6225 // until we are finished with the allocation to be relocated.
6226 //
6227
6228 *FirstSpliceTargetCluster = TargetCluster;
6229
6230 *SecondSpliceSourceCluster =
6231 *FirstSpliceTargetCluster +
6232 (BytesToReallocate >> Vcb->AllocationSupport.LogOfBytesPerCluster) - 1;
6233
6234 for (SourceBytesRemaining = BytesToReallocate, SourceMcbVbo = 0;
6235
6236 SourceBytesRemaining > 0;
6237
6238 SourceIndex += 1,
6239 SourceBytesRemaining -= SourceMcbBytesInRun,
6240 SourceMcbVbo += SourceMcbBytesInRun) {
6241
6242 if (SourceMcbVbo != 0) {
6243 FatGetNextMcbEntry( Vcb, &FcbOrDcb->Mcb,
6244 SourceIndex,
6245 &SourceVbo,
6246 &SourceLbo,
6247 &SourceBytesInRun );
6248 }
6249
6250 ASSERT( SourceVbo == SourceMcbVbo + FileOffset );
6251
6252 SourceMcbBytesInRun =
6253 SourceBytesInRun < SourceBytesRemaining ?
6254 SourceBytesInRun : SourceBytesRemaining;
6255
6256 FatAddMcbEntry( Vcb, SourceMcb,
6257 SourceMcbVbo,
6258 SourceLbo,
6259 SourceMcbBytesInRun );
6260 }
6261
6262 //
6263 // Now compute the cluster of the target of the second
6264 // splice. If the final run in the above loop was
6265 // more than we needed, then we can just do arithmetic,
6266 // otherwise we have to look up the next run.
6267 //
6268
6269 if (SourceMcbBytesInRun < SourceBytesInRun) {
6270
6271 *SecondSpliceTargetCluster =
6272 FatGetIndexFromLbo( Vcb, SourceLbo + SourceMcbBytesInRun );
6273
6274 } else {
6275
6276 if (FatGetNextMcbEntry( Vcb, &FcbOrDcb->Mcb,
6277 SourceIndex,
6278 &SourceVbo,
6279 &SourceLbo,
6280 &SourceBytesInRun )) {
6281
6282 *SecondSpliceTargetCluster = FatGetIndexFromLbo( Vcb, SourceLbo );
6283
6284 } else {
6285
6286 *SecondSpliceTargetCluster = FAT_CLUSTER_LAST;
6287 }
6288 }
6289 }
6290
6291 \f
6292 NTSTATUS
6293 FatAllowExtendedDasdIo(
6294 IN PIRP_CONTEXT IrpContext,
6295 IN PIRP Irp
6296 )
6297 /*++
6298
6299 Routine Description:
6300
6301 This routine marks the CCB to indicate that the handle
6302 may be used to read past the end of the volume file. The
6303 handle must be a dasd handle.
6304
6305 Arguments:
6306
6307 Irp - Supplies the Irp being processed.
6308
6309 Return Value:
6310
6311 NTSTATUS - The return status for the operation.
6312
6313 --*/
6314 {
6315 PIO_STACK_LOCATION IrpSp;
6316 PVCB Vcb;
6317 PFCB Fcb;
6318 PCCB Ccb;
6319
6320 //
6321 // Get the current Irp stack location and save some references.
6322 //
6323
6324 IrpSp = IoGetCurrentIrpStackLocation( Irp );
6325
6326 //
6327 // Extract and decode the file object and check for type of open.
6328 //
6329
6330 if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
6331
6332 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
6333 return STATUS_INVALID_PARAMETER;
6334 }
6335
6336 if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
6337
6338 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
6339
6340 DebugTrace(-1, Dbg, "FatAllowExtendedDasdIo -> %08lx\n", STATUS_INVALID_PARAMETER);
6341 return STATUS_INVALID_PARAMETER;
6342 }
6343
6344 SetFlag( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO );
6345
6346 FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
6347 return STATUS_SUCCESS;
6348 }
6349
6350
6351 \f
6352 VOID
6353 FatFlushAndCleanVolume(
6354 IN PIRP_CONTEXT IrpContext,
6355 IN PIRP Irp,
6356 IN PVCB Vcb,
6357 IN FAT_FLUSH_TYPE FlushType
6358 )
6359 /*++
6360
6361 Routine Description:
6362
6363 This routine flushes and otherwise preparse a volume to be eligible
6364 for deletion. The dismount and PNP paths share the need for this
6365 common work.
6366
6367 The Vcb will always be valid on return from this function. It is the
6368 caller's responsibility to attempt the dismount/deletion, and to setup
6369 allocation support again if the volume will be brought back from the
6370 brink.
6371
6372 Arguments:
6373
6374 Irp - Irp for the overlying request
6375
6376 Vcb - the volume being operated on
6377
6378 FlushType - specifies the kind of flushing desired
6379
6380 Return Value:
6381
6382 NTSTATUS - The return status for the operation.
6383
6384 --*/
6385 {
6386 //
6387 // The volume must be held exclusive.
6388 //
6389
6390 ASSERT( FatVcbAcquiredExclusive( IrpContext, Vcb ));
6391
6392 //
6393 // There is no fail, flush everything. If invalidating, it is important
6394 // that we invalidate as we flush (eventually, w/ paging io held) so that we
6395 // error out the maximum number of late writes.
6396 //
6397
6398 if (FlushType != NoFlush) {
6399
6400 (VOID) FatFlushVolume( IrpContext, Vcb, FlushType );
6401 }
6402
6403 FatCloseEaFile( IrpContext, Vcb, FALSE );
6404
6405 //
6406 // Now, tell the device to flush its buffers.
6407 //
6408
6409 if (FlushType != NoFlush) {
6410
6411 (VOID)FatHijackIrpAndFlushDevice( IrpContext, Irp, Vcb->TargetDeviceObject );
6412 }
6413
6414 //
6415 // Now purge everything in sight. We're trying to provoke as many closes as
6416 // soon as possible, this volume may be on its way out.
6417 //
6418
6419 if (FlushType != FlushWithoutPurge) {
6420
6421 (VOID) FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, NoFlush );
6422 }
6423
6424 //
6425 // If the volume was dirty and we were allowed to flush, do the processing that
6426 // the delayed callback would have done.
6427 //
6428
6429 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY)) {
6430
6431 //
6432 // Cancel any pending clean volumes.
6433 //
6434
6435 (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer );
6436 (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc );
6437
6438
6439 if (FlushType != NoFlush) {
6440
6441 //
6442 // The volume is now clean, note it.
6443 //
6444
6445 if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) {
6446
6447 FatMarkVolume( IrpContext, Vcb, VolumeClean );
6448 ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY );
6449 }
6450
6451 //
6452 // Unlock the volume if it is removable.
6453 //
6454
6455 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) &&
6456 !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) {
6457
6458 FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE );
6459 }
6460 }
6461 }
6462 }
6463
6464 \f
6465 NTSTATUS
6466 FatSearchBufferForLabel(
6467 IN PIRP_CONTEXT IrpContext,
6468 IN PVPB Vpb,
6469 IN PVOID Buffer,
6470 IN ULONG Size,
6471 OUT PBOOLEAN LabelFound
6472 )
6473 /*++
6474
6475 Routine Description:
6476
6477 Search a buffer (taken from the root directory) for a volume label
6478 matching the label in the
6479
6480 Arguments:
6481
6482 IrpContext - Supplies our irp context
6483 Vpb - Vpb supplying the volume label
6484 Buffer - Supplies the buffer we'll search
6485 Size - The size of the buffer in bytes.
6486 LabelFound - Returns whether a label was found.
6487
6488 Return Value:
6489
6490 There are four interesting cases:
6491
6492 1) Some random error occurred - that error returned as status, LabelFound
6493 is indeterminate.
6494
6495 2) No label was found - STATUS_SUCCESS returned, LabelFound is FALSE.
6496
6497 3) A matching label was found - STATUS_SUCCESS returned, LabelFound is TRUE.
6498
6499 4) A non-matching label found - STATUS_WRONG_VOLUME returned, LabelFound
6500 is indeterminate.
6501
6502 --*/
6503
6504 {
6505 NTSTATUS Status;
6506 WCHAR UnicodeBuffer[11];
6507
6508 PDIRENT Dirent;
6509 PDIRENT TerminationDirent;
6510 ULONG VolumeLabelLength;
6511 OEM_STRING OemString;
6512 UNICODE_STRING UnicodeString;
6513
6514 Dirent = Buffer;
6515
6516 TerminationDirent = Dirent + Size / sizeof(DIRENT);
6517
6518 while ( Dirent < TerminationDirent ) {
6519
6520 if ( Dirent->FileName[0] == FAT_DIRENT_NEVER_USED ) {
6521
6522 Dirent = TerminationDirent;
6523 break;
6524 }
6525
6526 //
6527 // If the entry is the non-deleted volume label break from the loop.
6528 //
6529 // Note that all out parameters are already correctly set.
6530 //
6531
6532 if (((Dirent->Attributes & ~FAT_DIRENT_ATTR_ARCHIVE) ==
6533 FAT_DIRENT_ATTR_VOLUME_ID) &&
6534 (Dirent->FileName[0] != FAT_DIRENT_DELETED)) {
6535
6536 break;
6537 }
6538
6539 Dirent += 1;
6540 }
6541
6542 if (Dirent >= TerminationDirent) {
6543
6544 //
6545 // We've run out of buffer.
6546 //
6547
6548 *LabelFound = FALSE;
6549 return STATUS_SUCCESS;
6550 }
6551
6552
6553 //
6554 // Compute the length of the volume name
6555 //
6556
6557 #ifndef __REACTOS__
6558 OemString.Buffer = &Dirent->FileName[0];
6559 #else
6560 OemString.Buffer = (PCHAR)&Dirent->FileName[0];
6561 #endif
6562 OemString.MaximumLength = 11;
6563
6564 for ( OemString.Length = 11;
6565 OemString.Length > 0;
6566 OemString.Length -= 1) {
6567
6568 if ( (Dirent->FileName[OemString.Length-1] != 0x00) &&
6569 (Dirent->FileName[OemString.Length-1] != 0x20) ) { break; }
6570 }
6571
6572 UnicodeString.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH;
6573 UnicodeString.Buffer = &UnicodeBuffer[0];
6574
6575 Status = RtlOemStringToCountedUnicodeString( &UnicodeString,
6576 &OemString,
6577 FALSE );
6578
6579 if ( !NT_SUCCESS( Status ) ) {
6580
6581 return Status;
6582 }
6583
6584 VolumeLabelLength = UnicodeString.Length;
6585
6586 if ( (VolumeLabelLength != (ULONG)Vpb->VolumeLabelLength) ||
6587 (!RtlEqualMemory(&UnicodeBuffer[0],
6588 &Vpb->VolumeLabel[0],
6589 VolumeLabelLength)) ) {
6590
6591 return STATUS_WRONG_VOLUME;
6592 }
6593
6594 //
6595 // We found a matching label.
6596 //
6597
6598 *LabelFound = TRUE;
6599 return STATUS_SUCCESS;
6600 }
6601
6602 \f
6603 VOID
6604 FatVerifyLookupFatEntry (
6605 IN PIRP_CONTEXT IrpContext,
6606 IN PVCB Vcb,
6607 IN ULONG FatIndex,
6608 IN OUT PULONG FatEntry
6609 )
6610 {
6611 ULONG PageEntryOffset;
6612 ULONG OffsetIntoVolumeFile;
6613 PVOID Buffer;
6614
6615 ASSERT(Vcb->AllocationSupport.FatIndexBitSize == 32);
6616
6617 FatVerifyIndexIsValid( IrpContext, Vcb, FatIndex);
6618
6619 Buffer = FsRtlAllocatePoolWithTag( NonPagedPoolCacheAligned,
6620 PAGE_SIZE,
6621 TAG_ENTRY_LOOKUP_BUFFER );
6622
6623 OffsetIntoVolumeFile = FatReservedBytes(&Vcb->Bpb) + FatIndex * sizeof(ULONG);
6624 PageEntryOffset = (OffsetIntoVolumeFile % PAGE_SIZE) / sizeof(ULONG);
6625
6626 _SEH2_TRY {
6627
6628 FatPerformVerifyDiskRead( IrpContext,
6629 Vcb,
6630 Buffer,
6631 OffsetIntoVolumeFile & ~(PAGE_SIZE - 1),
6632 PAGE_SIZE,
6633 TRUE );
6634
6635 *FatEntry = ((PULONG)(Buffer))[PageEntryOffset];
6636
6637 } _SEH2_FINALLY {
6638
6639 ExFreePool( Buffer );
6640 } _SEH2_END;
6641 }
6642
6643 //
6644 // Local support routine
6645 //
6646
6647 VOID
6648 FatScanForDismountedVcb (
6649 IN PIRP_CONTEXT IrpContext
6650 )
6651
6652 /*++
6653
6654 Routine Description:
6655
6656 This routine walks through the list of Vcb's looking for any which may
6657 now be deleted. They may have been left on the list because there were
6658 outstanding references.
6659
6660 Arguments:
6661
6662 Return Value:
6663
6664 None
6665
6666 --*/
6667
6668 {
6669 PVCB Vcb;
6670 PLIST_ENTRY Links;
6671 BOOLEAN VcbDeleted;
6672
6673 PAGED_CODE();
6674
6675 //
6676 // Walk through all of the Vcb's attached to the global data.
6677 //
6678
6679 FatAcquireExclusiveGlobal( IrpContext );
6680
6681 Links = FatData.VcbQueue.Flink;
6682
6683 while (Links != &FatData.VcbQueue) {
6684
6685 Vcb = CONTAINING_RECORD( Links, VCB, VcbLinks );
6686
6687 //
6688 // Move to the next link now since the current Vcb may be deleted.
6689 //
6690
6691 Links = Links->Flink;
6692
6693 //
6694 // Try to acquire the VCB for exclusive access. If we cannot, just skip
6695 // it for now.
6696 //
6697
6698 if (!ExAcquireResourceExclusiveLite( &(Vcb->Resource), FALSE )) {
6699
6700 continue;
6701 }
6702
6703 //
6704 // Check if this Vcb can go away.
6705 //
6706
6707 VcbDeleted = FatCheckForDismount ( IrpContext,
6708 Vcb,
6709 FALSE );
6710
6711 //
6712 // If the VCB was not deleted, release it.
6713 //
6714
6715 if (!VcbDeleted) {
6716
6717 ExReleaseResourceLite( &(Vcb->Resource) );
6718 }
6719 }
6720
6721 FatReleaseGlobal( IrpContext );
6722
6723 return;
6724 }
6725
6726