1 /*++
2
3 Copyright (c) 1989-2000 Microsoft Corporation
4
5 Module Name:
6
7 StrucSup.c
8
9 Abstract:
10
11 This module implements the Fat in-memory data structure manipulation
12 routines
13
14
15 --*/
16
17 #include "fatprocs.h"
18
19 //
20 // The Bug check file id for this module
21 //
22
23 #define BugCheckFileId (FAT_BUG_CHECK_STRUCSUP)
24
25 //
26 // The debug trace level
27 //
28
29 #define Dbg (DEBUG_TRACE_STRUCSUP)
30
31 #define FillMemory(BUF,SIZ,MASK) { \
32 ULONG i; \
33 for (i = 0; i < (((SIZ)/4) - 1); i += 2) { \
34 ((PULONG)(BUF))[i] = (MASK); \
35 ((PULONG)(BUF))[i+1] = (ULONG)PsGetCurrentThread(); \
36 } \
37 }
38
39 #define IRP_CONTEXT_HEADER (sizeof( IRP_CONTEXT ) * 0x10000 + FAT_NTC_IRP_CONTEXT)
40
41 //
42 // Local macros.
43 //
44 // Define our lookaside list allocators. For the time being, and perhaps
45 // permanently, the paged structures don't come off of lookasides. This
46 // is due to complications with clean unload as FAT can be in the paging
47 // path, making it really hard to find the right time to empty them.
48 //
49 // Fortunately, the hit rates on the Fcb/Ccb lists weren't stunning.
50 //
51
52 #define FAT_FILL_FREE 0
53
54 #ifdef __REACTOS__
55 static
56 #endif
57 INLINE
58 PCCB
FatAllocateCcb()59 FatAllocateCcb (
60 )
61 {
62 return (PCCB) FsRtlAllocatePoolWithTag( PagedPool, sizeof(CCB), TAG_CCB );
63 }
64
65 #ifdef __REACTOS__
66 static
67 #endif
68 INLINE
69 VOID
FatFreeCcb(IN PCCB Ccb)70 FatFreeCcb (
71 IN PCCB Ccb
72 )
73 {
74 #if FAT_FILL_FREE
75 RtlFillMemoryUlong(Ccb, sizeof(CCB), FAT_FILL_FREE);
76 #endif
77
78 ExFreePool( Ccb );
79 }
80
81 #ifdef __REACTOS__
82 static
83 #endif
84 INLINE
85 PFCB
FatAllocateFcb()86 FatAllocateFcb (
87 )
88 {
89 return (PFCB) FsRtlAllocatePoolWithTag( PagedPool, sizeof(FCB), TAG_FCB );
90 }
91
92 #ifdef __REACTOS__
93 static
94 #endif
95 INLINE
96 VOID
FatFreeFcb(IN PFCB Fcb)97 FatFreeFcb (
98 IN PFCB Fcb
99 )
100 {
101 #if FAT_FILL_FREE
102 RtlFillMemoryUlong(Fcb, sizeof(FCB), FAT_FILL_FREE);
103 #endif
104
105 ExFreePool( Fcb );
106 }
107
108 #ifdef __REACTOS__
109 static
110 #endif
111 INLINE
112 PNON_PAGED_FCB
FatAllocateNonPagedFcb()113 FatAllocateNonPagedFcb (
114 )
115 {
116 return (PNON_PAGED_FCB) ExAllocateFromNPagedLookasideList( &FatNonPagedFcbLookasideList );
117 }
118
119 #ifdef __REACTOS__
120 static
121 #endif
122 INLINE
123 VOID
FatFreeNonPagedFcb(PNON_PAGED_FCB NonPagedFcb)124 FatFreeNonPagedFcb (
125 PNON_PAGED_FCB NonPagedFcb
126 )
127 {
128 #if FAT_FILL_FREE
129 RtlFillMemoryUlong(NonPagedFcb, sizeof(NON_PAGED_FCB), FAT_FILL_FREE);
130 #endif
131
132 ExFreeToNPagedLookasideList( &FatNonPagedFcbLookasideList, (PVOID) NonPagedFcb );
133 }
134
135 #ifdef __REACTOS__
136 static
137 #endif
138 INLINE
139 PERESOURCE
FatAllocateResource()140 FatAllocateResource (
141 )
142 {
143 PERESOURCE Resource;
144
145 Resource = (PERESOURCE) ExAllocateFromNPagedLookasideList( &FatEResourceLookasideList );
146
147 ExInitializeResourceLite( Resource );
148
149 return Resource;
150 }
151
152 #ifdef __REACTOS__
153 static
154 #endif
155 INLINE
156 VOID
FatFreeResource(IN PERESOURCE Resource)157 FatFreeResource (
158 IN PERESOURCE Resource
159 )
160 {
161 ExDeleteResourceLite( Resource );
162
163 #if FAT_FILL_FREE
164 RtlFillMemoryUlong(Resource, sizeof(ERESOURCE), FAT_FILL_FREE);
165 #endif
166
167 ExFreeToNPagedLookasideList( &FatEResourceLookasideList, (PVOID) Resource );
168 }
169
170 #ifdef __REACTOS__
171 static
172 #endif
173 INLINE
174 PIRP_CONTEXT
FatAllocateIrpContext()175 FatAllocateIrpContext (
176 )
177 {
178 return (PIRP_CONTEXT) ExAllocateFromNPagedLookasideList( &FatIrpContextLookasideList );
179 }
180
181 #ifdef __REACTOS__
182 static
183 #endif
184 INLINE
185 VOID
FatFreeIrpContext(IN PIRP_CONTEXT IrpContext)186 FatFreeIrpContext (
187 IN PIRP_CONTEXT IrpContext
188 )
189 {
190 #if FAT_FILL_FREE
191 RtlFillMemoryUlong(IrpContext, sizeof(IRP_CONTEXT), FAT_FILL_FREE);
192 #endif
193
194 ExFreeToNPagedLookasideList( &FatIrpContextLookasideList, (PVOID) IrpContext );
195 }
196
197 #ifdef ALLOC_PRAGMA
198 #pragma alloc_text(PAGE, FatInitializeVcb)
199 #pragma alloc_text(PAGE, FatTearDownVcb)
200 #pragma alloc_text(PAGE, FatDeleteVcb)
201 #pragma alloc_text(PAGE, FatCreateRootDcb)
202 #pragma alloc_text(PAGE, FatCreateFcb)
203 #pragma alloc_text(PAGE, FatCreateDcb)
204 #pragma alloc_text(PAGE, FatDeleteFcb)
205 #pragma alloc_text(PAGE, FatCreateCcb)
206 #pragma alloc_text(PAGE, FatDeallocateCcbStrings)
207 #pragma alloc_text(PAGE, FatDeleteCcb)
208 #pragma alloc_text(PAGE, FatGetNextFcbTopDown)
209 #pragma alloc_text(PAGE, FatGetNextFcbBottomUp)
210 #pragma alloc_text(PAGE, FatConstructNamesInFcb)
211 #pragma alloc_text(PAGE, FatCheckFreeDirentBitmap)
212 #pragma alloc_text(PAGE, FatCreateIrpContext)
213 #pragma alloc_text(PAGE, FatDeleteIrpContext_Real)
214 #pragma alloc_text(PAGE, FatIsHandleCountZero)
215 #pragma alloc_text(PAGE, FatAllocateCloseContext)
216 #pragma alloc_text(PAGE, FatPreallocateCloseContext)
217 #pragma alloc_text(PAGE, FatEnsureStringBufferEnough)
218 #pragma alloc_text(PAGE, FatFreeStringBuffer)
219 #pragma alloc_text(PAGE, FatScanForDataTrack)
220 #endif
221
222
_Requires_lock_held_(_Global_critical_region_)223 _Requires_lock_held_(_Global_critical_region_)
224 VOID
225 FatInitializeVcb (
226 IN PIRP_CONTEXT IrpContext,
227 IN OUT PVCB Vcb,
228 IN PDEVICE_OBJECT TargetDeviceObject,
229 IN PVPB Vpb,
230 IN PDEVICE_OBJECT FsDeviceObject
231 )
232
233 /*++
234
235 Routine Description:
236
237 This routine initializes and inserts a new Vcb record into the in-memory
238 data structure. The Vcb record "hangs" off the end of the Volume device
239 object and must be allocated by our caller.
240
241 Arguments:
242
243 Vcb - Supplies the address of the Vcb record being initialized.
244
245 TargetDeviceObject - Supplies the address of the target device object to
246 associate with the Vcb record.
247
248 Vpb - Supplies the address of the Vpb to associate with the Vcb record.
249
250 FsDeviceObject - The filesystem device object that the mount was directed
251 too.
252
253 Return Value:
254
255 None.
256
257 --*/
258
259 {
260 CC_FILE_SIZES FileSizes;
261 PDEVICE_OBJECT RealDevice;
262 ULONG i;
263
264 STORAGE_HOTPLUG_INFO HotplugInfo;
265 STORAGE_DEVICE_NUMBER StorDeviceNumber;
266 NTSTATUS Status;
267
268 //
269 // The following variables are used for abnormal unwind
270 //
271
272 PLIST_ENTRY UnwindEntryList = NULL;
273 PERESOURCE UnwindResource = NULL;
274 PERESOURCE UnwindResource2 = NULL;
275 PFILE_OBJECT UnwindFileObject = NULL;
276 PFILE_OBJECT UnwindCacheMap = NULL;
277 BOOLEAN UnwindWeAllocatedMcb = FALSE;
278 PFILE_SYSTEM_STATISTICS UnwindStatistics = NULL;
279 BOOLEAN UnwindWeAllocatedBadBlockMap = FALSE;
280 BOOLEAN CloseContextAllocated = FALSE;
281
282 PAGED_CODE();
283 UNREFERENCED_PARAMETER( FsDeviceObject );
284
285 DebugTrace(+1, Dbg, "FatInitializeVcb, Vcb = %p\n", Vcb);
286
287 _SEH2_TRY {
288
289 //
290 // We start by first zeroing out all of the VCB, this will guarantee
291 // that any stale data is wiped clean
292 //
293
294 RtlZeroMemory( Vcb, sizeof(VCB) );
295
296 //
297 // Set the proper node type code and node byte size
298 //
299
300 Vcb->VolumeFileHeader.NodeTypeCode = FAT_NTC_VCB;
301 Vcb->VolumeFileHeader.NodeByteSize = sizeof(VCB);
302
303 //
304 // Initialize the tunneling cache
305 //
306
307 FsRtlInitializeTunnelCache(&Vcb->Tunnel);
308
309 //
310 // Insert this Vcb record on the FatData.VcbQueue
311 //
312
313 NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
314
315
316 #ifdef _MSC_VER
317 #pragma prefast( push )
318 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
319 #pragma prefast( disable: 28193, "this will always wait" )
320 #endif
321
322 (VOID)FatAcquireExclusiveGlobal( IrpContext );
323
324 #ifdef _MSC_VER
325 #pragma prefast( pop )
326 #endif
327
328 InsertTailList( &FatData.VcbQueue, &Vcb->VcbLinks );
329 FatReleaseGlobal( IrpContext );
330 UnwindEntryList = &Vcb->VcbLinks;
331
332 //
333 // Set the Target Device Object, Vpb, and Vcb State fields
334 //
335
336
337 ObReferenceObject( TargetDeviceObject );
338 Vcb->TargetDeviceObject = TargetDeviceObject;
339 Vcb->Vpb = Vpb;
340
341 Vcb->CurrentDevice = Vpb->RealDevice;
342
343 //
344 // Set the removable media and defflush flags based on the storage
345 // inquiry and the old characteristic bits.
346 //
347
348 Status = FatPerformDevIoCtrl( IrpContext,
349 IOCTL_STORAGE_GET_HOTPLUG_INFO,
350 TargetDeviceObject,
351 NULL,
352 0,
353 &HotplugInfo,
354 sizeof(HotplugInfo),
355 FALSE,
356 TRUE,
357 NULL );
358
359 if (NT_SUCCESS( Status )) {
360
361 if (HotplugInfo.MediaRemovable) {
362
363 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA );
364 }
365
366 //
367 // If the media or device is hot-pluggable, then set this flag.
368 //
369
370 if (HotplugInfo.MediaHotplug || HotplugInfo.DeviceHotplug) {
371
372 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_HOTPLUGGABLE );
373 }
374
375 if (!HotplugInfo.WriteCacheEnableOverride) {
376
377 //
378 // If the device or media is hotplug and the override is not
379 // set, force defflush behavior for the device.
380 //
381
382 if (HotplugInfo.MediaHotplug || HotplugInfo.DeviceHotplug) {
383
384 NT_ASSERT( FlagOn( Vcb->VcbState, VCB_STATE_FLAG_HOTPLUGGABLE ));
385
386 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH );
387
388 //
389 // Now, for removables that claim to be lockable, lob a lock
390 // request and see if it works. There can unfortunately be
391 // transient, media dependent reasons that it can fail. If
392 // it does not, we must force defflush on.
393 //
394
395 } else if (HotplugInfo.MediaRemovable &&
396 !HotplugInfo.MediaHotplug) {
397
398 Status = FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE );
399
400 if (!NT_SUCCESS( Status )) {
401
402 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH );
403
404 }
405
406 (VOID)FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE );
407 }
408 }
409 }
410
411 if (FlagOn(Vpb->RealDevice->Characteristics, FILE_REMOVABLE_MEDIA)) {
412
413 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA );
414 }
415
416 //
417 // Make sure we turn on deferred flushing for floppies like we always
418 // have.
419 //
420
421 if (FlagOn(Vpb->RealDevice->Characteristics, FILE_FLOPPY_DISKETTE)) {
422
423 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH );
424 }
425
426 //
427 // Query the storage device number.
428 //
429
430 Status = FatPerformDevIoCtrl( IrpContext,
431 IOCTL_STORAGE_GET_DEVICE_NUMBER,
432 TargetDeviceObject,
433 NULL,
434 0,
435 &StorDeviceNumber,
436 sizeof(StorDeviceNumber),
437 FALSE,
438 TRUE,
439 NULL );
440
441 if (NT_SUCCESS( Status )) {
442
443 Vcb->DeviceNumber = StorDeviceNumber.DeviceNumber;
444
445 } else {
446
447 Vcb->DeviceNumber = (ULONG)(-1);
448 }
449
450 FatSetVcbCondition( Vcb, VcbGood);
451
452 //
453 // Initialize the resource variable for the Vcb
454 //
455
456 ExInitializeResourceLite( &Vcb->Resource );
457 UnwindResource = &Vcb->Resource;
458
459 ExInitializeResourceLite( &Vcb->ChangeBitMapResource );
460 UnwindResource2 = &Vcb->ChangeBitMapResource;
461
462 //
463 // Initialize the free cluster bitmap mutex.
464 //
465
466 ExInitializeFastMutex( &Vcb->FreeClusterBitMapMutex );
467
468 //
469 // Create the special file object for the virtual volume file with a close
470 // context, its pointers back to the Vcb and the section object pointer.
471 //
472 // We don't have to unwind the close context. That will happen in the close
473 // path automatically.
474 //
475
476 RealDevice = Vcb->CurrentDevice;
477
478 FatPreallocateCloseContext(Vcb);
479 CloseContextAllocated = TRUE;
480
481 Vcb->VirtualVolumeFile = UnwindFileObject = IoCreateStreamFileObject( NULL, RealDevice );
482
483 FatSetFileObject( Vcb->VirtualVolumeFile,
484 VirtualVolumeFile,
485 Vcb,
486 NULL );
487
488 //
489 // Remember this internal, residual open.
490 //
491
492 InterlockedIncrement( (LONG*)&(Vcb->InternalOpenCount) );
493 InterlockedIncrement( (LONG*)&(Vcb->ResidualOpenCount) );
494
495 Vcb->VirtualVolumeFile->SectionObjectPointer = &Vcb->SectionObjectPointers;
496
497 Vcb->VirtualVolumeFile->ReadAccess = TRUE;
498 Vcb->VirtualVolumeFile->WriteAccess = TRUE;
499 Vcb->VirtualVolumeFile->DeleteAccess = TRUE;
500
501 //
502 // Initialize the notify structures.
503 //
504
505 InitializeListHead( &Vcb->DirNotifyList );
506
507 FsRtlNotifyInitializeSync( &Vcb->NotifySync );
508
509 //
510 // Initialize the Cache Map for the volume file. The size is
511 // initially set to that of our first read. It will be extended
512 // when we know how big the Fat is.
513 //
514
515 FileSizes.AllocationSize.QuadPart =
516 FileSizes.FileSize.QuadPart = sizeof(PACKED_BOOT_SECTOR);
517 FileSizes.ValidDataLength = FatMaxLarge;
518
519 FatInitializeCacheMap( Vcb->VirtualVolumeFile,
520 &FileSizes,
521 TRUE,
522 &FatData.CacheManagerNoOpCallbacks,
523 Vcb );
524
525 UnwindCacheMap = Vcb->VirtualVolumeFile;
526
527 //
528 // Initialize the structure that will keep track of dirty fat sectors.
529 // The largest possible Mcb structures are less than 1K, so we use
530 // non paged pool.
531 //
532
533 FsRtlInitializeLargeMcb( &Vcb->DirtyFatMcb, PagedPool );
534
535 UnwindWeAllocatedMcb = TRUE;
536
537 //
538 // Initialize the structure that will keep track of bad clusters on the volume.
539 //
540 // It will be empty until it is populated by FSCTL_GET_RETRIEVAL_POINTERS with a volume handle.
541 //
542
543 FsRtlInitializeLargeMcb( &Vcb->BadBlockMcb, PagedPool );
544 UnwindWeAllocatedBadBlockMap = TRUE;
545
546 //
547 // Set the cluster index hint to the first valid cluster of a fat: 2
548 //
549
550 Vcb->ClusterHint = 2;
551
552 //
553 // Initialize the directory stream file object creation event.
554 // This event is also "borrowed" for async non-cached writes.
555 //
556
557 ExInitializeFastMutex( &Vcb->DirectoryFileCreationMutex );
558
559 //
560 // Initialize the clean volume callback Timer and DPC.
561 //
562
563 KeInitializeTimer( &Vcb->CleanVolumeTimer );
564
565 KeInitializeDpc( &Vcb->CleanVolumeDpc, FatCleanVolumeDpc, Vcb );
566
567 //
568 // Initialize the performance counters.
569 //
570
571 Vcb->Statistics = FsRtlAllocatePoolWithTag( NonPagedPoolNx,
572 sizeof(FILE_SYSTEM_STATISTICS) * FatData.NumberProcessors,
573 TAG_VCB_STATS );
574 UnwindStatistics = Vcb->Statistics;
575
576 RtlZeroMemory( Vcb->Statistics, sizeof(FILE_SYSTEM_STATISTICS) * FatData.NumberProcessors );
577
578 for (i = 0; i < FatData.NumberProcessors; i += 1) {
579 Vcb->Statistics[i].Common.FileSystemType = FILESYSTEM_STATISTICS_TYPE_FAT;
580 Vcb->Statistics[i].Common.Version = 1;
581 Vcb->Statistics[i].Common.SizeOfCompleteStructure =
582 sizeof(FILE_SYSTEM_STATISTICS);
583 }
584
585 //
586 // Pick up a VPB right now so we know we can pull this filesystem stack off
587 // of the storage stack on demand.
588 //
589
590 Vcb->SwapVpb = FsRtlAllocatePoolWithTag( NonPagedPoolNx,
591 sizeof( VPB ),
592 TAG_VPB );
593
594 RtlZeroMemory( Vcb->SwapVpb, sizeof( VPB ) );
595
596 //
597 // Initialize the close queue listheads.
598 //
599
600 InitializeListHead( &Vcb->AsyncCloseList );
601 InitializeListHead( &Vcb->DelayedCloseList );
602
603 //
604 // Initialize the Advanced FCB Header
605 //
606
607 ExInitializeFastMutex( &Vcb->AdvancedFcbHeaderMutex );
608 FsRtlSetupAdvancedHeader( &Vcb->VolumeFileHeader,
609 &Vcb->AdvancedFcbHeaderMutex );
610
611
612 //
613 // With the Vcb now set up, set the IrpContext Vcb field.
614 //
615
616 IrpContext->Vcb = Vcb;
617
618 } _SEH2_FINALLY {
619
620 DebugUnwind( FatInitializeVcb );
621
622 //
623 // If this is an abnormal termination then undo our work
624 //
625
626 if (_SEH2_AbnormalTermination()) {
627
628 if (UnwindCacheMap != NULL) { FatSyncUninitializeCacheMap( IrpContext, UnwindCacheMap ); }
629 if (UnwindFileObject != NULL) { ObDereferenceObject( UnwindFileObject ); }
630 if (UnwindResource != NULL) { FatDeleteResource( UnwindResource ); }
631 if (UnwindResource2 != NULL) { FatDeleteResource( UnwindResource2 ); }
632 if (UnwindWeAllocatedMcb) { FsRtlUninitializeLargeMcb( &Vcb->DirtyFatMcb ); }
633 if (UnwindWeAllocatedBadBlockMap) { FsRtlUninitializeLargeMcb(&Vcb->BadBlockMcb ); }
634 if (UnwindEntryList != NULL) {
635 #ifdef _MSC_VER
636 #pragma prefast( suppress: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
637 #endif
638 (VOID)FatAcquireExclusiveGlobal( IrpContext );
639 RemoveEntryList( UnwindEntryList );
640 FatReleaseGlobal( IrpContext );
641 }
642 if (UnwindStatistics != NULL) { ExFreePool( UnwindStatistics ); }
643
644 //
645 // Cleanup the close context we preallocated above.
646 //
647
648 if (CloseContextAllocated && (Vcb->VirtualVolumeFile == NULL)) {
649
650 //
651 // FatAllocateCloseContext does not allocate memory, it
652 // pulls a close context off the preallocated slist queue.
653 //
654 // Doing this here is necessary to balance out the one we
655 // preallocated for the Vcb earlier in this function, but
656 // only if we failed to create the virtual volume file.
657 //
658 // If VirtualVolumeFile is not NULL, then this CloseContext
659 // will get cleaned up when the close comes in for it during
660 // Vcb teardown.
661 //
662
663 PCLOSE_CONTEXT CloseContext = FatAllocateCloseContext(Vcb);
664
665 ExFreePool( CloseContext );
666 CloseContextAllocated = FALSE;
667 }
668 }
669
670 DebugTrace(-1, Dbg, "FatInitializeVcb -> VOID\n", 0);
671 } _SEH2_END;
672
673 //
674 // and return to our caller
675 //
676
677 UNREFERENCED_PARAMETER( IrpContext );
678
679 return;
680 }
681
682
683 VOID
FatTearDownVcb(IN PIRP_CONTEXT IrpContext,IN PVCB Vcb)684 FatTearDownVcb (
685 IN PIRP_CONTEXT IrpContext,
686 IN PVCB Vcb
687 )
688
689 /*++
690
691 Routine Description:
692
693 This routine tries to remove all internal opens from the volume.
694
695 Arguments:
696
697 IrpContext - Supplies the context for the overall request.
698
699 Vcb - Supplies the Vcb to be torn down.
700
701 Return Value:
702
703 None
704
705 --*/
706
707 {
708 PFILE_OBJECT DirectoryFileObject;
709
710
711 PAGED_CODE();
712
713 //
714 // Get rid of the virtual volume file, if we need to.
715 //
716
717 if (Vcb->VirtualVolumeFile != NULL) {
718
719 //
720 // Uninitialize the cache
721 //
722
723 CcUninitializeCacheMap( Vcb->VirtualVolumeFile,
724 &FatLargeZero,
725 NULL );
726
727 FsRtlTeardownPerStreamContexts( &Vcb->VolumeFileHeader );
728
729 ObDereferenceObject( Vcb->VirtualVolumeFile );
730
731 Vcb->VirtualVolumeFile = NULL;
732 }
733
734 //
735 // Close down the EA file.
736 //
737
738 FatCloseEaFile( IrpContext, Vcb, FALSE );
739
740 //
741 // Close down the root directory stream..
742 //
743
744 if (Vcb->RootDcb != NULL) {
745
746 DirectoryFileObject = Vcb->RootDcb->Specific.Dcb.DirectoryFile;
747
748 if (DirectoryFileObject != NULL) {
749
750 //
751 // Tear down this directory file.
752 //
753
754 CcUninitializeCacheMap( DirectoryFileObject,
755 &FatLargeZero,
756 NULL );
757
758 Vcb->RootDcb->Specific.Dcb.DirectoryFile = NULL;
759 ObDereferenceObject( DirectoryFileObject );
760 }
761 }
762
763 //
764 // The VCB can no longer be used.
765 //
766
767 FatSetVcbCondition( Vcb, VcbBad );
768 }
769
770
771 VOID
FatDeleteVcb(IN PIRP_CONTEXT IrpContext,IN PVCB Vcb)772 FatDeleteVcb (
773 IN PIRP_CONTEXT IrpContext,
774 IN PVCB Vcb
775 )
776
777 /*++
778
779 Routine Description:
780
781 This routine removes the Vcb record from Fat's in-memory data
782 structures. It also will remove all associated underlings
783 (i.e., FCB records).
784
785 Arguments:
786
787 Vcb - Supplies the Vcb to be removed
788
789 Return Value:
790
791 None
792
793 --*/
794
795 {
796 PFCB Fcb;
797
798 PAGED_CODE();
799
800 DebugTrace(+1, Dbg, "FatDeleteVcb, Vcb = %p\n", Vcb);
801
802 //
803 // If the IrpContext points to the VCB being deleted NULL out the stail
804 // pointer.
805 //
806
807 if (IrpContext->Vcb == Vcb) {
808
809 IrpContext->Vcb = NULL;
810
811 }
812
813 //
814 // Chuck the backpocket Vpb we kept just in case.
815 //
816
817 if (Vcb->SwapVpb) {
818
819 ExFreePool( Vcb->SwapVpb );
820
821 }
822
823 //
824 // Free the VPB, if we need to.
825 //
826
827 if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED )) {
828
829 //
830 // We swapped the VPB, so we need to free the main one.
831 //
832
833 ExFreePool( Vcb->Vpb );
834 }
835
836 if (Vcb->VolumeGuidPath.Buffer) {
837 ExFreePool( Vcb->VolumeGuidPath.Buffer );
838 Vcb->VolumeGuidPath.Buffer = NULL;
839 }
840
841 //
842 // Remove this record from the global list of all Vcb records.
843 // Note that the global lock must already be held when calling
844 // this function.
845 //
846
847 RemoveEntryList( &(Vcb->VcbLinks) );
848
849 //
850 // Make sure the direct access open count is zero, and the open file count
851 // is also zero.
852 //
853
854 if ((Vcb->DirectAccessOpenCount != 0) || (Vcb->OpenFileCount != 0)) {
855
856 #ifdef _MSC_VER
857 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
858 #endif
859 FatBugCheck( 0, 0, 0 );
860 }
861
862 //
863 // Remove the EaFcb and dereference the Fcb for the Ea file if it
864 // exists.
865 //
866
867 if (Vcb->EaFcb != NULL) {
868
869 Vcb->EaFcb->OpenCount = 0;
870 FatDeleteFcb( IrpContext, &Vcb->EaFcb );
871 }
872
873 //
874 // Remove the Root Dcb
875 //
876
877 if (Vcb->RootDcb != NULL) {
878
879 //
880 // Rundown stale child Fcbs that may be hanging around. Yes, this
881 // can happen. No, the create path isn't perfectly defensive about
882 // tearing down branches built up on creates that don't wind up
883 // succeeding. Normal system operation usually winds up having
884 // cleaned them out through re-visiting, but ...
885 //
886 // Just pick off Fcbs from the bottom of the tree until we run out.
887 // Then we delete the root Dcb.
888 //
889
890 while( (Fcb = FatGetNextFcbBottomUp( IrpContext, NULL, Vcb->RootDcb )) != Vcb->RootDcb ) {
891
892 FatDeleteFcb( IrpContext, &Fcb );
893 }
894
895 FatDeleteFcb( IrpContext, &Vcb->RootDcb );
896 }
897
898 //
899 // Uninitialize the notify sychronization object.
900 //
901
902 FsRtlNotifyUninitializeSync( &Vcb->NotifySync );
903
904 //
905 // Uninitialize the resource variable for the Vcb
906 //
907
908 FatDeleteResource( &Vcb->Resource );
909 FatDeleteResource( &Vcb->ChangeBitMapResource );
910
911 //
912 // If allocation support has been setup, free it.
913 //
914
915 if (Vcb->FreeClusterBitMap.Buffer != NULL) {
916
917 FatTearDownAllocationSupport( IrpContext, Vcb );
918 }
919
920 //
921 // UnInitialize the Mcb structure that kept track of dirty fat sectors.
922 //
923
924 FsRtlUninitializeLargeMcb( &Vcb->DirtyFatMcb );
925
926 //
927 // Uninitialize the Mcb structure that kept track of bad sectors.
928 //
929
930 FsRtlUninitializeLargeMcb( &Vcb->BadBlockMcb );
931
932 //
933 // Free the pool for the stached copy of the boot sector
934 //
935
936 if ( Vcb->First0x24BytesOfBootSector ) {
937
938 ExFreePool( Vcb->First0x24BytesOfBootSector );
939 Vcb->First0x24BytesOfBootSector = NULL;
940 }
941
942 //
943 // Cancel the CleanVolume Timer and Dpc
944 //
945
946 (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer );
947
948 (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc );
949
950 //
951 // Free the performance counters memory
952 //
953
954 ExFreePool( Vcb->Statistics );
955
956 //
957 // Clean out the tunneling cache
958 //
959
960 FsRtlDeleteTunnelCache(&Vcb->Tunnel);
961
962 //
963 // Dereference the target device object.
964 //
965
966 ObDereferenceObject( Vcb->TargetDeviceObject );
967
968 //
969 // We better have used all the close contexts we allocated. There could be
970 // one remaining if we're doing teardown due to a final close coming in on
971 // a directory file stream object. It will be freed on the way back up.
972 //
973
974 NT_ASSERT( Vcb->CloseContextCount <= 1);
975
976 //
977 // And zero out the Vcb, this will help ensure that any stale data is
978 // wiped clean
979 //
980
981 RtlZeroMemory( Vcb, sizeof(VCB) );
982
983 //
984 // return and tell the caller
985 //
986
987 DebugTrace(-1, Dbg, "FatDeleteVcb -> VOID\n", 0);
988
989 return;
990 }
991
992
_Requires_lock_held_(_Global_critical_region_)993 _Requires_lock_held_(_Global_critical_region_)
994 VOID
995 FatCreateRootDcb (
996 IN PIRP_CONTEXT IrpContext,
997 IN PVCB Vcb
998 )
999
1000 /*++
1001
1002 Routine Description:
1003
1004 This routine allocates, initializes, and inserts a new root DCB record
1005 into the in memory data structure.
1006
1007 Arguments:
1008
1009 Vcb - Supplies the Vcb to associate the new DCB under
1010
1011 Return Value:
1012
1013 None. The Vcb is modified in-place.
1014
1015 --*/
1016
1017 {
1018 PDCB Dcb = NULL;
1019
1020 //
1021 // The following variables are used for abnormal unwind
1022 //
1023
1024 PVOID UnwindStorage[2] = { NULL, NULL };
1025 PERESOURCE UnwindResource = NULL;
1026 PERESOURCE UnwindResource2 = NULL;
1027 PLARGE_MCB UnwindMcb = NULL;
1028 PFILE_OBJECT UnwindFileObject = NULL;
1029
1030 PAGED_CODE();
1031
1032 DebugTrace(+1, Dbg, "FatCreateRootDcb, Vcb = %p\n", Vcb);
1033
1034 _SEH2_TRY {
1035
1036 //
1037 // Make sure we don't already have a root dcb for this vcb
1038 //
1039
1040 if (Vcb->RootDcb != NULL) {
1041
1042 DebugDump("Error trying to create multiple root dcbs\n", 0, Vcb);
1043 #ifdef _MSC_VER
1044 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
1045 #endif
1046 FatBugCheck( 0, 0, 0 );
1047 }
1048
1049 //
1050 // Allocate a new DCB and zero it out, we use Dcb locally so we don't
1051 // have to continually reference through the Vcb
1052 //
1053
1054 UnwindStorage[0] = Dcb = Vcb->RootDcb = FsRtlAllocatePoolWithTag( NonPagedPoolNx,
1055 sizeof(DCB),
1056 TAG_FCB );
1057
1058 RtlZeroMemory( Dcb, sizeof(DCB));
1059
1060 UnwindStorage[1] =
1061 Dcb->NonPaged = FatAllocateNonPagedFcb();
1062
1063 RtlZeroMemory( Dcb->NonPaged, sizeof( NON_PAGED_FCB ) );
1064
1065 //
1066 // Set the proper node type code, node byte size, and call backs
1067 //
1068
1069 Dcb->Header.NodeTypeCode = FAT_NTC_ROOT_DCB;
1070 Dcb->Header.NodeByteSize = sizeof(DCB);
1071
1072 Dcb->FcbCondition = FcbGood;
1073
1074 //
1075 // The parent Dcb, initial state, open count, dirent location
1076 // information, and directory change count fields are already zero so
1077 // we can skip setting them
1078 //
1079
1080 //
1081 // Initialize the resource variable
1082 //
1083
1084 UnwindResource =
1085 Dcb->Header.Resource = FatAllocateResource();
1086
1087 //
1088 // Initialize the PagingIo Resource. We no longer use the FsRtl common
1089 // shared pool because this led to a) deadlocks due to cases where files
1090 // and their parent directories shared a resource and b) there is no way
1091 // to anticipate inter-driver induced deadlock via recursive operation.
1092 //
1093
1094 UnwindResource2 =
1095 Dcb->Header.PagingIoResource = FatAllocateResource();
1096
1097 //
1098 // The root Dcb has an empty parent dcb links field
1099 //
1100
1101 InitializeListHead( &Dcb->ParentDcbLinks );
1102
1103 //
1104 // Set the Vcb
1105 //
1106
1107 Dcb->Vcb = Vcb;
1108
1109 //
1110 // initialize the parent dcb queue.
1111 //
1112
1113 InitializeListHead( &Dcb->Specific.Dcb.ParentDcbQueue );
1114
1115 //
1116 // Set the full file name up.
1117 //
1118
1119 Dcb->FullFileName.Buffer = L"\\";
1120 Dcb->FullFileName.Length = (USHORT)2;
1121 Dcb->FullFileName.MaximumLength = (USHORT)4;
1122
1123 Dcb->ShortName.Name.Oem.Buffer = "\\";
1124 Dcb->ShortName.Name.Oem.Length = (USHORT)1;
1125 Dcb->ShortName.Name.Oem.MaximumLength = (USHORT)2;
1126
1127 //
1128 // Construct a lie about file properties since we don't
1129 // have a proper "." entry to look at.
1130 //
1131
1132 Dcb->DirentFatFlags = FILE_ATTRIBUTE_DIRECTORY;
1133
1134 //
1135 // Initialize Advanced FCB Header fields
1136 //
1137
1138 ExInitializeFastMutex( &Dcb->NonPaged->AdvancedFcbHeaderMutex );
1139 FsRtlSetupAdvancedHeader( &Dcb->Header,
1140 &Dcb->NonPaged->AdvancedFcbHeaderMutex );
1141
1142 //
1143 // Initialize the Mcb, and setup its mapping. Note that the root
1144 // directory is a fixed size so we can set it everything up now.
1145 //
1146
1147 FsRtlInitializeLargeMcb( &Dcb->Mcb, NonPagedPoolNx );
1148 UnwindMcb = &Dcb->Mcb;
1149
1150 if (FatIsFat32(Vcb)) {
1151
1152 //
1153 // The first cluster of fat32 roots comes from the BPB
1154 //
1155
1156 Dcb->FirstClusterOfFile = Vcb->Bpb.RootDirFirstCluster;
1157
1158 } else {
1159
1160 FatAddMcbEntry( Vcb, &Dcb->Mcb,
1161 0,
1162 FatRootDirectoryLbo( &Vcb->Bpb ),
1163 FatRootDirectorySize( &Vcb->Bpb ));
1164 }
1165
1166 if (FatIsFat32(Vcb)) {
1167
1168 //
1169 // Find the size of the fat32 root. As a side-effect, this will create
1170 // MCBs for the entire root. In the process of doing this, we may
1171 // discover that the FAT chain is bogus and raise corruption.
1172 //
1173
1174 Dcb->Header.AllocationSize.LowPart = 0xFFFFFFFF;
1175 FatLookupFileAllocationSize( IrpContext, Dcb);
1176
1177 Dcb->Header.FileSize.QuadPart =
1178 Dcb->Header.AllocationSize.QuadPart;
1179 } else {
1180
1181 //
1182 // set the allocation size to real size of the root directory
1183 //
1184
1185 Dcb->Header.FileSize.QuadPart =
1186 Dcb->Header.AllocationSize.QuadPart = FatRootDirectorySize( &Vcb->Bpb );
1187
1188 }
1189
1190 //
1191 // Set our two create dirent aids to represent that we have yet to
1192 // enumerate the directory for never used or deleted dirents.
1193 //
1194
1195 Dcb->Specific.Dcb.UnusedDirentVbo = 0xffffffff;
1196 Dcb->Specific.Dcb.DeletedDirentHint = 0xffffffff;
1197
1198 //
1199 // Setup the free dirent bitmap buffer.
1200 //
1201
1202 RtlInitializeBitMap( &Dcb->Specific.Dcb.FreeDirentBitmap,
1203 NULL,
1204 0 );
1205
1206 FatCheckFreeDirentBitmap( IrpContext, Dcb );
1207
1208 #if (NTDDI_VERSION >= NTDDI_WIN8)
1209 //
1210 // Initialize the oplock structure.
1211 //
1212
1213 FsRtlInitializeOplock( FatGetFcbOplock(Dcb) );
1214 #endif
1215
1216 } _SEH2_FINALLY {
1217
1218 DebugUnwind( FatCreateRootDcb );
1219
1220 //
1221 // If this is an abnormal termination then undo our work
1222 //
1223
1224 if (_SEH2_AbnormalTermination()) {
1225
1226 ULONG i;
1227
1228 if (UnwindFileObject != NULL) { ObDereferenceObject( UnwindFileObject ); }
1229 if (UnwindMcb != NULL) { FsRtlUninitializeLargeMcb( UnwindMcb ); }
1230 if (UnwindResource != NULL) { FatFreeResource( UnwindResource ); }
1231 if (UnwindResource2 != NULL) { FatFreeResource( UnwindResource2 ); }
1232
1233 for (i = 0; i < sizeof(UnwindStorage)/sizeof(PVOID); i += 1) {
1234 if (UnwindStorage[i] != NULL) { ExFreePool( UnwindStorage[i] ); }
1235 }
1236
1237 //
1238 // Re-zero the entry in the Vcb.
1239 //
1240
1241 Vcb->RootDcb = NULL;
1242 }
1243
1244 DebugTrace(-1, Dbg, "FatCreateRootDcb -> %p\n", Dcb);
1245 } _SEH2_END;
1246
1247 return;
1248 }
1249
1250
1251 PFCB
FatCreateFcb(IN PIRP_CONTEXT IrpContext,IN PVCB Vcb,IN PDCB ParentDcb,IN ULONG LfnOffsetWithinDirectory,IN ULONG DirentOffsetWithinDirectory,IN PDIRENT Dirent,IN PUNICODE_STRING Lfn OPTIONAL,IN PUNICODE_STRING OrigLfn OPTIONAL,IN BOOLEAN IsPagingFile,IN BOOLEAN SingleResource)1252 FatCreateFcb (
1253 IN PIRP_CONTEXT IrpContext,
1254 IN PVCB Vcb,
1255 IN PDCB ParentDcb,
1256 IN ULONG LfnOffsetWithinDirectory,
1257 IN ULONG DirentOffsetWithinDirectory,
1258 IN PDIRENT Dirent,
1259 IN PUNICODE_STRING Lfn OPTIONAL,
1260 IN PUNICODE_STRING OrigLfn OPTIONAL,
1261 IN BOOLEAN IsPagingFile,
1262 IN BOOLEAN SingleResource
1263 )
1264
1265 /*++
1266
1267 Routine Description:
1268
1269 This routine allocates, initializes, and inserts a new Fcb record into
1270 the in-memory data structures.
1271
1272 Arguments:
1273
1274 Vcb - Supplies the Vcb to associate the new FCB under.
1275
1276 ParentDcb - Supplies the parent dcb that the new FCB is under.
1277
1278 LfnOffsetWithinDirectory - Supplies the offset of the LFN. If there is
1279 no LFN associated with this file then this value is same as
1280 DirentOffsetWithinDirectory.
1281
1282 DirentOffsetWithinDirectory - Supplies the offset, in bytes from the
1283 start of the directory file where the dirent for the fcb is located
1284
1285 Dirent - Supplies the dirent for the fcb being created
1286
1287 Lfn - Supplies a long UNICODE name associated with this file.
1288
1289 IsPagingFile - Indicates if we are creating an FCB for a paging file
1290 or some other type of file.
1291
1292 SingleResource - Indicates if this Fcb should share a single resource
1293 as both main and paging.
1294
1295 Return Value:
1296
1297 PFCB - Returns a pointer to the newly allocated FCB
1298
1299 --*/
1300
1301 {
1302 PFCB Fcb = NULL;
1303 POOL_TYPE PoolType;
1304
1305 //
1306 // The following variables are used for abnormal unwind
1307 //
1308
1309 PVOID UnwindStorage[2] = { NULL, NULL };
1310 PERESOURCE UnwindResource = NULL;
1311 PERESOURCE UnwindResource2 = NULL;
1312 PLIST_ENTRY UnwindEntryList = NULL;
1313 PLARGE_MCB UnwindMcb = NULL;
1314 PFILE_LOCK UnwindFileLock = NULL;
1315 POPLOCK UnwindOplock = NULL;
1316
1317 PAGED_CODE();
1318
1319 UNREFERENCED_PARAMETER( OrigLfn );
1320
1321 DebugTrace(+1, Dbg, "FatCreateFcb\n", 0);
1322
1323 _SEH2_TRY {
1324
1325 //
1326 // Determine the pool type we should be using for the fcb and the
1327 // mcb structure
1328 //
1329
1330 if (IsPagingFile) {
1331
1332 PoolType = NonPagedPoolNx;
1333 Fcb = UnwindStorage[0] = FsRtlAllocatePoolWithTag( NonPagedPoolNx,
1334 sizeof(FCB),
1335 TAG_FCB );
1336 } else {
1337
1338 PoolType = PagedPool;
1339 Fcb = UnwindStorage[0] = FatAllocateFcb();
1340
1341 }
1342
1343 //
1344 // ... and zero it out
1345 //
1346
1347 RtlZeroMemory( Fcb, sizeof(FCB) );
1348
1349 UnwindStorage[1] =
1350 Fcb->NonPaged = FatAllocateNonPagedFcb();
1351
1352 RtlZeroMemory( Fcb->NonPaged, sizeof( NON_PAGED_FCB ) );
1353
1354 //
1355 // Set the proper node type code, node byte size, and call backs
1356 //
1357
1358 Fcb->Header.NodeTypeCode = FAT_NTC_FCB;
1359 Fcb->Header.NodeByteSize = sizeof(FCB);
1360
1361 Fcb->FcbCondition = FcbGood;
1362
1363 //
1364 // Check to see if we need to set the Fcb state to indicate that this
1365 // is a paging/system file. This will prevent it from being opened
1366 // again.
1367 //
1368
1369 if (IsPagingFile) {
1370
1371 SetFlag( Fcb->FcbState, FCB_STATE_PAGING_FILE | FCB_STATE_SYSTEM_FILE );
1372 }
1373
1374 //
1375 // The initial state, open count, and segment objects fields are already
1376 // zero so we can skip setting them
1377 //
1378
1379 //
1380 // Initialize the resource variable
1381 //
1382
1383
1384 UnwindResource =
1385 Fcb->Header.Resource = FatAllocateResource();
1386
1387 //
1388 // Initialize the PagingIo Resource. We no longer use the FsRtl common
1389 // shared pool because this led to a) deadlocks due to cases where files
1390 // and their parent directories shared a resource and b) there is no way
1391 // to anticipate inter-driver induced deadlock via recursive operation.
1392 //
1393
1394 if (SingleResource) {
1395
1396 Fcb->Header.PagingIoResource = Fcb->Header.Resource;
1397
1398 } else {
1399
1400 UnwindResource2 =
1401 Fcb->Header.PagingIoResource = FatAllocateResource();
1402 }
1403
1404 //
1405 // Insert this fcb into our parent dcb's queue.
1406 //
1407 // There is a deep reason why this goes on the tail, to allow us
1408 // to easily enumerate all child directories before child files.
1409 // This is important to let us maintain whole-volume lockorder
1410 // via BottomUp enumeration.
1411 //
1412
1413 InsertTailList( &ParentDcb->Specific.Dcb.ParentDcbQueue,
1414 &Fcb->ParentDcbLinks );
1415 UnwindEntryList = &Fcb->ParentDcbLinks;
1416
1417 //
1418 // Point back to our parent dcb
1419 //
1420
1421 Fcb->ParentDcb = ParentDcb;
1422
1423 //
1424 // Set the Vcb
1425 //
1426
1427 Fcb->Vcb = Vcb;
1428
1429 //
1430 // Set the dirent offset within the directory
1431 //
1432
1433 Fcb->LfnOffsetWithinDirectory = LfnOffsetWithinDirectory;
1434 Fcb->DirentOffsetWithinDirectory = DirentOffsetWithinDirectory;
1435
1436 //
1437 // Set the DirentFatFlags and LastWriteTime
1438 //
1439
1440 Fcb->DirentFatFlags = Dirent->Attributes;
1441
1442 Fcb->LastWriteTime = FatFatTimeToNtTime( IrpContext,
1443 Dirent->LastWriteTime,
1444 0 );
1445
1446 //
1447 // These fields are only non-zero when in Chicago mode.
1448 //
1449
1450 if (FatData.ChicagoMode) {
1451
1452 LARGE_INTEGER FatSystemJanOne1980 = {0};
1453
1454 //
1455 // If either date is possibly zero, get the system
1456 // version of 1/1/80.
1457 //
1458
1459 if ((((PUSHORT)Dirent)[9] & ((PUSHORT)Dirent)[8]) == 0) {
1460
1461 ExLocalTimeToSystemTime( &FatJanOne1980,
1462 &FatSystemJanOne1980 );
1463 }
1464
1465 //
1466 // Only do the really hard work if this field is non-zero.
1467 //
1468
1469 if (((PUSHORT)Dirent)[9] != 0) {
1470
1471 Fcb->LastAccessTime =
1472 FatFatDateToNtTime( IrpContext,
1473 Dirent->LastAccessDate );
1474
1475 } else {
1476
1477 Fcb->LastAccessTime = FatSystemJanOne1980;
1478 }
1479
1480 //
1481 // Only do the really hard work if this field is non-zero.
1482 //
1483
1484 if (((PUSHORT)Dirent)[8] != 0) {
1485
1486 Fcb->CreationTime =
1487 FatFatTimeToNtTime( IrpContext,
1488 Dirent->CreationTime,
1489 Dirent->CreationMSec );
1490
1491 } else {
1492
1493 Fcb->CreationTime = FatSystemJanOne1980;
1494 }
1495 }
1496
1497 //
1498 // Initialize Advanced FCB Header fields
1499 //
1500
1501 ExInitializeFastMutex( &Fcb->NonPaged->AdvancedFcbHeaderMutex );
1502 FsRtlSetupAdvancedHeader( &Fcb->Header,
1503 &Fcb->NonPaged->AdvancedFcbHeaderMutex );
1504
1505 //
1506 // To make FAT match the present functionality of NTFS, disable
1507 // stream contexts on paging files
1508 //
1509
1510 if (IsPagingFile) {
1511
1512 SetFlag( Fcb->Header.Flags2, FSRTL_FLAG2_IS_PAGING_FILE );
1513 ClearFlag( Fcb->Header.Flags2, FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS );
1514 }
1515
1516 //
1517 // Initialize the Mcb
1518 //
1519
1520 FsRtlInitializeLargeMcb( &Fcb->Mcb, PoolType );
1521 UnwindMcb = &Fcb->Mcb;
1522
1523 //
1524 // Set the file size, valid data length, first cluster of file,
1525 // and allocation size based on the information stored in the dirent
1526 //
1527
1528 Fcb->Header.FileSize.LowPart = Dirent->FileSize;
1529
1530 Fcb->Header.ValidDataLength.LowPart = Dirent->FileSize;
1531
1532 Fcb->ValidDataToDisk = Dirent->FileSize;
1533
1534 Fcb->FirstClusterOfFile = (ULONG)Dirent->FirstClusterOfFile;
1535
1536 if ( FatIsFat32(Vcb) ) {
1537
1538 Fcb->FirstClusterOfFile += Dirent->FirstClusterOfFileHi << 16;
1539 }
1540
1541 if ( Fcb->FirstClusterOfFile == 0 ) {
1542
1543 Fcb->Header.AllocationSize.QuadPart = 0;
1544
1545 } else {
1546
1547 Fcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT;
1548 }
1549
1550
1551 //
1552 // Initialize the Fcb's file lock record
1553 //
1554
1555 FsRtlInitializeFileLock( &Fcb->Specific.Fcb.FileLock, NULL, NULL );
1556 UnwindFileLock = &Fcb->Specific.Fcb.FileLock;
1557
1558 //
1559 // Initialize the oplock structure.
1560 //
1561
1562 FsRtlInitializeOplock( FatGetFcbOplock(Fcb) );
1563 UnwindOplock = FatGetFcbOplock(Fcb);
1564
1565 //
1566 // Indicate that Fast I/O is possible
1567 //
1568
1569 Fcb->Header.IsFastIoPossible = TRUE;
1570
1571 //
1572 // Set the file names. This must be the last thing we do.
1573 //
1574
1575 FatConstructNamesInFcb( IrpContext,
1576 Fcb,
1577 Dirent,
1578 Lfn );
1579
1580 //
1581 // Drop the shortname hint so prefix searches can figure out
1582 // what they found
1583 //
1584
1585 Fcb->ShortName.FileNameDos = TRUE;
1586
1587 } _SEH2_FINALLY {
1588
1589 DebugUnwind( FatCreateFcb );
1590
1591 //
1592 // If this is an abnormal termination then undo our work
1593 //
1594
1595 if (_SEH2_AbnormalTermination()) {
1596
1597 ULONG i;
1598
1599 if (UnwindOplock != NULL) { FsRtlUninitializeOplock( UnwindOplock ); }
1600 if (UnwindFileLock != NULL) { FsRtlUninitializeFileLock( UnwindFileLock ); }
1601 if (UnwindMcb != NULL) { FsRtlUninitializeLargeMcb( UnwindMcb ); }
1602 if (UnwindEntryList != NULL) { RemoveEntryList( UnwindEntryList ); }
1603 if (UnwindResource != NULL) { FatFreeResource( UnwindResource ); }
1604 if (UnwindResource2 != NULL) { FatFreeResource( UnwindResource2 ); }
1605
1606 for (i = 0; i < sizeof(UnwindStorage)/sizeof(PVOID); i += 1) {
1607 if (UnwindStorage[i] != NULL) { ExFreePool( UnwindStorage[i] ); }
1608 }
1609 }
1610
1611 DebugTrace(-1, Dbg, "FatCreateFcb -> %p\n", Fcb);
1612 } _SEH2_END;
1613
1614 //
1615 // return and tell the caller
1616 //
1617
1618 return Fcb;
1619 }
1620
1621
1622 PDCB
FatCreateDcb(IN PIRP_CONTEXT IrpContext,IN PVCB Vcb,IN PDCB ParentDcb,IN ULONG LfnOffsetWithinDirectory,IN ULONG DirentOffsetWithinDirectory,IN PDIRENT Dirent,IN PUNICODE_STRING Lfn OPTIONAL)1623 FatCreateDcb (
1624 IN PIRP_CONTEXT IrpContext,
1625 IN PVCB Vcb,
1626 IN PDCB ParentDcb,
1627 IN ULONG LfnOffsetWithinDirectory,
1628 IN ULONG DirentOffsetWithinDirectory,
1629 IN PDIRENT Dirent,
1630 IN PUNICODE_STRING Lfn OPTIONAL
1631 )
1632
1633 /*++
1634
1635 Routine Description:
1636
1637 This routine allocates, initializes, and inserts a new Dcb record into
1638 the in memory data structures.
1639
1640 Arguments:
1641
1642 Vcb - Supplies the Vcb to associate the new DCB under.
1643
1644 ParentDcb - Supplies the parent dcb that the new DCB is under.
1645
1646 LfnOffsetWithinDirectory - Supplies the offset of the LFN. If there is
1647 no LFN associated with this file then this value is same as
1648 DirentOffsetWithinDirectory.
1649
1650 DirentOffsetWithinDirectory - Supplies the offset, in bytes from the
1651 start of the directory file where the dirent for the fcb is located
1652
1653 Dirent - Supplies the dirent for the dcb being created
1654
1655 FileName - Supplies the file name of the file relative to the directory
1656 it's in (e.g., the file \config.sys is called "CONFIG.SYS" without
1657 the preceding backslash).
1658
1659 Lfn - Supplies a long UNICODE name associated with this directory.
1660
1661 Return Value:
1662
1663 PDCB - Returns a pointer to the newly allocated DCB
1664
1665 --*/
1666
1667 {
1668 PDCB Dcb = NULL;
1669
1670 //
1671 // The following variables are used for abnormal unwind
1672 //
1673
1674 PVOID UnwindStorage[2] = { NULL, NULL };
1675 PERESOURCE UnwindResource = NULL;
1676 PERESOURCE UnwindResource2 = NULL;
1677 PLIST_ENTRY UnwindEntryList = NULL;
1678 PLARGE_MCB UnwindMcb = NULL;
1679 POPLOCK UnwindOplock = NULL;
1680
1681 PAGED_CODE();
1682
1683 DebugTrace(+1, Dbg, "FatCreateDcb\n", 0);
1684
1685 _SEH2_TRY {
1686
1687 //
1688 // assert that the only time we are called is if wait is true
1689 //
1690
1691 NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
1692
1693 //
1694 // Allocate a new DCB, and zero it out
1695 //
1696
1697 UnwindStorage[0] = Dcb = FatAllocateFcb();
1698
1699 RtlZeroMemory( Dcb, sizeof(DCB) );
1700
1701 UnwindStorage[1] =
1702 Dcb->NonPaged = FatAllocateNonPagedFcb();
1703
1704 RtlZeroMemory( Dcb->NonPaged, sizeof( NON_PAGED_FCB ) );
1705
1706 //
1707 // Set the proper node type code, node byte size and call backs
1708 //
1709
1710 Dcb->Header.NodeTypeCode = FAT_NTC_DCB;
1711 Dcb->Header.NodeByteSize = sizeof(DCB);
1712
1713 Dcb->FcbCondition = FcbGood;
1714
1715 //
1716 // The initial state, open count, and directory change count fields are
1717 // already zero so we can skip setting them
1718 //
1719
1720 //
1721 // Initialize the resource variable
1722 //
1723
1724
1725 UnwindResource =
1726 Dcb->Header.Resource = FatAllocateResource();
1727
1728 //
1729 // Initialize the PagingIo Resource. We no longer use the FsRtl common
1730 // shared pool because this led to a) deadlocks due to cases where files
1731 // and their parent directories shared a resource and b) there is no way
1732 // to anticipate inter-driver induced deadlock via recursive operation.
1733 //
1734
1735 UnwindResource2 =
1736 Dcb->Header.PagingIoResource = FatAllocateResource();
1737
1738 //
1739 // Insert this Dcb into our parent dcb's queue
1740 //
1741 // There is a deep reason why this goes on the head, to allow us
1742 // to easily enumerate all child directories before child files.
1743 // This is important to let us maintain whole-volume lockorder
1744 // via BottomUp enumeration.
1745 //
1746
1747 InsertHeadList( &ParentDcb->Specific.Dcb.ParentDcbQueue,
1748 &Dcb->ParentDcbLinks );
1749 UnwindEntryList = &Dcb->ParentDcbLinks;
1750
1751 //
1752 // Point back to our parent dcb
1753 //
1754
1755 Dcb->ParentDcb = ParentDcb;
1756
1757 //
1758 // Set the Vcb
1759 //
1760
1761 Dcb->Vcb = Vcb;
1762
1763 //
1764 // Set the dirent offset within the directory
1765 //
1766
1767 Dcb->LfnOffsetWithinDirectory = LfnOffsetWithinDirectory;
1768 Dcb->DirentOffsetWithinDirectory = DirentOffsetWithinDirectory;
1769
1770 //
1771 // Set the DirentFatFlags and LastWriteTime
1772 //
1773
1774 Dcb->DirentFatFlags = Dirent->Attributes;
1775
1776 Dcb->LastWriteTime = FatFatTimeToNtTime( IrpContext,
1777 Dirent->LastWriteTime,
1778 0 );
1779
1780 //
1781 // These fields are only non-zero when in Chicago mode.
1782 //
1783
1784 if (FatData.ChicagoMode) {
1785
1786 LARGE_INTEGER FatSystemJanOne1980 = {0};
1787
1788 //
1789 // If either date is possibly zero, get the system
1790 // version of 1/1/80.
1791 //
1792
1793 if ((((PUSHORT)Dirent)[9] & ((PUSHORT)Dirent)[8]) == 0) {
1794
1795 ExLocalTimeToSystemTime( &FatJanOne1980,
1796 &FatSystemJanOne1980 );
1797 }
1798
1799 //
1800 // Only do the really hard work if this field is non-zero.
1801 //
1802
1803 if (((PUSHORT)Dirent)[9] != 0) {
1804
1805 Dcb->LastAccessTime =
1806 FatFatDateToNtTime( IrpContext,
1807 Dirent->LastAccessDate );
1808
1809 } else {
1810
1811 Dcb->LastAccessTime = FatSystemJanOne1980;
1812 }
1813
1814 //
1815 // Only do the really hard work if this field is non-zero.
1816 //
1817
1818 if (((PUSHORT)Dirent)[8] != 0) {
1819
1820 Dcb->CreationTime =
1821 FatFatTimeToNtTime( IrpContext,
1822 Dirent->CreationTime,
1823 Dirent->CreationMSec );
1824
1825 } else {
1826
1827 Dcb->CreationTime = FatSystemJanOne1980;
1828 }
1829 }
1830
1831 //
1832 // Initialize Advanced FCB Header fields
1833 //
1834
1835 ExInitializeFastMutex( &Dcb->NonPaged->AdvancedFcbHeaderMutex );
1836 FsRtlSetupAdvancedHeader( &Dcb->Header,
1837 &Dcb->NonPaged->AdvancedFcbHeaderMutex );
1838
1839 //
1840 // Initialize the Mcb
1841 //
1842
1843 FsRtlInitializeLargeMcb( &Dcb->Mcb, PagedPool );
1844 UnwindMcb = &Dcb->Mcb;
1845
1846 //
1847 // Set the file size, first cluster of file, and allocation size
1848 // based on the information stored in the dirent
1849 //
1850
1851 Dcb->FirstClusterOfFile = (ULONG)Dirent->FirstClusterOfFile;
1852
1853 if ( FatIsFat32(Dcb->Vcb) ) {
1854
1855 Dcb->FirstClusterOfFile += Dirent->FirstClusterOfFileHi << 16;
1856 }
1857
1858 if ( Dcb->FirstClusterOfFile == 0 ) {
1859
1860 Dcb->Header.AllocationSize.QuadPart = 0;
1861
1862 } else {
1863
1864 Dcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT;
1865 }
1866
1867
1868 // initialize the notify queues, and the parent dcb queue.
1869 //
1870
1871 InitializeListHead( &Dcb->Specific.Dcb.ParentDcbQueue );
1872
1873 //
1874 // Setup the free dirent bitmap buffer. Since we don't know the
1875 // size of the directory, leave it zero for now.
1876 //
1877
1878 RtlInitializeBitMap( &Dcb->Specific.Dcb.FreeDirentBitmap,
1879 NULL,
1880 0 );
1881
1882 //
1883 // Set our two create dirent aids to represent that we have yet to
1884 // enumerate the directory for never used or deleted dirents.
1885 //
1886
1887 Dcb->Specific.Dcb.UnusedDirentVbo = 0xffffffff;
1888 Dcb->Specific.Dcb.DeletedDirentHint = 0xffffffff;
1889
1890 #if (NTDDI_VERSION >= NTDDI_WIN8)
1891 //
1892 // Initialize the oplock structure.
1893 //
1894
1895 FsRtlInitializeOplock( FatGetFcbOplock(Dcb) );
1896 UnwindOplock = FatGetFcbOplock(Dcb);
1897 #endif
1898
1899 //
1900 // Postpone initializing the cache map until we need to do a read/write
1901 // of the directory file.
1902
1903
1904 //
1905 // set the file names. This must be the last thing we do.
1906 //
1907
1908 FatConstructNamesInFcb( IrpContext,
1909 Dcb,
1910 Dirent,
1911 Lfn );
1912
1913 Dcb->ShortName.FileNameDos = TRUE;
1914
1915 } _SEH2_FINALLY {
1916
1917 DebugUnwind( FatCreateDcb );
1918
1919 //
1920 // If this is an abnormal termination then undo our work
1921 //
1922
1923 if (_SEH2_AbnormalTermination()) {
1924
1925 ULONG i;
1926
1927 if (UnwindOplock != NULL) { FsRtlUninitializeOplock( UnwindOplock ); }
1928 if (UnwindMcb != NULL) { FsRtlUninitializeLargeMcb( UnwindMcb ); }
1929 if (UnwindEntryList != NULL) { RemoveEntryList( UnwindEntryList ); }
1930 if (UnwindResource != NULL) { FatFreeResource( UnwindResource ); }
1931 if (UnwindResource2 != NULL) { FatFreeResource( UnwindResource2 ); }
1932
1933 for (i = 0; i < sizeof(UnwindStorage)/sizeof(PVOID); i += 1) {
1934 if (UnwindStorage[i] != NULL) { ExFreePool( UnwindStorage[i] ); }
1935 }
1936 }
1937
1938 DebugTrace(-1, Dbg, "FatCreateDcb -> %p\n", Dcb);
1939 } _SEH2_END;
1940
1941 //
1942 // return and tell the caller
1943 //
1944
1945 DebugTrace(-1, Dbg, "FatCreateDcb -> %p\n", Dcb);
1946
1947 return Dcb;
1948 }
1949
1950
1951 VOID
FatDeleteFcb(IN PIRP_CONTEXT IrpContext,IN PFCB * FcbPtr)1952 FatDeleteFcb (
1953 IN PIRP_CONTEXT IrpContext,
1954 IN PFCB *FcbPtr
1955 )
1956
1957 /*++
1958
1959 Routine Description:
1960
1961 This routine deallocates and removes an FCB, DCB, or ROOT DCB record
1962 from Fat's in-memory data structures. It also will remove all
1963 associated underlings (i.e., Notify irps, and child FCB/DCB records).
1964
1965 Arguments:
1966
1967 Fcb - Supplies the FCB/DCB/ROOT DCB to be removed
1968
1969 Return Value:
1970
1971 None
1972
1973 --*/
1974
1975 {
1976 PFCB Fcb = *FcbPtr;
1977
1978 PAGED_CODE();
1979
1980 DebugTrace(+1, Dbg, "FatDeleteFcb, Fcb = %p\n", Fcb);
1981
1982 //
1983 // We can only delete this record if the open count is zero.
1984 //
1985
1986 if (Fcb->OpenCount != 0) {
1987
1988 DebugDump("Error deleting Fcb, Still Open\n", 0, Fcb);
1989 #ifdef _MSC_VER
1990 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
1991 #endif
1992 FatBugCheck( 0, 0, 0 );
1993 }
1994
1995 //
1996 // Better be an FCB/DCB.
1997 //
1998
1999 if ((Fcb->Header.NodeTypeCode != FAT_NTC_DCB) &&
2000 (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB) &&
2001 (Fcb->Header.NodeTypeCode != FAT_NTC_FCB)) {
2002
2003 #ifdef _MSC_VER
2004 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
2005 #endif
2006 FatBugCheck( 0, 0, 0 );
2007 }
2008
2009 //
2010 // If this is a DCB then remove every Notify record from the two
2011 // notify queues
2012 //
2013
2014 if ((Fcb->Header.NodeTypeCode == FAT_NTC_DCB) ||
2015 (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB)) {
2016
2017 //
2018 // If we allocated a free dirent bitmap buffer, free it.
2019 //
2020
2021 if ((Fcb->Specific.Dcb.FreeDirentBitmap.Buffer != NULL) &&
2022 (Fcb->Specific.Dcb.FreeDirentBitmap.Buffer !=
2023 &Fcb->Specific.Dcb.FreeDirentBitmapBuffer[0])) {
2024
2025 ExFreePool(Fcb->Specific.Dcb.FreeDirentBitmap.Buffer);
2026 }
2027
2028 #if (NTDDI_VERSION >= NTDDI_WIN8)
2029 //
2030 // Uninitialize the oplock.
2031 //
2032
2033 FsRtlUninitializeOplock( FatGetFcbOplock(Fcb) );
2034 #endif
2035
2036 NT_ASSERT( Fcb->Specific.Dcb.DirectoryFileOpenCount == 0 );
2037 NT_ASSERT( IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) );
2038 NT_ASSERT( NULL == Fcb->Specific.Dcb.DirectoryFile);
2039
2040 } else {
2041
2042 //
2043 // Uninitialize the byte range file locks and opportunistic locks
2044 //
2045
2046 FsRtlUninitializeFileLock( &Fcb->Specific.Fcb.FileLock );
2047 FsRtlUninitializeOplock( FatGetFcbOplock(Fcb) );
2048 }
2049
2050
2051 //
2052 // Release any Filter Context structures associated with this FCB
2053 //
2054
2055 FsRtlTeardownPerStreamContexts( &Fcb->Header );
2056
2057 //
2058 // Uninitialize the Mcb
2059 //
2060
2061 FsRtlUninitializeLargeMcb( &Fcb->Mcb );
2062
2063 //
2064 // If this is not the root dcb then we need to remove ourselves from
2065 // our parents Dcb queue
2066 //
2067
2068 if (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB) {
2069
2070 RemoveEntryList( &(Fcb->ParentDcbLinks) );
2071 }
2072
2073 //
2074 // Remove the entry from the splay table if there is still is one.
2075 //
2076
2077 if (FlagOn( Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE )) {
2078
2079 FatRemoveNames( IrpContext, Fcb );
2080 }
2081
2082 //
2083 // Free the file name pool if allocated.
2084 //
2085
2086 if (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB) {
2087
2088 //
2089 // If we blew up at inconvenient times, the shortname
2090 // could be null even though you will *never* see this
2091 // normally. Rename is a good example of this case.
2092 //
2093
2094 if (Fcb->ShortName.Name.Oem.Buffer) {
2095
2096 ExFreePool( Fcb->ShortName.Name.Oem.Buffer );
2097 }
2098
2099 if (Fcb->FullFileName.Buffer) {
2100
2101 ExFreePool( Fcb->FullFileName.Buffer );
2102 }
2103 }
2104
2105 if (Fcb->ExactCaseLongName.Buffer) {
2106
2107 ExFreePool(Fcb->ExactCaseLongName.Buffer);
2108 }
2109
2110 #ifdef SYSCACHE_COMPILE
2111
2112 if (Fcb->WriteMask) {
2113
2114 ExFreePool( Fcb->WriteMask );
2115 }
2116
2117 #endif
2118
2119 //
2120 // Finally deallocate the Fcb and non-paged fcb records
2121 //
2122
2123 FatFreeResource( Fcb->Header.Resource );
2124
2125 if (Fcb->Header.PagingIoResource != Fcb->Header.Resource) {
2126
2127 FatFreeResource( Fcb->Header.PagingIoResource );
2128 }
2129
2130 //
2131 // If an Event was allocated, get rid of it.
2132 //
2133
2134 if (Fcb->NonPaged->OutstandingAsyncEvent) {
2135
2136 ExFreePool( Fcb->NonPaged->OutstandingAsyncEvent );
2137 }
2138
2139 FatFreeNonPagedFcb( Fcb->NonPaged );
2140
2141 Fcb->Header.NodeTypeCode = NTC_UNDEFINED;
2142
2143 FatFreeFcb( Fcb );
2144 *FcbPtr = NULL;
2145
2146 //
2147 // and return to our caller
2148 //
2149
2150 DebugTrace(-1, Dbg, "FatDeleteFcb -> VOID\n", 0);
2151 }
2152
2153
2154 PCCB
FatCreateCcb(IN PIRP_CONTEXT IrpContext)2155 FatCreateCcb (
2156 IN PIRP_CONTEXT IrpContext
2157 )
2158
2159 /*++
2160
2161 Routine Description:
2162
2163 This routine creates a new CCB record
2164
2165 Arguments:
2166
2167 Return Value:
2168
2169 CCB - returns a pointer to the newly allocate CCB
2170
2171 --*/
2172
2173 {
2174 PCCB Ccb;
2175
2176 PAGED_CODE();
2177
2178 DebugTrace(+1, Dbg, "FatCreateCcb\n", 0);
2179
2180 //
2181 // Allocate a new CCB Record
2182 //
2183
2184 Ccb = FatAllocateCcb();
2185
2186 RtlZeroMemory( Ccb, sizeof(CCB) );
2187
2188 //
2189 // Set the proper node type code and node byte size
2190 //
2191
2192 Ccb->NodeTypeCode = FAT_NTC_CCB;
2193 Ccb->NodeByteSize = sizeof(CCB);
2194
2195 //
2196 // return and tell the caller
2197 //
2198
2199 DebugTrace(-1, Dbg, "FatCreateCcb -> %p\n", Ccb);
2200
2201 UNREFERENCED_PARAMETER( IrpContext );
2202
2203 return Ccb;
2204 }
2205
2206
2207
2208 VOID
FatDeallocateCcbStrings(IN PCCB Ccb)2209 FatDeallocateCcbStrings(
2210 IN PCCB Ccb
2211 )
2212 /*++
2213
2214 Routine Description:
2215
2216 This routine deallocates CCB query templates
2217
2218 Arguments:
2219
2220 Ccb - Supplies the CCB
2221
2222 Return Value:
2223
2224 None
2225
2226 --*/
2227 {
2228 PAGED_CODE();
2229
2230 //
2231 // If we allocated query template buffers, deallocate them now.
2232 //
2233
2234 if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_UNICODE)) {
2235
2236 NT_ASSERT( Ccb->UnicodeQueryTemplate.Buffer);
2237 NT_ASSERT( !FlagOn( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT));
2238 RtlFreeUnicodeString( &Ccb->UnicodeQueryTemplate );
2239 }
2240
2241 if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT)) {
2242
2243 NT_ASSERT( Ccb->OemQueryTemplate.Wild.Buffer );
2244 NT_ASSERT( !FlagOn( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT));
2245 RtlFreeOemString( &Ccb->OemQueryTemplate.Wild );
2246 }
2247
2248 ClearFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT | CCB_FLAG_FREE_UNICODE);
2249 }
2250
2251
2252
2253 VOID
FatDeleteCcb(IN PIRP_CONTEXT IrpContext,IN PCCB * Ccb)2254 FatDeleteCcb (
2255 IN PIRP_CONTEXT IrpContext,
2256 IN PCCB *Ccb
2257 )
2258
2259 /*++
2260
2261 Routine Description:
2262
2263 This routine deallocates and removes the specified CCB record
2264 from the Fat in memory data structures
2265
2266 Arguments:
2267
2268 Ccb - Supplies the CCB to remove
2269
2270 Return Value:
2271
2272 None
2273
2274 --*/
2275
2276 {
2277 PAGED_CODE();
2278
2279 DebugTrace(+1, Dbg, "FatDeleteCcb, Ccb = %p\n", *Ccb);
2280
2281 FatDeallocateCcbStrings( *Ccb);
2282
2283 //
2284 // Deallocate the Ccb record
2285 //
2286
2287 FatFreeCcb( *Ccb );
2288 *Ccb = NULL;
2289
2290 //
2291 // return and tell the caller
2292 //
2293
2294 DebugTrace(-1, Dbg, "FatDeleteCcb -> VOID\n", 0);
2295
2296 UNREFERENCED_PARAMETER( IrpContext );
2297 }
2298
2299
2300 PIRP_CONTEXT
FatCreateIrpContext(IN PIRP Irp,IN BOOLEAN Wait)2301 FatCreateIrpContext (
2302 IN PIRP Irp,
2303 IN BOOLEAN Wait
2304 )
2305
2306 /*++
2307
2308 Routine Description:
2309
2310 This routine creates a new IRP_CONTEXT record
2311
2312 Arguments:
2313
2314 Irp - Supplies the originating Irp.
2315
2316 Wait - Supplies the wait value to store in the context
2317
2318 Return Value:
2319
2320 PIRP_CONTEXT - returns a pointer to the newly allocate IRP_CONTEXT Record
2321
2322 --*/
2323
2324 {
2325 PIRP_CONTEXT IrpContext;
2326 PIO_STACK_LOCATION IrpSp;
2327
2328 PAGED_CODE();
2329
2330 DebugTrace(+1, Dbg, "FatCreateIrpContext\n", 0);
2331
2332 IrpSp = IoGetCurrentIrpStackLocation( Irp );
2333
2334 //
2335 // The only operations a filesystem device object should ever receive
2336 // are create/teardown of fsdo handles and operations which do not
2337 // occur in the context of fileobjects (i.e., mount).
2338 //
2339
2340 if (FatDeviceIsFatFsdo( IrpSp->DeviceObject)) {
2341
2342 if (IrpSp->FileObject != NULL &&
2343 IrpSp->MajorFunction != IRP_MJ_CREATE &&
2344 IrpSp->MajorFunction != IRP_MJ_CLEANUP &&
2345 IrpSp->MajorFunction != IRP_MJ_CLOSE) {
2346
2347 ExRaiseStatus( STATUS_INVALID_DEVICE_REQUEST );
2348 }
2349
2350 NT_ASSERT( IrpSp->FileObject != NULL ||
2351
2352 (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL &&
2353 IrpSp->MinorFunction == IRP_MN_USER_FS_REQUEST &&
2354 IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_INVALIDATE_VOLUMES) ||
2355
2356 (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL &&
2357 IrpSp->MinorFunction == IRP_MN_MOUNT_VOLUME ) ||
2358
2359 IrpSp->MajorFunction == IRP_MJ_SHUTDOWN );
2360 }
2361
2362 //
2363 // Attemtp to allocate from the region first and failing that allocate
2364 // from pool.
2365 //
2366
2367 DebugDoit( FatFsdEntryCount += 1);
2368
2369 IrpContext = FatAllocateIrpContext();
2370
2371 //
2372 // Zero out the irp context.
2373 //
2374
2375 RtlZeroMemory( IrpContext, sizeof(IRP_CONTEXT) );
2376
2377 //
2378 // Set the proper node type code and node byte size
2379 //
2380
2381 IrpContext->NodeTypeCode = FAT_NTC_IRP_CONTEXT;
2382 IrpContext->NodeByteSize = sizeof(IRP_CONTEXT);
2383
2384 //
2385 // Set the originating Irp field
2386 //
2387
2388 IrpContext->OriginatingIrp = Irp;
2389
2390 //
2391 // Major/Minor Function codes
2392 //
2393
2394 IrpContext->MajorFunction = IrpSp->MajorFunction;
2395 IrpContext->MinorFunction = IrpSp->MinorFunction;
2396
2397 //
2398 // Copy RealDevice for workque algorithms, and also set Write Through
2399 // and Removable Media if there is a file object. Only file system
2400 // control Irps won't have a file object, and they should all have
2401 // a Vpb as the first IrpSp location.
2402 //
2403
2404 if (IrpSp->FileObject != NULL) {
2405
2406 PFILE_OBJECT FileObject = IrpSp->FileObject;
2407
2408 IrpContext->RealDevice = FileObject->DeviceObject;
2409 IrpContext->Vcb = &((PVOLUME_DEVICE_OBJECT)(IrpSp->DeviceObject))->Vcb;
2410
2411 //
2412 // See if the request is Write Through. Look for both FileObjects opened
2413 // as write through, and non-cached requests with the SL_WRITE_THROUGH flag set.
2414 //
2415 // The latter can only originate from kernel components. (Note - NtWriteFile()
2416 // does redundantly set the SL_W_T flag for all requests it issues on write
2417 // through file objects)
2418 //
2419
2420 if (IsFileWriteThrough( FileObject, IrpContext->Vcb ) ||
2421 ( (IrpSp->MajorFunction == IRP_MJ_WRITE) &&
2422 BooleanFlagOn( Irp->Flags, IRP_NOCACHE) &&
2423 BooleanFlagOn( IrpSp->Flags, SL_WRITE_THROUGH))) {
2424
2425 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH);
2426 }
2427 } else if (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) {
2428
2429 IrpContext->RealDevice = IrpSp->Parameters.MountVolume.Vpb->RealDevice;
2430 }
2431
2432 //
2433 // Set the wait parameter
2434 //
2435
2436 if (Wait) { SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); }
2437
2438 //
2439 // Set the recursive file system call parameter. We set it true if
2440 // the TopLevelIrp field in the thread local storage is not the current
2441 // irp, otherwise we leave it as FALSE.
2442 //
2443
2444 if ( IoGetTopLevelIrp() != Irp) {
2445
2446 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL);
2447 }
2448
2449 //
2450 // return and tell the caller
2451 //
2452
2453 DebugTrace(-1, Dbg, "FatCreateIrpContext -> %p\n", IrpContext);
2454
2455 return IrpContext;
2456 }
2457
2458
2459
2460 VOID
FatDeleteIrpContext_Real(IN PIRP_CONTEXT IrpContext)2461 FatDeleteIrpContext_Real (
2462 IN PIRP_CONTEXT IrpContext
2463 )
2464
2465 /*++
2466
2467 Routine Description:
2468
2469 This routine deallocates and removes the specified IRP_CONTEXT record
2470 from the Fat in memory data structures. It should only be called
2471 by FatCompleteRequest.
2472
2473 Arguments:
2474
2475 IrpContext - Supplies the IRP_CONTEXT to remove
2476
2477 Return Value:
2478
2479 None
2480
2481 --*/
2482
2483 {
2484 PAGED_CODE();
2485
2486 DebugTrace(+1, Dbg, "FatDeleteIrpContext, IrpContext = %p\n", IrpContext);
2487
2488 NT_ASSERT( IrpContext->NodeTypeCode == FAT_NTC_IRP_CONTEXT );
2489 NT_ASSERT( IrpContext->PinCount == 0 );
2490
2491
2492 //
2493 // If there is a FatIoContext that was allocated, free it.
2494 //
2495
2496 if (IrpContext->FatIoContext != NULL) {
2497
2498 if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT)) {
2499
2500 if (IrpContext->FatIoContext->ZeroMdl) {
2501 IoFreeMdl( IrpContext->FatIoContext->ZeroMdl );
2502 }
2503
2504 ExFreePool( IrpContext->FatIoContext );
2505 }
2506 }
2507
2508 //
2509 // Drop the IrpContext.
2510 //
2511
2512 FatFreeIrpContext( IrpContext );
2513
2514 //
2515 // return and tell the caller
2516 //
2517
2518 DebugTrace(-1, Dbg, "FatDeleteIrpContext -> VOID\n", 0);
2519
2520 return;
2521 }
2522
2523
2524 PFCB
FatGetNextFcbBottomUp(IN PIRP_CONTEXT IrpContext,IN PFCB Fcb OPTIONAL,IN PFCB TerminationFcb)2525 FatGetNextFcbBottomUp (
2526 IN PIRP_CONTEXT IrpContext,
2527 IN PFCB Fcb OPTIONAL,
2528 IN PFCB TerminationFcb
2529 )
2530
2531 /*++
2532
2533 Routine Description:
2534
2535 This routine is used to iterate through Fcbs in a tree. In order to match
2536 the lockorder for getting multiple Fcbs (so this can be used for acquiring
2537 all Fcbs), this version does a bottom-up enumeration.
2538
2539 This is different than the old one, now called TopDown. The problem with
2540 lockorder was very well hidden.
2541
2542 The transition rule is still pretty simple:
2543
2544 A) If you have an adjacent sibling, go to it
2545 1) Descend to its leftmost child
2546 B) Else go to your parent
2547
2548 If this routine is called with in invalid TerminationFcb it will fail,
2549 badly.
2550
2551 The TerminationFcb is the last Fcb returned in the enumeration.
2552
2553 This method is incompatible with the possibility that ancestors may vanish
2554 based on operations done on the last returned node. For instance,
2555 FatPurgeReferencedFileObjects cannot use BottomUp enumeration.
2556
2557 Arguments:
2558
2559 Fcb - Supplies the current Fcb. This is NULL if enumeration is starting.
2560
2561 TerminationFcb - The root Fcb of the tree in which the enumeration starts
2562 and at which it inclusively stops.
2563
2564 Return Value:
2565
2566 The next Fcb in the enumeration, or NULL if Fcb was the final one.
2567
2568 --*/
2569
2570 {
2571 PFCB NextFcb;
2572
2573 PAGED_CODE();
2574 UNREFERENCED_PARAMETER( IrpContext );
2575
2576 NT_ASSERT( FatVcbAcquiredExclusive( IrpContext, TerminationFcb->Vcb ) ||
2577 FlagOn( TerminationFcb->Vcb->VcbState, VCB_STATE_FLAG_LOCKED ) );
2578
2579 //
2580 // Do we need to begin the enumeration?
2581 //
2582
2583 if (Fcb != NULL) {
2584
2585 //
2586 // Did we complete?
2587 //
2588
2589 if (Fcb == TerminationFcb) {
2590
2591 return NULL;
2592 }
2593
2594 //
2595 // Do we have a sibling to return?
2596 //
2597
2598 NextFcb = FatGetNextSibling( Fcb );
2599
2600 //
2601 // If not, return our parent. We are done with this branch.
2602 //
2603
2604 if (NextFcb == NULL) {
2605
2606 return Fcb->ParentDcb;
2607 }
2608
2609 } else {
2610
2611 NextFcb = TerminationFcb;
2612 }
2613
2614 //
2615 // Decend to its furthest child (if it exists) and return it.
2616 //
2617
2618 for (;
2619 NodeType( NextFcb ) != FAT_NTC_FCB && FatGetFirstChild( NextFcb ) != NULL;
2620 NextFcb = FatGetFirstChild( NextFcb )) {
2621 }
2622
2623 return NextFcb;
2624 }
2625
2626 PFCB
FatGetNextFcbTopDown(IN PIRP_CONTEXT IrpContext,IN PFCB Fcb,IN PFCB TerminationFcb)2627 FatGetNextFcbTopDown (
2628 IN PIRP_CONTEXT IrpContext,
2629 IN PFCB Fcb,
2630 IN PFCB TerminationFcb
2631 )
2632
2633 /*++
2634
2635 Routine Description:
2636
2637 This routine is used to iterate through Fcbs in a tree, from the top down.
2638
2639 The rule is very simple:
2640
2641 A) If you have a child, go to it, else
2642 B) If you have an older sibling, go to it, else
2643 C) Go to your parent's older sibling.
2644
2645 If this routine is called with in invalid TerminationFcb it will fail,
2646 badly.
2647
2648 The Termination Fcb is never returned. If it is the root of the tree you
2649 are traversing, visit it first.
2650
2651 This routine never returns direct ancestors of Fcb, and thus is useful when
2652 making Fcb's go away (which may tear up the tree).
2653
2654 Arguments:
2655
2656 Fcb - Supplies the current Fcb
2657
2658 TerminationFcb - The Fcb at which the enumeration should (non-inclusivly)
2659 stop. Assumed to be a directory.
2660
2661 Return Value:
2662
2663 The next Fcb in the enumeration, or NULL if Fcb was the final one.
2664
2665 --*/
2666
2667 {
2668 PFCB Sibling;
2669
2670 PAGED_CODE();
2671 UNREFERENCED_PARAMETER( IrpContext );
2672
2673 NT_ASSERT( FatVcbAcquiredExclusive( IrpContext, Fcb->Vcb ) ||
2674 FlagOn( Fcb->Vcb->VcbState, VCB_STATE_FLAG_LOCKED ) );
2675
2676 //
2677 // If this was a directory (ie. not a file), get the child. If
2678 // there aren't any children and this is our termination Fcb,
2679 // return NULL.
2680 //
2681
2682 if ( ((NodeType(Fcb) == FAT_NTC_DCB) ||
2683 (NodeType(Fcb) == FAT_NTC_ROOT_DCB)) &&
2684 !IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) ) {
2685
2686 return FatGetFirstChild( Fcb );
2687 }
2688
2689 //
2690 // Were we only meant to do one iteration?
2691 //
2692
2693 if ( Fcb == TerminationFcb ) {
2694
2695 return NULL;
2696 }
2697
2698 Sibling = FatGetNextSibling(Fcb);
2699
2700 while (TRUE) {
2701
2702 //
2703 // Do we still have an "older" sibling in this directory who is
2704 // not the termination Fcb?
2705 //
2706
2707 if ( Sibling != NULL ) {
2708
2709 return (Sibling != TerminationFcb) ? Sibling : NULL;
2710 }
2711
2712 //
2713 // OK, let's move on to out parent and see if he is the termination
2714 // node or has any older siblings.
2715 //
2716
2717 if ( Fcb->ParentDcb == TerminationFcb ) {
2718
2719 return NULL;
2720 }
2721
2722 Fcb = Fcb->ParentDcb;
2723
2724 Sibling = FatGetNextSibling(Fcb);
2725 }
2726 }
2727
2728
2729 BOOLEAN
FatSwapVpb(IN PIRP_CONTEXT IrpContext,PVCB Vcb)2730 FatSwapVpb (
2731 IN PIRP_CONTEXT IrpContext,
2732 PVCB Vcb
2733 )
2734
2735 /*++
2736
2737 Routine Description:
2738
2739 This routine swaps the VPB for this VCB if it has not been done already.
2740 This means the device object will get our spare VPB and we will cleanup
2741 the one used while the volume was mounted.
2742
2743 Arguments:
2744
2745 IrpContext - Supplies the context for the overall request.
2746
2747 Vcb - Supplies the VCB to swap the VPB on.
2748
2749 Return Value:
2750
2751 TRUE - If the VPB was actually swapped.
2752
2753 FALSE - If the VPB was already swapped.
2754
2755 --*/
2756
2757 {
2758 BOOLEAN Result = FALSE;
2759 PVPB OldVpb;
2760 PIO_STACK_LOCATION IrpSp;
2761
2762
2763 //
2764 // Make sure we have not already swapped it.
2765 //
2766
2767 OldVpb = Vcb->Vpb;
2768
2769 #ifdef _MSC_VER
2770 #pragma prefast( push )
2771 #pragma prefast( disable: 28175, "touching Vpb is ok for a filesystem" )
2772 #endif
2773
2774 if (!FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED ) && OldVpb->RealDevice->Vpb == OldVpb) {
2775
2776 //
2777 // If not the final reference and we are forcing the disconnect,
2778 // then swap out the Vpb. We must preserve the REMOVE_PENDING flag
2779 // so that the device is not remounted in the middle of a PnP remove
2780 // operation.
2781 //
2782
2783 NT_ASSERT( Vcb->SwapVpb != NULL );
2784
2785 Vcb->SwapVpb->Type = IO_TYPE_VPB;
2786 Vcb->SwapVpb->Size = sizeof( VPB );
2787 Vcb->SwapVpb->RealDevice = OldVpb->RealDevice;
2788
2789 Vcb->SwapVpb->RealDevice->Vpb = Vcb->SwapVpb;
2790
2791 Vcb->SwapVpb->Flags = FlagOn( OldVpb->Flags, VPB_REMOVE_PENDING );
2792
2793 //
2794 // If we are working on a mount request, we need to make sure we update
2795 // the VPB in the IRP, since the one it points to may no longer be valid.
2796 //
2797
2798 if (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) {
2799
2800 //
2801 // Get the IRP stack.
2802 //
2803
2804 IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );
2805
2806 NT_ASSERT( IrpSp->FileObject == NULL );
2807
2808 //
2809 // Check the VPB in the IRP to see if it is the one we are swapping.
2810 //
2811
2812 if (IrpSp->Parameters.MountVolume.Vpb == OldVpb) {
2813
2814 //
2815 // Change the IRP to point to the swap VPB.
2816 //
2817
2818 IrpSp->Parameters.MountVolume.Vpb = Vcb->SwapVpb;
2819 }
2820 }
2821
2822 #ifdef _MSC_VER
2823 #pragma prefast( pop )
2824 #endif
2825
2826 //
2827 // We place the volume in the Bad state (as opposed to NotMounted) so
2828 // that it is not eligible for a remount. Also indicate we used up
2829 // the swap.
2830 //
2831
2832 Vcb->SwapVpb = NULL;
2833 FatSetVcbCondition( Vcb, VcbBad);
2834 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED );
2835
2836 Result = TRUE;
2837 }
2838
2839 return Result;
2840 }
2841
2842
_Requires_lock_held_(_Global_critical_region_)2843 _Requires_lock_held_(_Global_critical_region_)
2844 BOOLEAN
2845 FatCheckForDismount (
2846 IN PIRP_CONTEXT IrpContext,
2847 PVCB Vcb,
2848 IN BOOLEAN Force
2849 )
2850
2851 /*++
2852
2853 Routine Description:
2854
2855 This routine determines if a volume is ready for deletion. It
2856 correctly synchronizes with creates en-route to the file system.
2857
2858 Arguments:
2859
2860 Vcb - Supplies the volume to examine
2861
2862 Force - Specifies whether we want this Vcb forcibly disconnected
2863 from the driver stack if it will not be deleted (a new vpb will
2864 be installed if neccesary). Caller is responsible for making
2865 sure that the volume has been marked in such a way that attempts
2866 to operate through the realdevice are blocked (i.e., move the vcb
2867 out of the mounted state).
2868
2869 Return Value:
2870
2871 BOOLEAN - TRUE if the volume was deleted, FALSE otherwise.
2872
2873 --*/
2874
2875 {
2876 KIRQL SavedIrql;
2877 BOOLEAN VcbDeleted = FALSE;
2878
2879
2880 //
2881 // If the VCB condition is good and we are not forcing, just return.
2882 //
2883
2884 if (Vcb->VcbCondition == VcbGood && !Force) {
2885
2886 return FALSE;
2887 }
2888
2889 //
2890 // Now check for a zero Vpb count on an unmounted volume. These
2891 // volumes will be deleted as they now have no file objects and
2892 // there are no creates en route to this volume.
2893 //
2894
2895 IoAcquireVpbSpinLock( &SavedIrql );
2896
2897 if (Vcb->Vpb->ReferenceCount == Vcb->ResidualOpenCount && Vcb->OpenFileCount == 0) {
2898
2899 PVPB Vpb = Vcb->Vpb;
2900
2901 #if DBG
2902 UNICODE_STRING VolumeLabel;
2903
2904 //
2905 // Setup the VolumeLabel string
2906 //
2907
2908 VolumeLabel.Length = Vcb->Vpb->VolumeLabelLength;
2909 VolumeLabel.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH;
2910 VolumeLabel.Buffer = &Vcb->Vpb->VolumeLabel[0];
2911
2912 KdPrintEx((DPFLTR_FASTFAT_ID,
2913 DPFLTR_INFO_LEVEL,
2914 "FASTFAT: Dismounting Volume %Z\n",
2915 &VolumeLabel));
2916 #endif // DBG
2917
2918 //
2919 // Swap this VCB's VPB.
2920 //
2921
2922 FatSwapVpb( IrpContext,
2923 Vcb );
2924
2925 //
2926 // Clear the VPB_MOUNTED bit. New opens should not come in due
2927 // to the swapped VPB, but having the flag cleared helps debugging.
2928 // Note that we must leave the Vpb->DeviceObject field set until
2929 // after the FatTearDownVcb call as closes will have to make their
2930 // way back to us.
2931 //
2932
2933 ClearFlag( Vpb->Flags, VPB_MOUNTED );
2934
2935 //
2936 // If this Vpb was locked, clear this flag now.
2937 //
2938
2939 ClearFlag( Vpb->Flags, (VPB_LOCKED | VPB_DIRECT_WRITES_ALLOWED) );
2940
2941 IoReleaseVpbSpinLock( SavedIrql );
2942
2943 //
2944 // We are going to attempt the dismount, so mark the VCB as having
2945 // a dismount in progress.
2946 //
2947
2948 NT_ASSERT( !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ) );
2949 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS );
2950
2951 //
2952 // Close down our internal opens.
2953 //
2954
2955 FatTearDownVcb( IrpContext,
2956 Vcb );
2957
2958 //
2959 // Process any delayed closes.
2960 //
2961
2962 FatFspClose( Vcb );
2963
2964 //
2965 // Grab the VPB lock again so that we can recheck our counts.
2966 //
2967
2968 IoAcquireVpbSpinLock( &SavedIrql );
2969
2970 //
2971 // See if we can delete this VCB.
2972 //
2973
2974 if (Vcb->Vpb->ReferenceCount == 0 && Vcb->InternalOpenCount == 0) {
2975
2976 Vpb->DeviceObject = NULL;
2977
2978 IoReleaseVpbSpinLock( SavedIrql );
2979
2980 FatDeleteVcb( IrpContext, Vcb );
2981
2982 IoDeleteDevice( (PDEVICE_OBJECT)
2983 CONTAINING_RECORD( Vcb,
2984 VOLUME_DEVICE_OBJECT,
2985 Vcb ) );
2986
2987 VcbDeleted = TRUE;
2988
2989 } else {
2990
2991 IoReleaseVpbSpinLock( SavedIrql );
2992
2993 NT_ASSERT( FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ) );
2994 ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS );
2995 }
2996
2997 } else if (Force) {
2998
2999 //
3000 // The requester is forcing the issue. We need to swap the VPB with our spare.
3001 //
3002
3003 FatSwapVpb( IrpContext,
3004 Vcb );
3005
3006 IoReleaseVpbSpinLock( SavedIrql );
3007
3008 } else {
3009
3010 //
3011 // Just drop the Vpb spinlock.
3012 //
3013
3014 IoReleaseVpbSpinLock( SavedIrql );
3015 }
3016
3017 return VcbDeleted;
3018 }
3019
3020
3021 VOID
FatConstructNamesInFcb(IN PIRP_CONTEXT IrpContext,PFCB Fcb,PDIRENT Dirent,PUNICODE_STRING Lfn OPTIONAL)3022 FatConstructNamesInFcb (
3023 IN PIRP_CONTEXT IrpContext,
3024 PFCB Fcb,
3025 PDIRENT Dirent,
3026 PUNICODE_STRING Lfn OPTIONAL
3027 )
3028
3029 /*++
3030
3031 Routine Description:
3032
3033 This routine places the short name in the dirent in the first set of
3034 STRINGs in the Fcb. If a long file name (Lfn) was specified, then
3035 we must decide whether we will store its Oem equivolent in the same
3036 prefix table as the short name, or rather just save the upcased
3037 version of the UNICODE string in the FCB.
3038
3039 For looking up Fcbs, the first approach will be faster, so we want to
3040 do this as much as possible. Here are the rules that I have thought
3041 through extensively to determine when it is safe to store only Oem
3042 version of the UNICODE name.
3043
3044 - If the UNICODE name contains no extended characters (>0x80), use Oem.
3045
3046 - Let U be the upcased version of the UNICODE name.
3047 Let Up(x) be the function that upcases a UNICODE string.
3048 Let Down(x) be the function that upcases a UNICODE string.
3049 Let OemToUni(x) be the function that converts an Oem string to Unicode.
3050 Let UniToOem(x) be the function that converts a Unicode string to Oem.
3051 Let BestOemFit(x) be the function that creates the Best uppercase Oem
3052 fit for the UNICODE string x.
3053
3054 BestOemFit(x) = UniToOem(Up(OemToUni(UniToOem(x)))) <1>
3055
3056 if (BestOemFit(U) == BestOemFit(Down(U)) <2>
3057
3058 then I know that there exists no UNICODE string Y such that:
3059
3060 Up(Y) == Up(U) <3>
3061
3062 AND
3063
3064 BestOemFit(U) != BestOemFit(Y) <4>
3065
3066 Consider string U as a collection of one character strings. The
3067 conjecture is clearly true for each sub-string, thus it is true
3068 for the entire string.
3069
3070 Equation <1> is what we use to convert an incoming unicode name in
3071 FatCommonCreate() to Oem. The double conversion is done to provide
3072 better visual best fitting for characters in the Ansi code page but
3073 not in the Oem code page. A single Nls routine is provided to do
3074 this conversion efficiently.
3075
3076 The idea is that with U, I only have to worry about a case varient Y
3077 matching it in a unicode compare, and I have shown that any case varient
3078 of U (the set Y defined in equation <3>), when filtered through <1>
3079 (as in create), will match the Oem string defined in <1>.
3080
3081 Thus I do not have to worry about another UNICODE string missing in
3082 the prefix lookup, but matching when comparing LFNs in the directory.
3083
3084 Arguments:
3085
3086 Fcb - The Fcb we are supposed to fill in. Note that ParentDcb must
3087 already be filled in.
3088
3089 Dirent - The gives up the short name.
3090
3091 Lfn - If provided, this gives us the long name.
3092
3093 Return Value:
3094
3095 None
3096
3097 --*/
3098
3099 {
3100 #ifndef __REACTOS__
3101 NTSTATUS Status;
3102 #endif
3103 ULONG i;
3104
3105 #ifndef __REACTOS__
3106 OEM_STRING OemA;
3107 OEM_STRING OemB;
3108 #endif
3109 UNICODE_STRING Unicode;
3110 POEM_STRING ShortName;
3111 POEM_STRING LongOemName;
3112 PUNICODE_STRING LongUniName;
3113
3114 PAGED_CODE();
3115
3116 ShortName = &Fcb->ShortName.Name.Oem;
3117
3118 NT_ASSERT( ShortName->Buffer == NULL );
3119
3120 _SEH2_TRY {
3121
3122 //
3123 // First do the short name.
3124 //
3125
3126 //
3127 // Copy over the case flags for the short name of the file
3128 //
3129
3130 if (FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_8_LOWER_CASE)) {
3131
3132 SetFlag(Fcb->FcbState, FCB_STATE_8_LOWER_CASE);
3133
3134 } else {
3135
3136 ClearFlag(Fcb->FcbState, FCB_STATE_8_LOWER_CASE);
3137 }
3138
3139 if (FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_3_LOWER_CASE)) {
3140
3141 SetFlag(Fcb->FcbState, FCB_STATE_3_LOWER_CASE);
3142
3143 } else {
3144
3145 ClearFlag(Fcb->FcbState, FCB_STATE_3_LOWER_CASE);
3146 }
3147
3148 ShortName->MaximumLength = 16;
3149 ShortName->Buffer = FsRtlAllocatePoolWithTag( PagedPool,
3150 16,
3151 TAG_FILENAME_BUFFER );
3152
3153 Fat8dot3ToString( IrpContext, Dirent, FALSE, ShortName );
3154
3155 Fcb->ShortName.FileNameDos = TRUE;
3156
3157 //
3158 // If no Lfn was specified, we are done. In either case, set the
3159 // final name length.
3160 //
3161
3162 NT_ASSERT( Fcb->ExactCaseLongName.Buffer == NULL );
3163
3164 if (!ARGUMENT_PRESENT(Lfn) || (Lfn->Length == 0)) {
3165
3166 Fcb->FinalNameLength = (USHORT) RtlOemStringToCountedUnicodeSize( ShortName );
3167 Fcb->ExactCaseLongName.Length = Fcb->ExactCaseLongName.MaximumLength = 0;
3168
3169 try_return( NOTHING );
3170 }
3171
3172 //
3173 // If we already set up the full filename, we could be in trouble. If the fast
3174 // path for doing it already fired, FatSetFullFileNameInFcb, it will have missed
3175 // this and could have built the full filename out of the shortname of the file.
3176 //
3177 // At that point, disaster could be inevitable since the final name length will not
3178 // match. We use this to tell the notify package what to do - FatNotifyReportChange.
3179 //
3180
3181 NT_ASSERT( Fcb->FullFileName.Buffer == NULL );
3182
3183 //
3184 // We know now we have an Lfn, save away a copy.
3185 //
3186
3187 Fcb->FinalNameLength = Lfn->Length;
3188
3189 Fcb->ExactCaseLongName.Length = Fcb->ExactCaseLongName.MaximumLength = Lfn->Length;
3190 Fcb->ExactCaseLongName.Buffer = FsRtlAllocatePoolWithTag( PagedPool,
3191 Lfn->Length,
3192 TAG_FILENAME_BUFFER );
3193 RtlCopyMemory(Fcb->ExactCaseLongName.Buffer, Lfn->Buffer, Lfn->Length);
3194
3195 //
3196 // First check for no extended characters.
3197 //
3198
3199 for (i=0; i < Lfn->Length/sizeof(WCHAR); i++) {
3200
3201 if (Lfn->Buffer[i] >= 0x80) {
3202
3203 break;
3204 }
3205 }
3206
3207 if (i == Lfn->Length/sizeof(WCHAR)) {
3208
3209 //
3210 // Cool, I can go with the Oem, upcase it fast by hand.
3211 //
3212
3213 LongOemName = &Fcb->LongName.Oem.Name.Oem;
3214
3215
3216 LongOemName->Buffer = FsRtlAllocatePoolWithTag( PagedPool,
3217 Lfn->Length/sizeof(WCHAR),
3218 TAG_FILENAME_BUFFER );
3219 LongOemName->Length =
3220 LongOemName->MaximumLength = Lfn->Length/sizeof(WCHAR);
3221
3222 for (i=0; i < Lfn->Length/sizeof(WCHAR); i++) {
3223
3224 WCHAR c;
3225
3226 c = Lfn->Buffer[i];
3227
3228 #ifdef _MSC_VER
3229 #pragma warning( push )
3230 #pragma warning( disable:4244 )
3231 #endif
3232 LongOemName->Buffer[i] = c < 'a' ?
3233 (UCHAR)c :
3234 c <= 'z' ?
3235 c - (UCHAR)('a'-'A') :
3236 (UCHAR)c;
3237 #ifdef _MSC_VER
3238 #pragma warning( pop )
3239 #endif
3240 }
3241
3242 //
3243 // If this name happens to be exactly the same as the short
3244 // name, don't add it to the splay table.
3245 //
3246
3247 if (FatAreNamesEqual(IrpContext, *ShortName, *LongOemName) ||
3248 (FatFindFcb( IrpContext,
3249 &Fcb->ParentDcb->Specific.Dcb.RootOemNode,
3250 LongOemName,
3251 NULL) != NULL)) {
3252
3253 ExFreePool( LongOemName->Buffer );
3254
3255 LongOemName->Buffer = NULL;
3256 LongOemName->Length =
3257 LongOemName->MaximumLength = 0;
3258
3259 } else {
3260
3261 SetFlag( Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME );
3262 }
3263
3264 try_return( NOTHING );
3265 }
3266
3267 //
3268 // Now we have the fun part. Make a copy of the Lfn.
3269 //
3270
3271 #ifndef __REACTOS__
3272 OemA.Buffer = NULL;
3273 OemB.Buffer = NULL;
3274 #endif
3275 Unicode.Buffer = NULL;
3276
3277 Unicode.Length =
3278 Unicode.MaximumLength = Lfn->Length;
3279 Unicode.Buffer = FsRtlAllocatePoolWithTag( PagedPool,
3280 Lfn->Length,
3281 TAG_FILENAME_BUFFER );
3282
3283 RtlCopyMemory( Unicode.Buffer, Lfn->Buffer, Lfn->Length );
3284
3285 #ifndef __REACTOS__
3286 Status = STATUS_SUCCESS;
3287 #endif
3288
3289 #if TRUE
3290 //
3291 // Unfortunately, this next block of code breaks down when you have
3292 // two long Unicode filenames that both map to the same Oem (and are,
3293 // well, long, i.e. are not the short names). In this case, with one
3294 // in the prefix table first, the other will hit the common Oem
3295 // representation. This leads to several forms of user astonishment.
3296 //
3297 // It isn't worth it, or probably even possible, to try to figure out
3298 // when this is really safe to go through. Simply omit the attempt.
3299 //
3300 // Ex: ANSI 0x82 and 0x84 in the 1252 ANSI->UNI and 437 UNI->OEM codepages.
3301 //
3302 // 0x82 => 0x201a => 0x2c
3303 // 0x84 => 0x201e => 0x2c
3304 //
3305 // 0x2c is comma, so is FAT Oem illegal and forces shortname generation.
3306 // Since it is otherwise well-formed by the rules articulated previously,
3307 // we would have put 0x2c in the Oem prefix tree. In terms of the
3308 // argument given above, even though there exist no Y and U s.t.
3309 //
3310 // Up(Y) == Up(U) && BestOemFit(U) != BestOemFit(Y)
3311 //
3312 // there most certainly exist Y and U s.t.
3313 //
3314 // Up(Y) != Up(U) && BestOemFit(U) == BestOemFit(Y)
3315 //
3316 // and that is enough to keep us from doing this. Note that the < 0x80
3317 // case is OK since we know that the mapping in the OEM codepages are
3318 // the identity in that range.
3319 //
3320 // We still need to monocase it, though. Do this through a full down/up
3321 // transition.
3322 //
3323
3324 (VOID)RtlDowncaseUnicodeString( &Unicode, &Unicode, FALSE );
3325 (VOID)RtlUpcaseUnicodeString( &Unicode, &Unicode, FALSE );
3326 #else
3327 //
3328 // Downcase and convert to upcased Oem. Only continue if we can
3329 // convert without error. Any error other than UNMAPPABLE_CHAR
3330 // is a fatal error and we raise.
3331 //
3332 // Note that even if the conversion fails, we must leave Unicode
3333 // in an upcased state.
3334 //
3335 // NB: The Rtl doesn't NULL .Buffer on error.
3336 //
3337
3338 (VOID)RtlDowncaseUnicodeString( &Unicode, &Unicode, FALSE );
3339 Status = RtlUpcaseUnicodeStringToCountedOemString( &OemA, &Unicode, TRUE );
3340 (VOID)RtlUpcaseUnicodeString( &Unicode, &Unicode, FALSE );
3341
3342 if (!NT_SUCCESS(Status)) {
3343
3344 if (Status != STATUS_UNMAPPABLE_CHARACTER) {
3345
3346 NT_ASSERT( Status == STATUS_NO_MEMORY );
3347 ExFreePool(Unicode.Buffer);
3348 FatNormalizeAndRaiseStatus( IrpContext, Status );
3349 }
3350
3351 } else {
3352
3353 //
3354 // The same as above except upcase.
3355 //
3356
3357 Status = RtlUpcaseUnicodeStringToCountedOemString( &OemB, &Unicode, TRUE );
3358
3359 if (!NT_SUCCESS(Status)) {
3360
3361 RtlFreeOemString( &OemA );
3362
3363 if (Status != STATUS_UNMAPPABLE_CHARACTER) {
3364
3365 NT_ASSERT( Status == STATUS_NO_MEMORY );
3366 ExFreePool(Unicode.Buffer);
3367 FatNormalizeAndRaiseStatus( IrpContext, Status );
3368 }
3369 }
3370 }
3371
3372 //
3373 // If the final OemNames are equal, I can use save only the Oem
3374 // name. If the name did not map, then I have to go with the UNICODE
3375 // name because I could get a case varient that didn't convert
3376 // in create, but did match the LFN.
3377 //
3378
3379 if (NT_SUCCESS(Status) && FatAreNamesEqual( IrpContext, OemA, OemB )) {
3380
3381 //
3382 // Cool, I can go with the Oem. If we didn't convert correctly,
3383 // get a fresh convert from the original LFN.
3384 //
3385
3386 ExFreePool(Unicode.Buffer);
3387
3388 RtlFreeOemString( &OemB );
3389
3390 Fcb->LongName.Oem.Name.Oem = OemA;
3391
3392 //
3393 // If this name happens to be exactly the same as the short
3394 // name, or a similar short name already exists don't add it
3395 // to the splay table (note the final condition implies a
3396 // corrupt disk.
3397 //
3398
3399 if (FatAreNamesEqual(IrpContext, *ShortName, OemA) ||
3400 (FatFindFcb( IrpContext,
3401 &Fcb->ParentDcb->Specific.Dcb.RootOemNode,
3402 &OemA,
3403 NULL) != NULL)) {
3404
3405 RtlFreeOemString( &OemA );
3406
3407 } else {
3408
3409 SetFlag( Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME );
3410 }
3411
3412 try_return( NOTHING );
3413 }
3414
3415 //
3416 // The long name must be left in UNICODE. Free the two Oem strings
3417 // if we got here just because they weren't equal.
3418 //
3419
3420 if (NT_SUCCESS(Status)) {
3421
3422 RtlFreeOemString( &OemA );
3423 RtlFreeOemString( &OemB );
3424 }
3425 #endif
3426
3427 LongUniName = &Fcb->LongName.Unicode.Name.Unicode;
3428
3429 LongUniName->Length =
3430 LongUniName->MaximumLength = Unicode.Length;
3431 LongUniName->Buffer = Unicode.Buffer;
3432
3433 SetFlag(Fcb->FcbState, FCB_STATE_HAS_UNICODE_LONG_NAME);
3434
3435 try_exit: NOTHING;
3436 } _SEH2_FINALLY {
3437
3438 if (_SEH2_AbnormalTermination()) {
3439
3440 if (ShortName->Buffer != NULL) {
3441
3442 ExFreePool( ShortName->Buffer );
3443 ShortName->Buffer = NULL;
3444 }
3445
3446 } else {
3447
3448 //
3449 // Creating all the names worked, so add all the names
3450 // to the splay tree.
3451 //
3452
3453 FatInsertName( IrpContext,
3454 &Fcb->ParentDcb->Specific.Dcb.RootOemNode,
3455 &Fcb->ShortName );
3456
3457 Fcb->ShortName.Fcb = Fcb;
3458
3459 if (FlagOn(Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME)) {
3460
3461 FatInsertName( IrpContext,
3462 &Fcb->ParentDcb->Specific.Dcb.RootOemNode,
3463 &Fcb->LongName.Oem );
3464
3465 Fcb->LongName.Oem.Fcb = Fcb;
3466 }
3467
3468 if (FlagOn(Fcb->FcbState, FCB_STATE_HAS_UNICODE_LONG_NAME)) {
3469
3470 FatInsertName( IrpContext,
3471 &Fcb->ParentDcb->Specific.Dcb.RootUnicodeNode,
3472 &Fcb->LongName.Unicode );
3473
3474 Fcb->LongName.Unicode.Fcb = Fcb;
3475 }
3476
3477 SetFlag(Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE);
3478 }
3479 } _SEH2_END;
3480
3481 return;
3482 }
3483
3484
_Requires_lock_held_(_Global_critical_region_)3485 _Requires_lock_held_(_Global_critical_region_)
3486 VOID
3487 FatCheckFreeDirentBitmap (
3488 IN PIRP_CONTEXT IrpContext,
3489 IN PDCB Dcb
3490 )
3491
3492 /*++
3493
3494 Routine Description:
3495
3496 This routine checks if the size of the free dirent bitmap is
3497 sufficient to for the current directory size. It is called
3498 whenever we grow a directory.
3499
3500 Arguments:
3501
3502 Dcb - Supplies the directory in question.
3503
3504 Return Value:
3505
3506 None
3507
3508 --*/
3509
3510 {
3511 ULONG OldNumberOfDirents;
3512 ULONG NewNumberOfDirents;
3513
3514 PAGED_CODE();
3515 UNREFERENCED_PARAMETER( IrpContext );
3516
3517 //
3518 // Setup the Bitmap buffer if it is not big enough already
3519 //
3520
3521 NT_ASSERT( Dcb->Header.AllocationSize.QuadPart != FCB_LOOKUP_ALLOCATIONSIZE_HINT );
3522
3523 OldNumberOfDirents = Dcb->Specific.Dcb.FreeDirentBitmap.SizeOfBitMap;
3524 NewNumberOfDirents = Dcb->Header.AllocationSize.LowPart / sizeof(DIRENT);
3525
3526 //
3527 // Do the usual unsync/sync check.
3528 //
3529
3530 if (NewNumberOfDirents > OldNumberOfDirents) {
3531
3532 FatAcquireDirectoryFileMutex( Dcb->Vcb );
3533
3534 _SEH2_TRY {
3535
3536 PULONG OldBitmapBuffer;
3537 PULONG BitmapBuffer;
3538
3539 ULONG BytesInBitmapBuffer;
3540 ULONG BytesInOldBitmapBuffer;
3541
3542 OldNumberOfDirents = Dcb->Specific.Dcb.FreeDirentBitmap.SizeOfBitMap;
3543 NewNumberOfDirents = Dcb->Header.AllocationSize.LowPart / sizeof(DIRENT);
3544
3545 if (NewNumberOfDirents > OldNumberOfDirents) {
3546
3547 //
3548 // Remember the old bitmap
3549 //
3550
3551 OldBitmapBuffer = Dcb->Specific.Dcb.FreeDirentBitmap.Buffer;
3552
3553 //
3554 // Now make a new bitmap bufffer
3555 //
3556
3557 BytesInBitmapBuffer = NewNumberOfDirents / 8;
3558
3559 BytesInOldBitmapBuffer = OldNumberOfDirents / 8;
3560
3561 if (DCB_UNION_SLACK_SPACE >= BytesInBitmapBuffer) {
3562
3563 BitmapBuffer = &Dcb->Specific.Dcb.FreeDirentBitmapBuffer[0];
3564
3565 } else {
3566
3567 BitmapBuffer = FsRtlAllocatePoolWithTag( PagedPool,
3568 BytesInBitmapBuffer,
3569 TAG_DIRENT_BITMAP );
3570 }
3571
3572 //
3573 // Copy the old buffer to the new buffer, free the old one, and zero
3574 // the rest of the new one. Only do the first two steps though if
3575 // we moved out of the initial buffer.
3576 //
3577
3578 if ((OldNumberOfDirents != 0) &&
3579 (BitmapBuffer != &Dcb->Specific.Dcb.FreeDirentBitmapBuffer[0])) {
3580
3581 RtlCopyMemory( BitmapBuffer,
3582 OldBitmapBuffer,
3583 BytesInOldBitmapBuffer );
3584
3585 if (OldBitmapBuffer != &Dcb->Specific.Dcb.FreeDirentBitmapBuffer[0]) {
3586
3587 ExFreePool( OldBitmapBuffer );
3588 }
3589 }
3590
3591 NT_ASSERT( BytesInBitmapBuffer > BytesInOldBitmapBuffer );
3592
3593 RtlZeroMemory( (PUCHAR)BitmapBuffer + BytesInOldBitmapBuffer,
3594 BytesInBitmapBuffer - BytesInOldBitmapBuffer );
3595
3596 //
3597 // Now initialize the new bitmap.
3598 //
3599
3600 RtlInitializeBitMap( &Dcb->Specific.Dcb.FreeDirentBitmap,
3601 BitmapBuffer,
3602 NewNumberOfDirents );
3603 }
3604
3605 } _SEH2_FINALLY {
3606
3607 FatReleaseDirectoryFileMutex( Dcb->Vcb );
3608 } _SEH2_END;
3609 }
3610 }
3611
3612
3613 BOOLEAN
FatIsHandleCountZero(IN PIRP_CONTEXT IrpContext,IN PVCB Vcb)3614 FatIsHandleCountZero (
3615 IN PIRP_CONTEXT IrpContext,
3616 IN PVCB Vcb
3617 )
3618
3619 /*++
3620
3621 Routine Description:
3622
3623 This routine decides if the handle count on the volume is zero.
3624
3625 Arguments:
3626
3627 Vcb - The volume in question
3628
3629 Return Value:
3630
3631 BOOLEAN - TRUE if there are no open handles on the volume, FALSE
3632 otherwise.
3633
3634 --*/
3635
3636 {
3637 PFCB Fcb;
3638
3639 PAGED_CODE();
3640
3641 Fcb = Vcb->RootDcb;
3642
3643 while (Fcb != NULL) {
3644
3645 if (Fcb->UncleanCount != 0) {
3646
3647 return FALSE;
3648 }
3649
3650 Fcb = FatGetNextFcbTopDown(IrpContext, Fcb, Vcb->RootDcb);
3651 }
3652
3653 return TRUE;
3654 }
3655
3656
3657 PCLOSE_CONTEXT
3658
FatAllocateCloseContext(OPTIONAL PVCB Vcb)3659 FatAllocateCloseContext(
3660 OPTIONAL PVCB Vcb
3661 )
3662 /*++
3663
3664 Routine Description:
3665
3666 This routine preallocates a close context, presumeably on behalf
3667 of a fileobject which does not have a structure we can embed one
3668 in.
3669
3670 Arguments:
3671
3672 None.
3673
3674 Return Value:
3675
3676 None.
3677
3678 --*/
3679 {
3680 PAGED_CODE();
3681 UNREFERENCED_PARAMETER( Vcb );
3682
3683 #if DBG
3684 if (ARGUMENT_PRESENT(Vcb)) {
3685
3686 NT_ASSERT( 0 != Vcb->CloseContextCount);
3687 InterlockedDecrement( (LONG*)&Vcb->CloseContextCount);
3688 }
3689 #endif
3690 return (PCLOSE_CONTEXT)ExInterlockedPopEntrySList( &FatCloseContextSList,
3691 &FatData.GeneralSpinLock );
3692 }
3693
3694
3695 VOID
FatPreallocateCloseContext(PVCB Vcb)3696 FatPreallocateCloseContext (
3697 PVCB Vcb
3698 )
3699
3700 /*++
3701
3702 Routine Description:
3703
3704 This routine preallocates a close context, presumeably on behalf
3705 of a fileobject which does not have a structure we can embed one
3706 in.
3707
3708 Arguments:
3709
3710 None.
3711
3712 Return Value:
3713
3714 None.
3715
3716 --*/
3717
3718 {
3719 PCLOSE_CONTEXT CloseContext;
3720
3721 PAGED_CODE();
3722
3723 UNREFERENCED_PARAMETER( Vcb );
3724
3725 CloseContext = FsRtlAllocatePoolWithTag( PagedPool,
3726 sizeof(CLOSE_CONTEXT),
3727 TAG_FAT_CLOSE_CONTEXT );
3728
3729 ExInterlockedPushEntrySList( &FatCloseContextSList,
3730 (PSLIST_ENTRY) CloseContext,
3731 &FatData.GeneralSpinLock );
3732
3733 DbgDoit( InterlockedIncrement( (LONG*)&Vcb->CloseContextCount));
3734 }
3735
3736
3737
3738 VOID
FatEnsureStringBufferEnough(_Inout_ PVOID String,_In_ USHORT DesiredBufferSize)3739 FatEnsureStringBufferEnough (
3740 _Inout_ PVOID String,
3741 _In_ USHORT DesiredBufferSize
3742 )
3743
3744 /*++
3745
3746 Routine Description:
3747
3748 Ensures that a string string (STRING, UNICODE_STRING, ANSI_STRING, OEM_STRING)
3749 has a buffer >= DesiredBufferSize, allocating from pool if neccessary. Any
3750 existing pool buffer will be freed if a new one is allocated.
3751
3752 NOTE: No copy of old buffer contents is performed on reallocation.
3753
3754 Will raise on allocation failure.
3755
3756 Arguments:
3757
3758 String - pointer to string structure
3759
3760 DesiredBufferSize - (bytes) minimum required buffer size
3761
3762 --*/
3763
3764 {
3765 PSTRING LocalString = String;
3766
3767 PAGED_CODE();
3768
3769 if (LocalString->MaximumLength < DesiredBufferSize) {
3770
3771 FatFreeStringBuffer( LocalString);
3772
3773 LocalString->Buffer = FsRtlAllocatePoolWithTag( PagedPool,
3774 DesiredBufferSize,
3775 TAG_DYNAMIC_NAME_BUFFER);
3776 NT_ASSERT( LocalString->Buffer);
3777
3778 LocalString->MaximumLength = DesiredBufferSize;
3779 }
3780 }
3781
3782
3783 VOID
FatFreeStringBuffer(_Inout_ PVOID String)3784 FatFreeStringBuffer (
3785 _Inout_ PVOID String
3786 )
3787
3788 /*++
3789
3790 Routine Description:
3791
3792 Frees the buffer of an string (STRING, UNICODE_STRING, ANSI_STRING, OEM_STRING)
3793 structure if it is not within the current thread's stack limits.
3794
3795 Regardless of action performed, on exit String->Buffer will be set to NULL and
3796 String->MaximumLength to zero.
3797
3798 Arguments:
3799
3800 String - pointer to string structure
3801
3802 --*/
3803
3804 {
3805 ULONG_PTR High, Low;
3806 PSTRING LocalString = String;
3807
3808 PAGED_CODE();
3809
3810 if (NULL != LocalString->Buffer) {
3811
3812 IoGetStackLimits( &Low, &High );
3813
3814 if (((ULONG_PTR)(LocalString->Buffer) < Low) ||
3815 ((ULONG_PTR)(LocalString->Buffer) > High)) {
3816
3817 ExFreePool( LocalString->Buffer);
3818 }
3819
3820 LocalString->Buffer = NULL;
3821 }
3822
3823 LocalString->MaximumLength = LocalString->Length = 0;
3824 }
3825
3826
3827 BOOLEAN
FatScanForDataTrack(IN PIRP_CONTEXT IrpContext,IN PDEVICE_OBJECT TargetDeviceObject)3828 FatScanForDataTrack(
3829 IN PIRP_CONTEXT IrpContext,
3830 IN PDEVICE_OBJECT TargetDeviceObject
3831 )
3832
3833 /*++
3834
3835 Routine Description:
3836
3837 This routine is called to verify and process the TOC for this disk.
3838
3839 FAT queries for the TOC to avoid trying to mount on CD-DA/CD-E media, Doing data reads on
3840 audio/leadin of that media sends a lot of drives into what could charitably be called
3841 "conniptions" which take a couple seconds to clear and would also convince FAT that the
3842 device was busted, and fail the mount (not letting CDFS get its crack).
3843
3844 There is special handling of PD media. These things fail the TOC read, but return
3845 a special error code so FAT knows to continue to try the mount anyway.
3846
3847 Arguments:
3848
3849 TargetDeviceObject - Device object to send TOC request to.
3850
3851 Return Value:
3852
3853 BOOLEAN - TRUE if we found a TOC with a single data track.
3854
3855 --*/
3856
3857 {
3858 NTSTATUS Status;
3859 IO_STATUS_BLOCK Iosb;
3860
3861 ULONG LocalTrackCount;
3862 ULONG LocalTocLength;
3863
3864 PCDROM_TOC CdromToc;
3865 BOOLEAN Result = FALSE;
3866
3867 PAGED_CODE();
3868
3869 CdromToc = FsRtlAllocatePoolWithTag( PagedPool,
3870 sizeof( CDROM_TOC ),
3871 TAG_IO_BUFFER );
3872
3873 RtlZeroMemory( CdromToc, sizeof( CDROM_TOC ));
3874
3875 _SEH2_TRY {
3876
3877 //
3878 // Go ahead and read the table of contents
3879 //
3880
3881 Status = FatPerformDevIoCtrl( IrpContext,
3882 IOCTL_CDROM_READ_TOC,
3883 TargetDeviceObject,
3884 NULL,
3885 0,
3886 CdromToc,
3887 sizeof( CDROM_TOC ),
3888 FALSE,
3889 TRUE,
3890 &Iosb );
3891
3892 //
3893 // Nothing to process if this request fails.
3894 //
3895
3896 if (Status != STATUS_SUCCESS) {
3897
3898 //
3899 // If we get the special error indicating a failed TOC read on PD media just
3900 // plow ahead with the mount (see comments above).
3901 //
3902
3903 if ((Status == STATUS_IO_DEVICE_ERROR) || (Status == STATUS_INVALID_DEVICE_REQUEST)) {
3904
3905 Result = TRUE;
3906
3907 }
3908
3909 try_leave( NOTHING );
3910 }
3911
3912 //
3913 // Get the number of tracks and stated size of this structure.
3914 //
3915
3916 LocalTrackCount = CdromToc->LastTrack - CdromToc->FirstTrack + 1;
3917 LocalTocLength = PtrOffset( CdromToc, &CdromToc->TrackData[LocalTrackCount + 1] );
3918
3919 //
3920 // Get out if there is an immediate problem with the TOC, or more than
3921 // one track.
3922 //
3923
3924 if ((LocalTocLength > Iosb.Information) ||
3925 (CdromToc->FirstTrack > CdromToc->LastTrack) ||
3926 (LocalTrackCount != 1)) {
3927
3928 try_leave( NOTHING);
3929 }
3930
3931 //
3932 // Is it a data track? DVD-RAM reports single, data, track.
3933 //
3934
3935 Result = BooleanFlagOn( CdromToc->TrackData[ 0].Control, 0x04 );
3936 }
3937 _SEH2_FINALLY {
3938
3939 ExFreePool( CdromToc);
3940 } _SEH2_END;
3941
3942 return Result;
3943 }
3944
3945
3946