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