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