3 Copyright (c) 1989-2000 Microsoft Corporation
11 This module implements the dirent support routines for Fat.
19 // The Bug check file id for this module
22 #define BugCheckFileId (FAT_BUG_CHECK_DIRSUP)
25 // Local debug trace level
28 #define Dbg (DEBUG_TRACE_DIRSUP)
31 // The following three macro all assume the input dirent has been zeroed.
37 // IN PIRP_CONTEXT IrpContext,
39 // IN PDIRENT ParentDirent,
40 // IN OUT PDIRENT Dirent
43 // The following macro is called to initalize the "." dirent.
45 // Always setting FirstClusterOfFileHi is OK because it will be zero
46 // unless we're working on a FAT 32 disk.
49 #define FatConstructDot(IRPCONTEXT,DCB,PARENT,DIRENT) { \
51 RtlCopyMemory( (PUCHAR)(DIRENT), ". ", 11 ); \
52 (DIRENT)->Attributes = FAT_DIRENT_ATTR_DIRECTORY; \
53 (DIRENT)->LastWriteTime = (PARENT)->LastWriteTime; \
54 if (FatData.ChicagoMode) { \
55 (DIRENT)->CreationTime = (PARENT)->CreationTime; \
56 (DIRENT)->CreationMSec = (PARENT)->CreationMSec; \
57 (DIRENT)->LastAccessDate = (PARENT)->LastAccessDate; \
59 (DIRENT)->FirstClusterOfFile = \
60 (USHORT)(DCB)->FirstClusterOfFile; \
61 (DIRENT)->FirstClusterOfFileHi = \
62 (USHORT)((DCB)->FirstClusterOfFile/0x10000); \
67 // FatConstructDotDot (
68 // IN PIRP_CONTEXT IrpContext,
70 // IN PDIRENT ParentDirent,
71 // IN OUT PDIRENT Dirent
74 // The following macro is called to initalize the ".." dirent.
76 // Always setting FirstClusterOfFileHi is OK because it will be zero
77 // unless we're working on a FAT 32 disk.
80 #define FatConstructDotDot(IRPCONTEXT,DCB,PARENT,DIRENT) { \
82 RtlCopyMemory( (PUCHAR)(DIRENT), ".. ", 11 ); \
83 (DIRENT)->Attributes = FAT_DIRENT_ATTR_DIRECTORY; \
84 (DIRENT)->LastWriteTime = (PARENT)->LastWriteTime; \
85 if (FatData.ChicagoMode) { \
86 (DIRENT)->CreationTime = (PARENT)->CreationTime; \
87 (DIRENT)->CreationMSec = (PARENT)->CreationMSec; \
88 (DIRENT)->LastAccessDate = (PARENT)->LastAccessDate; \
90 if (NodeType((DCB)->ParentDcb) == FAT_NTC_ROOT_DCB) { \
91 (DIRENT)->FirstClusterOfFile = 0; \
92 (DIRENT)->FirstClusterOfFileHi = 0; \
94 (DIRENT)->FirstClusterOfFile = (USHORT) \
95 ((DCB)->ParentDcb->FirstClusterOfFile); \
96 (DIRENT)->FirstClusterOfFileHi = (USHORT) \
97 ((DCB)->ParentDcb->FirstClusterOfFile/0x10000); \
103 // FatConstructEndDirent (
104 // IN PIRP_CONTEXT IrpContext,
105 // IN OUT PDIRENT Dirent
108 // The following macro created the end dirent. Note that since the
109 // dirent was zeroed, the first byte of the name already contains 0x0,
110 // so there is nothing to do.
113 #define FatConstructEndDirent(IRPCONTEXT,DIRENT) NOTHING
118 // IN PIRP_CONTEXT IrpContext,
122 // OUT PVOID *Dirent,
123 // OUT PNTSTATUS Status
128 // This macro reads in a page of dirents when we step onto a new page,
129 // or this is the first iteration of a loop and Bcb is NULL.
132 #define FatReadDirent(IRPCONTEXT,DCB,VBO,BCB,DIRENT,STATUS) \
133 if ((VBO) >= (DCB)->Header.AllocationSize.LowPart) { \
134 *(STATUS) = STATUS_END_OF_FILE; \
135 FatUnpinBcb( (IRPCONTEXT), *(BCB) ); \
136 } else if ( ((VBO) % PAGE_SIZE == 0) || (*(BCB) == NULL) ) { \
137 FatUnpinBcb( (IRPCONTEXT), *(BCB) ); \
138 FatReadDirectoryFile( (IRPCONTEXT), \
140 (VBO) & ~(PAGE_SIZE - 1), \
146 *(DIRENT) = (PVOID)((PUCHAR)*(DIRENT) + ((VBO) % PAGE_SIZE)); \
150 // Internal support routines
154 FatComputeLfnChecksum (
158 _Requires_lock_held_(_Global_critical_region_
)
161 PIRP_CONTEXT IrpContext
,
165 _Requires_lock_held_(_Global_critical_region_
)
168 IN PIRP_CONTEXT IrpContext
,
170 IN ULONG DirentsNeeded
175 #pragma alloc_text(PAGE, FatComputeLfnChecksum)
176 #pragma alloc_text(PAGE, FatConstructDirent)
177 #pragma alloc_text(PAGE, FatConstructLabelDirent)
178 #pragma alloc_text(PAGE, FatCreateNewDirent)
179 #pragma alloc_text(PAGE, FatDefragDirectory)
180 #pragma alloc_text(PAGE, FatDeleteDirent)
181 #pragma alloc_text(PAGE, FatGetDirentFromFcbOrDcb)
182 #pragma alloc_text(PAGE, FatInitializeDirectoryDirent)
183 #pragma alloc_text(PAGE, FatIsDirectoryEmpty)
184 #pragma alloc_text(PAGE, FatLfnDirentExists)
185 #pragma alloc_text(PAGE, FatLocateDirent)
186 #pragma alloc_text(PAGE, FatLocateSimpleOemDirent)
187 #pragma alloc_text(PAGE, FatLocateVolumeLabel)
188 #pragma alloc_text(PAGE, FatRescanDirectory)
189 #pragma alloc_text(PAGE, FatSetFileSizeInDirent)
190 #pragma alloc_text(PAGE, FatSetFileSizeInDirentNoRaise)
191 #pragma alloc_text(PAGE, FatTunnelFcbOrDcb)
192 #pragma alloc_text(PAGE, FatUpdateDirentFromFcb)
198 _Requires_lock_held_(_Global_critical_region_
)
201 IN PIRP_CONTEXT IrpContext
,
202 IN PDCB ParentDirectory
,
203 IN ULONG DirentsNeeded
,
211 This routine allocates on the disk a new dirent inside of the
212 parent directory. If a new dirent cannot be allocated (i.e.,
213 because the disk is full or the root directory is full) then
214 it raises the appropriate status. The dirent itself is
215 neither initialized nor pinned by this procedure.
219 ParentDirectory - Supplies the DCB for the directory in which
220 to create the new dirent
222 DirentsNeeded - This is the number of continginous dirents required
226 ByteOffset - Returns the VBO within the Parent directory where
227 the dirent has been allocated
237 PDIRENT Dirent
= NULL
;
238 NTSTATUS Status
= STATUS_SUCCESS
;
242 DebugTrace(+1, Dbg
, "FatCreateNewDirent\n", 0);
244 DebugTrace( 0, Dbg
, " ParentDirectory = %p\n", ParentDirectory
);
247 // If UnusedDirentVbo is within our current file allocation then we
248 // don't have to search through the directory at all; we know just
251 // If UnusedDirentVbo is beyond the current file allocation then
252 // there are no more unused dirents in the current allocation, though
253 // upon adding another cluster of allocation UnusedDirentVbo
254 // will point to an unused dirent. Haveing found no unused dirents
255 // we use the DeletedDirentHint to try and find a deleted dirent in
256 // the current allocation. In this also runs off the end of the file,
257 // we finally have to break down and allocate another sector. Note
258 // that simply writing beyond the current allocation will automatically
261 // We also must deal with the special case where UnusedDirentVbo and
262 // DeletedDirentHint have yet to be initialized. In this case we must
263 // first walk through the directory looking for the first deleted entry
264 // first unused dirent. After this point we continue as before.
265 // This initial state is denoted by the special value of 0xffffffff.
268 UnusedVbo
= ParentDirectory
->Specific
.Dcb
.UnusedDirentVbo
;
269 DeletedHint
= ParentDirectory
->Specific
.Dcb
.DeletedDirentHint
;
272 // Check for our first call to this routine with this Dcb. If so
273 // we have to correctly set the two hints in the Dcb.
276 if (UnusedVbo
== 0xffffffff || RescanDir
) {
278 FatRescanDirectory( IrpContext
, ParentDirectory
);
280 UnusedVbo
= ParentDirectory
->Specific
.Dcb
.UnusedDirentVbo
;
281 DeletedHint
= ParentDirectory
->Specific
.Dcb
.DeletedDirentHint
;
285 // Now we know that UnusedDirentVbo and DeletedDirentHint are correctly
286 // set so we check if there is already an unused dirent in the the
287 // current allocation. This is the easy case.
290 DebugTrace( 0, Dbg
, " UnusedVbo = %08lx\n", UnusedVbo
);
291 DebugTrace( 0, Dbg
, " DeletedHint = %08lx\n", DeletedHint
);
293 if (!RescanDir
&& ( UnusedVbo
+ (DirentsNeeded
* sizeof(DIRENT
)) <=
294 ParentDirectory
->Header
.AllocationSize
.LowPart
)) {
297 // Get this unused dirent for the caller. We have a
298 // sporting chance that we won't have to wait.
301 DebugTrace( 0, Dbg
, "There is a never used entry.\n", 0);
303 ByteOffset
= UnusedVbo
;
305 UnusedVbo
+= DirentsNeeded
* sizeof(DIRENT
);
310 // Life is tough. We have to march from the DeletedDirentHint
311 // looking for a deleted dirent. If we get to EOF without finding
312 // one, we will have to allocate a new cluster.
316 RtlFindClearBits( &ParentDirectory
->Specific
.Dcb
.FreeDirentBitmap
,
318 DeletedHint
/ sizeof(DIRENT
) );
321 // Do a quick check for a root directory allocation that failed
322 // simply because of fragmentation. Also, only attempt to defrag
323 // if the length is less that 0x40000. This is to avoid
324 // complications arising from crossing a MM view boundary (256kb).
325 // By default on DOS the root directory is only 0x2000 long.
327 // Don't try to defrag fat32 root dirs.
330 if (!FatIsFat32(ParentDirectory
->Vcb
) &&
331 (ByteOffset
== -1) &&
332 (NodeType(ParentDirectory
) == FAT_NTC_ROOT_DCB
) &&
333 (ParentDirectory
->Header
.AllocationSize
.LowPart
<= 0x40000)) {
335 ByteOffset
= FatDefragDirectory( IrpContext
, ParentDirectory
, DirentsNeeded
);
338 if (ByteOffset
!= -1) {
341 // If we consuemed deleted dirents at Deleted Hint, update.
342 // We also may have consumed some un-used dirents as well,
343 // so be sure to check for that as well.
346 ByteOffset
*= sizeof(DIRENT
);
348 if (ByteOffset
== DeletedHint
) {
350 DeletedHint
+= DirentsNeeded
* sizeof(DIRENT
);
353 if (ByteOffset
+ DirentsNeeded
* sizeof(DIRENT
) > UnusedVbo
) {
355 UnusedVbo
= ByteOffset
+ DirentsNeeded
* sizeof(DIRENT
);
361 // We are going to have to allocate another cluster. Do
362 // so, update both the UnusedVbo and the DeletedHint and bail.
365 DebugTrace( 0, Dbg
, "We have to allocate another cluster.\n", 0);
368 // A reason why we might fail, unrelated to physical reasons,
369 // is that we constrain to 64k directory entries to match the
370 // restriction on Win95. There are fundamental reasons to do
371 // this since searching a FAT directory is a linear operation
372 // and to allow FAT32 to toss us over the cliff is not permissable.
375 if (ParentDirectory
->Header
.AllocationSize
.LowPart
>= (64 * 1024 * sizeof(DIRENT
)) ||
378 // Make sure we are not trying to expand the root directory on non
379 // FAT32. FAT16 and FAT12 have fixed size allocations.
382 (!FatIsFat32(ParentDirectory
->Vcb
) &&
383 NodeType(ParentDirectory
) == FAT_NTC_ROOT_DCB
)) {
385 DebugTrace(0, Dbg
, "Full root directory or too big on FAT32. Raise Status.\n", 0);
387 FatRaiseStatus( IrpContext
, STATUS_CANNOT_MAKE
);
391 // Take the last dirent(s) in this cluster. We will allocate
392 // more clusters below.
395 ByteOffset
= UnusedVbo
;
396 UnusedVbo
+= DirentsNeeded
* sizeof(DIRENT
);
399 // Touch the directory file to cause space for the new dirents
409 FatPrepareWriteDirectoryFile( IrpContext
,
421 FatUnpinBcb( IrpContext
, Bcb
);
427 // If we are only requesting a single dirent, and we did not get the
428 // first dirent in a directory, then check that the preceding dirent
429 // is not an orphaned LFN. If it is, then mark it deleted. Thus
430 // reducing the possibility of an accidental pairing.
432 // Only do this when we are in Chicago Mode.
437 if (FatData
.ChicagoMode
&&
438 (DirentsNeeded
== 1) &&
439 (ByteOffset
> (NodeType(ParentDirectory
) == FAT_NTC_ROOT_DCB
?
440 0 : 2 * sizeof(DIRENT
)))) {
443 FatReadDirent( IrpContext
,
445 ByteOffset
- sizeof(DIRENT
),
450 if ((Status
!= STATUS_SUCCESS
) ||
451 (Dirent
->FileName
[0] == FAT_DIRENT_NEVER_USED
)) {
453 FatPopUpFileCorrupt( IrpContext
, ParentDirectory
);
455 FatRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
458 if ((Dirent
->Attributes
== FAT_DIRENT_ATTR_LFN
) &&
459 (Dirent
->FileName
[0] != FAT_DIRENT_DELETED
)) {
462 // Pin it, mark it, and set it dirty.
465 FatPinMappedData( IrpContext
,
467 ByteOffset
- sizeof(DIRENT
),
471 Dirent
->FileName
[0] = FAT_DIRENT_DELETED
;
473 FatSetDirtyBcb( IrpContext
, Bcb
, ParentDirectory
->Vcb
, TRUE
);
475 NT_ASSERT( RtlAreBitsSet( &ParentDirectory
->Specific
.Dcb
.FreeDirentBitmap
,
476 (ByteOffset
- sizeof(DIRENT
))/ sizeof(DIRENT
),
479 RtlClearBits( &ParentDirectory
->Specific
.Dcb
.FreeDirentBitmap
,
480 (ByteOffset
- sizeof(DIRENT
))/ sizeof(DIRENT
),
487 FatUnpinBcb( IrpContext
, Bcb
);
492 // Assert that the dirents are in fact unused
501 for (i
= 0; i
< DirentsNeeded
; i
++) {
503 FatReadDirent( IrpContext
,
505 ByteOffset
+ i
*sizeof(DIRENT
),
510 if ((Status
!= STATUS_SUCCESS
) ||
511 ((Dirent
->FileName
[0] != FAT_DIRENT_NEVER_USED
) &&
512 (Dirent
->FileName
[0] != FAT_DIRENT_DELETED
))) {
514 FatPopUpFileCorrupt( IrpContext
, ParentDirectory
);
515 FatRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
521 FatUnpinBcb( IrpContext
, Bcb
);
525 // Set the Bits in the bitmap and move the Unused Dirent Vbo.
528 NT_ASSERT( RtlAreBitsClear( &ParentDirectory
->Specific
.Dcb
.FreeDirentBitmap
,
529 ByteOffset
/ sizeof(DIRENT
),
532 RtlSetBits( &ParentDirectory
->Specific
.Dcb
.FreeDirentBitmap
,
533 ByteOffset
/ sizeof(DIRENT
),
537 // Save the newly computed values in the Parent Directory Fcb
540 ParentDirectory
->Specific
.Dcb
.UnusedDirentVbo
= UnusedVbo
;
541 ParentDirectory
->Specific
.Dcb
.DeletedDirentHint
= DeletedHint
;
543 DebugTrace(-1, Dbg
, "FatCreateNewDirent -> (VOID)\n", 0);
550 _Requires_lock_held_(_Global_critical_region_
)
552 FatInitializeDirectoryDirent (
553 IN PIRP_CONTEXT IrpContext
,
555 IN PDIRENT ParentDirent
562 This routine converts a dirent into a directory on the disk. It does this
563 setting the directory flag in the dirent, and by allocating the necessary
564 space for the "." and ".." dirents and initializing them.
566 If a new dirent cannot be allocated (i.e., because the disk is full) then
567 it raises the appropriate status.
571 Dcb - Supplies the Dcb denoting the file that is to be made into a
572 directory. This must be input a completely empty file with
573 an allocation size of zero.
575 ParentDirent - Provides the parent Dirent for a time-stamp model.
586 NTSTATUS DontCare
= STATUS_SUCCESS
;
590 DebugTrace(+1, Dbg
, "FatInitializeDirectoryDirent\n", 0);
592 DebugTrace( 0, Dbg
, " Dcb = %p\n", Dcb
);
595 // Assert that we are not attempting this on the root directory.
598 NT_ASSERT( NodeType(Dcb
) != FAT_NTC_ROOT_DCB
);
601 // Assert that this is only attempted on newly created directories.
604 NT_ASSERT( Dcb
->Header
.AllocationSize
.LowPart
== 0 );
607 // Prepare the directory file for writing. Note that we can use a single
608 // Bcb for these two entries because we know they are the first two in
609 // the directory, and thus together do not span a page boundry. Also
610 // note that we prepare write 2 entries: one for "." and one for "..".
611 // The end of directory marker is automatically set since the whole
612 // directory is initially zero (DIRENT_NEVER_USED).
615 FatPrepareWriteDirectoryFile( IrpContext
,
625 NT_ASSERT( NT_SUCCESS( DontCare
));
628 // Add the . and .. entries
633 FatConstructDot( IrpContext
, Dcb
, ParentDirent
, (PDIRENT
)Buffer
+ 0);
635 FatConstructDotDot( IrpContext
, Dcb
, ParentDirent
, (PDIRENT
)Buffer
+ 1);
638 // Unpin the buffer and return to the caller.
643 FatUnpinBcb( IrpContext
, Bcb
);
646 DebugTrace(-1, Dbg
, "FatInitializeDirectoryDirent -> (VOID)\n", 0);
660 This routine handles tunneling of an Fcb or Dcb associated with
661 an object whose name is disappearing from a directory.
665 FcbOrDcb - Supplies the Fcb/Dcb whose name will be going away
667 Ccb - Supplies the Ccb for the Fcb (not reqired for a Dcb) so
668 that we know which name the Fcb was opened by
676 UNICODE_STRING ShortNameWithCase
= {0};
677 UNICODE_STRING DownCaseSeg
;
678 WCHAR ShortNameBuffer
[8+1+3];
684 DebugTrace(+1, Dbg
, "FatTunnelFcbOrDcb\n", 0);
686 if (NodeType(FcbOrDcb
) == FAT_NTC_DCB
) {
689 // Directory deletion. Flush all entries from this directory in
690 // the cache for this volume
693 FsRtlDeleteKeyFromTunnelCache( &FcbOrDcb
->Vcb
->Tunnel
,
694 FatDirectoryKey(FcbOrDcb
) );
699 // Was a file, so throw it into the tunnel cache
703 // Get the short name into UNICODE
706 ShortNameWithCase
.Length
= 0;
707 ShortNameWithCase
.MaximumLength
= sizeof(ShortNameBuffer
);
708 ShortNameWithCase
.Buffer
= ShortNameBuffer
;
711 #pragma prefast( suppress:28931, "needed for debug build" )
713 Status
= RtlOemStringToCountedUnicodeString( &ShortNameWithCase
,
714 &FcbOrDcb
->ShortName
.Name
.Oem
,
717 NT_ASSERT(ShortNameWithCase
.Length
!= 0);
719 NT_ASSERT(NT_SUCCESS(Status
));
721 if (FlagOn(FcbOrDcb
->FcbState
, FCB_STATE_8_LOWER_CASE
| FCB_STATE_3_LOWER_CASE
)) {
724 // Have to repair the case of the short name
727 for (i
= 0; i
< (ShortNameWithCase
.Length
/sizeof(WCHAR
)) &&
728 ShortNameWithCase
.Buffer
[i
] != L
'.'; i
++);
731 // Now pointing at the '.', or otherwise the end of name component
734 if (FlagOn(FcbOrDcb
->FcbState
, FCB_STATE_8_LOWER_CASE
)) {
736 DownCaseSeg
.Buffer
= ShortNameWithCase
.Buffer
;
737 DownCaseSeg
.MaximumLength
= DownCaseSeg
.Length
= i
*sizeof(WCHAR
);
739 RtlDowncaseUnicodeString(&DownCaseSeg
, &DownCaseSeg
, FALSE
);
745 // Now pointing at first wchar of the extension.
748 if (FlagOn(FcbOrDcb
->FcbState
, FCB_STATE_3_LOWER_CASE
)) {
751 // It is not neccesarily the case that we can rely on the flag
752 // indicating that we really have an extension.
755 if ((i
*sizeof(WCHAR
)) < ShortNameWithCase
.Length
) {
756 DownCaseSeg
.Buffer
= &ShortNameWithCase
.Buffer
[i
];
757 DownCaseSeg
.MaximumLength
= DownCaseSeg
.Length
= ShortNameWithCase
.Length
- i
*sizeof(WCHAR
);
759 RtlDowncaseUnicodeString(&DownCaseSeg
, &DownCaseSeg
, FALSE
);
768 FsRtlAddToTunnelCache( &FcbOrDcb
->Vcb
->Tunnel
,
769 FatDirectoryKey(FcbOrDcb
->ParentDcb
),
771 &FcbOrDcb
->ExactCaseLongName
,
772 BooleanFlagOn(Ccb
->Flags
, CCB_FLAG_OPENED_BY_SHORTNAME
),
773 sizeof(LARGE_INTEGER
),
774 &FcbOrDcb
->CreationTime
);
777 DebugTrace(-1, Dbg
, "FatTunnelFcbOrDcb -> (VOID)\n", 0);
784 _Requires_lock_held_(_Global_critical_region_
)
787 IN PIRP_CONTEXT IrpContext
,
789 IN PDELETE_CONTEXT DeleteContext OPTIONAL
,
797 This routine Deletes on the disk the indicated dirent. It does
798 this by marking the dirent as deleted.
802 FcbOrDcb - Supplies the FCB/DCB for the file/directory being
803 deleted. For a file the file size and allocation must be zero.
804 (Zero allocation is implied by a zero cluster index).
805 For a directory the allocation must be zero.
807 DeleteContext - This variable, if speicified, may be used to preserve
808 the file size and first cluster of file information in the dirent
809 fot the benefit of unerase utilities.
811 DeleteEa - Tells us whether to delete the EA and whether to check
812 for no allocation/ Mainly TRUE. FALSE passed in from rename.
822 PDIRENT Dirent
= NULL
;
825 ULONG DirentsToDelete
;
829 DebugTrace(+1, Dbg
, "FatDeleteDirent\n", 0);
831 DebugTrace( 0, Dbg
, " FcbOrDcb = %p\n", FcbOrDcb
);
834 // We must be holding the vcb exclusive here to deal with the locate dirent
835 // cases where it cannot be holding the parent simply. This is actually
836 // a true statement from olden daze, lets just wire in our assertion.
838 // Among other reasons, it'd be unfortunate if this raced with the
842 NT_ASSERT( ExIsResourceAcquiredExclusiveLite( &FcbOrDcb
->Vcb
->Resource
));
845 // Assert that we are not attempting this on the root directory.
848 NT_ASSERT( NodeType(FcbOrDcb
) != FAT_NTC_ROOT_DCB
);
851 // Make sure all requests have zero allocation/file size
855 ((FcbOrDcb
->Header
.AllocationSize
.LowPart
!= 0) ||
856 ((NodeType(FcbOrDcb
) == FAT_NTC_FCB
) &&
857 (FcbOrDcb
->Header
.FileSize
.LowPart
!= 0)))) {
859 DebugTrace( 0, Dbg
, "Called with non zero allocation/file size.\n", 0);
862 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
864 FatBugCheck( 0, 0, 0 );
868 // Now, mark the dirents deleted, unpin the Bcb, and return to the caller.
869 // Assert that there isn't any allocation associated with this dirent.
871 // Note that this loop will end with Dirent pointing to the short name.
877 // We must acquire our parent exclusive to synchronize with enumerators
878 // who do not hold the vcb (ex: dirctrl).
880 // This relies on our bottom up lockorder.
883 ExAcquireResourceExclusiveLite( FcbOrDcb
->ParentDcb
->Header
.Resource
, TRUE
);
885 for ( Offset
= FcbOrDcb
->LfnOffsetWithinDirectory
;
886 Offset
<= FcbOrDcb
->DirentOffsetWithinDirectory
;
887 Offset
+= sizeof(DIRENT
), Dirent
+= 1 ) {
890 // If we stepped onto a new page, or this is the first iteration,
891 // unpin the old page, and pin the new one.
894 if ((Offset
== FcbOrDcb
->LfnOffsetWithinDirectory
) ||
895 ((Offset
& (PAGE_SIZE
- 1)) == 0)) {
897 FatUnpinBcb( IrpContext
, Bcb
);
899 FatPrepareWriteDirectoryFile( IrpContext
,
910 NT_ASSERT( (Dirent
->FirstClusterOfFile
== 0) || !DeleteEa
);
911 Dirent
->FileName
[0] = FAT_DIRENT_DELETED
;
915 // Back Dirent off by one to point back to the short dirent.
921 // If there are extended attributes for this dirent, we will attempt
922 // to remove them. We ignore any errors in removing Eas.
925 if (!FatIsFat32(FcbOrDcb
->Vcb
) &&
926 DeleteEa
&& (Dirent
->ExtendedAttributes
!= 0)) {
930 FatDeleteEa( IrpContext
,
932 Dirent
->ExtendedAttributes
,
933 &FcbOrDcb
->ShortName
.Name
.Oem
);
935 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext
, _SEH2_GetExceptionInformation() )) {
938 // We catch all exceptions that Fat catches, but don't do
939 // anything with them.
945 // Now clear the bits in the free dirent mask.
948 DirentsToDelete
= (FcbOrDcb
->DirentOffsetWithinDirectory
-
949 FcbOrDcb
->LfnOffsetWithinDirectory
) / sizeof(DIRENT
) + 1;
952 NT_ASSERT( (FcbOrDcb
->ParentDcb
->Specific
.Dcb
.UnusedDirentVbo
== 0xffffffff) ||
953 RtlAreBitsSet( &FcbOrDcb
->ParentDcb
->Specific
.Dcb
.FreeDirentBitmap
,
954 FcbOrDcb
->LfnOffsetWithinDirectory
/ sizeof(DIRENT
),
957 RtlClearBits( &FcbOrDcb
->ParentDcb
->Specific
.Dcb
.FreeDirentBitmap
,
958 FcbOrDcb
->LfnOffsetWithinDirectory
/ sizeof(DIRENT
),
962 // Now, if the caller specified a DeleteContext, use it.
965 if ( ARGUMENT_PRESENT( DeleteContext
) ) {
967 Dirent
->FileSize
= DeleteContext
->FileSize
;
970 Dirent
->FirstClusterOfFile
= (USHORT
)DeleteContext
->FirstClusterOfFile
;
974 // If this newly deleted dirent is before the DeletedDirentHint, change
975 // the DeletedDirentHint to point here.
978 if (FcbOrDcb
->DirentOffsetWithinDirectory
<
979 FcbOrDcb
->ParentDcb
->Specific
.Dcb
.DeletedDirentHint
) {
981 FcbOrDcb
->ParentDcb
->Specific
.Dcb
.DeletedDirentHint
=
982 FcbOrDcb
->LfnOffsetWithinDirectory
;
987 FatUnpinBcb( IrpContext
, Bcb
);
990 // Release our parent.
993 ExReleaseResourceLite( FcbOrDcb
->ParentDcb
->Header
.Resource
);
996 DebugTrace(-1, Dbg
, "FatDeleteDirent -> (VOID)\n", 0);
1000 _Requires_lock_held_(_Global_critical_region_
)
1002 FatLfnDirentExists (
1003 IN PIRP_CONTEXT IrpContext
,
1005 IN PUNICODE_STRING Lfn
,
1006 IN PUNICODE_STRING LfnTmp
1010 Routine Description:
1012 This routine looks for a given Lfn in a directory
1016 Dcb - The directory to search
1018 Lfn - The Lfn to look for
1020 Lfn - Temporary buffer to use to search for Lfn with (if < MAX_LFN then this
1021 function may cause it to be allocated from pool if not large enough.
1025 BOOLEAN TRUE if it exists, FALSE if not
1031 PBCB DirentBcb
= NULL
;
1032 VBO DirentByteOffset
;
1033 BOOLEAN Result
= FALSE
;
1039 // Pay performance penalty by forcing the compares to be case insensitive as
1040 // opposed to grabbing more pool for a monocased copy of the Lfn. This is slight.
1043 Ccb
.UnicodeQueryTemplate
= *Lfn
;
1044 Ccb
.ContainsWildCards
= FALSE
;
1045 Ccb
.Flags
= CCB_FLAG_SKIP_SHORT_NAME_COMPARE
| CCB_FLAG_QUERY_TEMPLATE_MIXED
;
1049 FatLocateDirent( IrpContext
,
1068 FatUnpinBcb(IrpContext
, DirentBcb
);
1075 _Requires_lock_held_(_Global_critical_region_
)
1078 IN PIRP_CONTEXT IrpContext
,
1079 IN PDCB ParentDirectory
,
1081 IN VBO OffsetToStartSearchFrom
,
1082 IN OUT PULONG Flags
,
1083 OUT PDIRENT
*Dirent
,
1085 OUT PVBO ByteOffset
,
1086 OUT PBOOLEAN FileNameDos OPTIONAL
,
1087 IN OUT PUNICODE_STRING LongFileName OPTIONAL
,
1088 IN OUT PUNICODE_STRING OrigLongFileName OPTIONAL
1093 Routine Description:
1095 This routine locates on the disk an undeleted dirent matching a given name.
1099 ParentDirectory - Supplies the DCB for the directory to search
1101 Ccb - Contains a context control block with all matching information.
1103 OffsetToStartSearchFrom - Supplies the VBO within the parent directory
1104 from which to start looking for another real dirent.
1106 Dirent - Receives a pointer to the located dirent if one was found
1109 Bcb - Receives the Bcb for the located dirent if one was found or
1112 ByteOffset - Receives the VBO within the Parent directory for
1113 the located dirent if one was found, or 0 otherwise.
1115 FileNameDos - Receives TRUE if the element of the dirent we hit on
1116 was the short (non LFN) side
1118 LongFileName - If specified, this parameter returns the long file name
1119 associated with the returned dirent. Note that it is the caller's
1120 responsibility to provide the buffer (and set MaximumLength
1121 accordingly) for this unicode string. The Length field is reset
1122 to 0 by this routine on invocation. If the supplied buffer is not
1123 large enough, a new one will be allocated from pool.
1132 NTSTATUS Status
= STATUS_SUCCESS
;
1135 UCHAR NameBuffer
[12];
1137 BOOLEAN UpcasedLfnValid
= FALSE
;
1138 UNICODE_STRING UpcasedLfn
= {0};
1139 WCHAR LocalLfnBuffer
[32];
1142 BOOLEAN LfnInProgress
= FALSE
;
1143 UCHAR LfnChecksum
= 0;
1147 VBO LfnByteOffset
= 0;
1153 DebugTrace(+1, Dbg
, "FatLocateDirent\n", 0);
1155 DebugTrace( 0, Dbg
, " ParentDirectory = %p\n", ParentDirectory
);
1156 DebugTrace( 0, Dbg
, " OffsetToStartSearchFrom = %08lx\n", OffsetToStartSearchFrom
);
1157 DebugTrace( 0, Dbg
, " Dirent = %p\n", Dirent
);
1158 DebugTrace( 0, Dbg
, " Bcb = %p\n", Bcb
);
1159 DebugTrace( 0, Dbg
, " ByteOffset = %08lx\n", *ByteOffset
);
1162 // We must have acquired the parent or the vcb to synchronize with deletion. This
1163 // is important since we can't survive racing a thread marking a series of lfn
1164 // dirents deleted - we'd get a bogus ordinal, and otherwise get really messed up.
1166 // This routine cannot do the acquire since it would be out-of-order with respect
1167 // to the Bcb resources on iterative calls. Our order has Bcbs as the inferior resource.
1169 // Deletion always grabs the parent (safely - this used to not be possible until the
1170 // multiple fcb lockorder was fixed to be bottom up!). Deletion always occurs with
1171 // the vcb held exclusive as well, and this will cover the cases where we can't easily
1172 // hold the parent here, see above.
1175 NT_ASSERT( ExIsResourceAcquiredSharedLite( ParentDirectory
->Header
.Resource
) ||
1176 ExIsResourceAcquiredExclusiveLite( ParentDirectory
->Header
.Resource
) ||
1177 ExIsResourceAcquiredSharedLite( &ParentDirectory
->Vcb
->Resource
) ||
1178 ExIsResourceAcquiredExclusiveLite( &ParentDirectory
->Vcb
->Resource
));
1181 // The algorithm here is pretty simple. We just walk through the
1182 // parent directory until we:
1184 // A) Find a matching entry.
1186 // C) Hit the End of Directory
1189 // In the first case we found it, in the latter three cases we did not.
1192 UNREFERENCED_PARAMETER( Flags
); // future use
1195 Name
.MaximumLength
= 12;
1196 Name
.Buffer
= (PCHAR
)NameBuffer
;
1198 UpcasedLfn
.Length
= 0;
1199 UpcasedLfn
.MaximumLength
= sizeof( LocalLfnBuffer
);
1200 UpcasedLfn
.Buffer
= LocalLfnBuffer
;
1204 // If we were given a non-NULL Bcb, compute the new Dirent address
1205 // from the prior one, or unpin the Bcb if the new Dirent is not pinned.
1210 if ((OffsetToStartSearchFrom
/ PAGE_SIZE
) == (*ByteOffset
/ PAGE_SIZE
)) {
1212 *Dirent
+= (OffsetToStartSearchFrom
- *ByteOffset
) / sizeof(DIRENT
);
1216 FatUnpinBcb( IrpContext
, *Bcb
);
1221 // Init the Lfn if we were given one.
1224 if (ARGUMENT_PRESENT(LongFileName
)) {
1226 LongFileName
->Length
= 0;
1229 if (ARGUMENT_PRESENT(OrigLongFileName
)) {
1231 OrigLongFileName
->Length
= 0;
1235 // Init the FileNameDos flag
1240 *FileNameDos
= FALSE
;
1244 // Round up OffsetToStartSearchFrom to the nearest Dirent, and store
1245 // in ByteOffset. Note that this wipes out the prior value.
1248 *ByteOffset
= (OffsetToStartSearchFrom
+ (sizeof(DIRENT
) - 1))
1249 & ~(sizeof(DIRENT
) - 1);
1255 BOOLEAN FoundValidLfn
;
1257 UpcasedLfnValid
= FALSE
;
1261 // Try to read in the dirent
1264 FatReadDirent( IrpContext
,
1272 // If End Directory dirent or EOF, set all out parameters to
1273 // indicate entry not found and, like, bail.
1275 // Note that the order of evaluation here is important since we
1276 // cannot check the first character of the dirent until after we
1277 // know we are not beyond EOF
1280 if ((Status
== STATUS_END_OF_FILE
) ||
1281 ((*Dirent
)->FileName
[0] == FAT_DIRENT_NEVER_USED
)) {
1283 DebugTrace( 0, Dbg
, "End of directory: entry not found.\n", 0);
1286 // If there is a Bcb, unpin it and set it to null
1289 FatUnpinBcb( IrpContext
, *Bcb
);
1297 // If the entry is marked deleted, skip. If there was an Lfn in
1298 // progress we throw it out at this point.
1301 if ((*Dirent
)->FileName
[0] == FAT_DIRENT_DELETED
) {
1303 LfnInProgress
= FALSE
;
1308 // If we have wandered onto an LFN entry, try to interpret it.
1311 if (FatData
.ChicagoMode
&&
1312 ARGUMENT_PRESENT(LongFileName
) &&
1313 ((*Dirent
)->Attributes
== FAT_DIRENT_ATTR_LFN
)) {
1317 Lfn
= (PLFN_DIRENT
)*Dirent
;
1319 if (LfnInProgress
) {
1322 // Check for a proper continuation of the Lfn in progress.
1325 if ((Lfn
->Ordinal
& FAT_LAST_LONG_ENTRY
) ||
1326 (Lfn
->Ordinal
== 0) ||
1327 (Lfn
->Ordinal
!= Ordinal
- 1) ||
1328 (Lfn
->Checksum
!= LfnChecksum
) ||
1329 (Lfn
->MustBeZero
!= 0)) {
1332 // The Lfn is not proper, stop constructing it.
1335 LfnInProgress
= FALSE
;
1339 NT_ASSERT( ((LfnIndex
% 13) == 0) && LfnIndex
);
1343 RtlCopyMemory( &LongFileName
->Buffer
[LfnIndex
+0],
1347 RtlCopyMemory( &LongFileName
->Buffer
[LfnIndex
+5],
1349 6 * sizeof(WCHAR
) );
1351 RtlCopyMemory( &LongFileName
->Buffer
[LfnIndex
+11],
1353 2 * sizeof(WCHAR
) );
1355 Ordinal
= Lfn
->Ordinal
;
1356 LfnByteOffset
= *ByteOffset
;
1361 // Now check (maybe again) if we should analyze this entry
1362 // for a possible last entry.
1365 if ((!LfnInProgress
) &&
1366 (Lfn
->Ordinal
& FAT_LAST_LONG_ENTRY
) &&
1367 ((Lfn
->Ordinal
& ~FAT_LAST_LONG_ENTRY
) <= MAX_LFN_DIRENTS
) &&
1368 (Lfn
->MustBeZero
== 0)) {
1370 BOOLEAN CheckTail
= FALSE
;
1372 Ordinal
= Lfn
->Ordinal
& ~FAT_LAST_LONG_ENTRY
;
1375 // We're usually permissive (following the lead of Win9x) when we find
1376 // malformation of the LFN dirent pile. I'm not sure this is a good idea,
1377 // so I'm going to trigger corruption on this particularly ugly one. Perhaps
1378 // we should come back and redo the original code here with this in mind in the
1385 // First LFN in the pile was zero marked as the last. This is never
1386 // possible since oridinals are 1-based.
1389 FatPopUpFileCorrupt( IrpContext
, ParentDirectory
);
1390 FatRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
1393 LfnIndex
= (Ordinal
- 1) * 13;
1395 FatEnsureStringBufferEnough( LongFileName
,
1396 (USHORT
)((LfnIndex
+ 13) << 1));
1398 RtlCopyMemory( &LongFileName
->Buffer
[LfnIndex
+0],
1402 RtlCopyMemory( &LongFileName
->Buffer
[LfnIndex
+5],
1404 6 * sizeof(WCHAR
) );
1406 RtlCopyMemory( &LongFileName
->Buffer
[LfnIndex
+11],
1408 2 * sizeof(WCHAR
) );
1411 // Now compute the Lfn size and make sure that the tail
1412 // bytes are correct.
1415 while (LfnIndex
!= (ULONG
)Ordinal
* 13) {
1419 if (LongFileName
->Buffer
[LfnIndex
] == 0x0000) {
1427 if (LongFileName
->Buffer
[LfnIndex
] != 0xffff) {
1437 // If we exited this loop prematurely, the LFN is not valid.
1440 if (LfnIndex
== (ULONG
)Ordinal
* 13) {
1443 // If we didn't find the NULL terminator, then the size
1453 LfnInProgress
= TRUE
;
1454 LfnChecksum
= Lfn
->Checksum
;
1455 LfnByteOffset
= *ByteOffset
;
1460 // Move on to the next dirent.
1467 // If this is the volume label, skip. Note that we never arrive here
1468 // while building the LFN. If we did, we weren't asked to find LFNs
1469 // and that is another good reason to skip this LFN fragment.
1472 if (FlagOn((*Dirent
)->Attributes
, FAT_DIRENT_ATTR_VOLUME_ID
)) {
1475 // If we actually were asked to hand back volume labels,
1479 if (FlagOn(Ccb
->Flags
, CCB_FLAG_MATCH_VOLUME_ID
)) {
1488 // We may have just stepped off a valid Lfn run. Check to see if
1489 // it is indeed valid for the following dirent.
1492 if (LfnInProgress
&&
1493 (*ByteOffset
== LfnByteOffset
+ sizeof(DIRENT
)) &&
1495 (FatComputeLfnChecksum(*Dirent
) == LfnChecksum
)) {
1497 NT_ASSERT( Ordinal
== 1);
1499 FoundValidLfn
= TRUE
;
1500 LongFileName
->Length
= (USHORT
)(LfnSize
* sizeof(WCHAR
));
1503 if (ARGUMENT_PRESENT(OrigLongFileName
)) {
1504 *OrigLongFileName
= *LongFileName
;
1509 FoundValidLfn
= FALSE
;
1515 // If we are supposed to match all entries, then match this entry.
1518 if (FlagOn(Ccb
->Flags
, CCB_FLAG_MATCH_ALL
)) {
1524 // Check against the short name given if one was.
1527 if (!FlagOn( Ccb
->Flags
, CCB_FLAG_SKIP_SHORT_NAME_COMPARE
)) {
1529 if (Ccb
->ContainsWildCards
) {
1532 // If we get one, note that all out parameters are already set.
1535 (VOID
)Fat8dot3ToString( IrpContext
, (*Dirent
), FALSE
, &Name
);
1538 // For fat we special case the ".." dirent because we want it to
1539 // match ????????.??? and to do that we change ".." to "." before
1540 // calling the Fsrtl routine. But only do this if the expression
1541 // is greater than one character long.
1544 if ((Name
.Length
== 2) &&
1545 (Name
.Buffer
[0] == '.') &&
1546 (Name
.Buffer
[1] == '.') &&
1547 (Ccb
->OemQueryTemplate
.Wild
.Length
> 1)) {
1552 if (FatIsNameInExpression( IrpContext
,
1553 Ccb
->OemQueryTemplate
.Wild
,
1556 DebugTrace( 0, Dbg
, "Entry found: Name = \"%Z\"\n", &Name
);
1557 DebugTrace( 0, Dbg
, " VBO = %08lx\n", *ByteOffset
);
1561 *FileNameDos
= TRUE
;
1564 SetFlag( Ccb
->Flags
, CCB_FLAG_OPENED_BY_SHORTNAME
);
1572 // Do the quickest 8.3 equivalency check possible
1575 if (!FlagOn((*Dirent
)->Attributes
, FAT_DIRENT_ATTR_VOLUME_ID
) &&
1576 (*(PULONG
)&(Ccb
->OemQueryTemplate
.Constant
[0]) == *(PULONG
)&((*Dirent
)->FileName
[0])) &&
1577 (*(PULONG
)&(Ccb
->OemQueryTemplate
.Constant
[4]) == *(PULONG
)&((*Dirent
)->FileName
[4])) &&
1578 (*(PUSHORT
)&(Ccb
->OemQueryTemplate
.Constant
[8]) == *(PUSHORT
)&((*Dirent
)->FileName
[8])) &&
1579 (*(PUCHAR
)&(Ccb
->OemQueryTemplate
.Constant
[10]) == *(PUCHAR
)&((*Dirent
)->FileName
[10]))) {
1581 DebugTrace( 0, Dbg
, "Entry found.\n", 0);
1585 *FileNameDos
= TRUE
;
1588 SetFlag( Ccb
->Flags
, CCB_FLAG_OPENED_BY_SHORTNAME
);
1596 // No matches were found with the short name. If an LFN exists,
1597 // use it for the search.
1600 if (FoundValidLfn
) {
1604 // First do a quick check here for different sized constant
1605 // name and expression before upcasing.
1608 if (!Ccb
->ContainsWildCards
&&
1609 (Ccb
->UnicodeQueryTemplate
.Length
!= (USHORT
)(LfnSize
* sizeof(WCHAR
)))) {
1612 // Move on to the next dirent.
1615 FoundValidLfn
= FALSE
;
1616 LongFileName
->Length
= 0;
1617 if (OrigLongFileName
) {
1618 OrigLongFileName
->Length
= 0;
1626 if (!UpcasedLfnValid
) {
1629 // We need to upcase the name we found on disk.
1630 // We need a buffer. Try to avoid doing an allocation.
1633 FatEnsureStringBufferEnough( &UpcasedLfn
,
1634 LongFileName
->Length
);
1636 Status
= RtlUpcaseUnicodeString( &UpcasedLfn
,
1640 if (!NT_SUCCESS(Status
)) {
1642 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
1646 UpcasedLfnValid
= TRUE
;
1654 if (Ccb
->ContainsWildCards
) {
1656 if (FsRtlIsNameInExpression( &Ccb
->UnicodeQueryTemplate
,
1666 if (FsRtlAreNamesEqual( &Ccb
->UnicodeQueryTemplate
,
1668 BooleanFlagOn( Ccb
->Flags
, CCB_FLAG_QUERY_TEMPLATE_MIXED
),
1680 // This long name was not a match. Zero out the Length field.
1683 if (FoundValidLfn
) {
1685 FoundValidLfn
= FALSE
;
1686 LongFileName
->Length
= 0;
1689 if (OrigLongFileName
) {
1690 OrigLongFileName
->Length
= 0;
1697 // Move on to the next dirent.
1700 *ByteOffset
+= sizeof(DIRENT
);
1706 FatFreeStringBuffer( &UpcasedLfn
);
1711 DebugTrace(-1, Dbg
, "FatLocateDirent -> (VOID)\n", 0);
1713 TimerStop(Dbg
,"FatLocateDirent");
1719 _Requires_lock_held_(_Global_critical_region_
)
1721 FatLocateSimpleOemDirent (
1722 IN PIRP_CONTEXT IrpContext
,
1723 IN PDCB ParentDirectory
,
1724 IN POEM_STRING FileName
,
1725 OUT PDIRENT
*Dirent
,
1732 Routine Description:
1734 This routine locates on the disk an undelted simple Oem dirent. By simple
1735 I mean that FileName cannot contain any extended characters, and we do
1736 not search LFNs or return them.
1740 ParentDirectory - Supplies the DCB for the directory in which
1743 FileName - Supplies the filename to search for. The name may contain
1746 OffsetToStartSearchFrom - Supplies the VBO within the parent directory
1747 from which to start looking for another real dirent.
1749 Dirent - Receives a pointer to the located dirent if one was found
1752 Bcb - Receives the Bcb for the located dirent if one was found or
1755 ByteOffset - Receives the VBO within the Parent directory for
1756 the located dirent if one was found, or 0 otherwise.
1770 // Note, this routine is called rarely, so performance is not critical.
1771 // Just fill in a Ccb structure on my stack with the values that are
1775 FatStringTo8dot3( IrpContext
,
1777 &LocalCcb
.OemQueryTemplate
.Constant
);
1778 LocalCcb
.ContainsWildCards
= FALSE
;
1781 FatLocateDirent( IrpContext
,
1798 _Requires_lock_held_(_Global_critical_region_
)
1800 FatLocateVolumeLabel (
1801 IN PIRP_CONTEXT IrpContext
,
1803 OUT PDIRENT
*Dirent
,
1810 Routine Description:
1812 This routine locates on the disk a dirent representing the volume
1813 label. It does this by searching the root directory for a special
1814 volume label dirent.
1818 Vcb - Supplies the VCB for the volume to search
1820 Dirent - Receives a pointer to the located dirent if one was found
1823 Bcb - Receives the Bcb for the located dirent if one was found or
1826 ByteOffset - Receives the VBO within the Parent directory for
1827 the located dirent if one was found, or 0 otherwise.
1836 NTSTATUS Status
= STATUS_SUCCESS
;
1840 DebugTrace(+1, Dbg
, "FatLocateVolumeLabel\n", 0);
1842 DebugTrace( 0, Dbg
, " Vcb = %p\n", Vcb
);
1843 DebugTrace( 0, Dbg
, " Dirent = %p\n", Dirent
);
1844 DebugTrace( 0, Dbg
, " Bcb = %p\n", Bcb
);
1845 DebugTrace( 0, Dbg
, " ByteOffset = %08lx\n", *ByteOffset
);
1848 // The algorithm here is really simple. We just walk through the
1849 // root directory until we:
1851 // A) Find the non-deleted volume label
1853 // C) Hit the End of Directory
1856 // In the first case we found it, in the latter three cases we did not.
1865 // Try to read in the dirent
1868 FatReadDirent( IrpContext
,
1876 // If End Directory dirent or EOF, set all out parameters to
1877 // indicate volume label not found and, like, bail.
1879 // Note that the order of evaluation here is important since we cannot
1880 // check the first character of the dirent until after we know we
1881 // are not beyond EOF
1884 if ((Status
== STATUS_END_OF_FILE
) ||
1885 ((*Dirent
)->FileName
[0] == FAT_DIRENT_NEVER_USED
)) {
1887 DebugTrace( 0, Dbg
, "Volume label not found.\n", 0);
1890 // If there is a Bcb, unpin it and set it to null
1893 FatUnpinBcb( IrpContext
, *Bcb
);
1901 // If the entry is the non-deleted volume label break from the loop.
1903 // Note that all out parameters are already correctly set.
1906 if ((((*Dirent
)->Attributes
& ~FAT_DIRENT_ATTR_ARCHIVE
) == FAT_DIRENT_ATTR_VOLUME_ID
) &&
1907 ((*Dirent
)->FileName
[0] != FAT_DIRENT_DELETED
)) {
1909 DebugTrace( 0, Dbg
, "Volume label found at VBO = %08lx\n", *ByteOffset
);
1912 // We may set this dirty, so pin it.
1915 FatPinMappedData( IrpContext
,
1925 // Move on to the next dirent.
1928 *ByteOffset
+= sizeof(DIRENT
);
1933 DebugTrace(-1, Dbg
, "FatLocateVolumeLabel -> (VOID)\n", 0);
1940 _Requires_lock_held_(_Global_critical_region_
)
1942 FatGetDirentFromFcbOrDcb (
1943 IN PIRP_CONTEXT IrpContext
,
1945 IN BOOLEAN ReturnOnFailure
,
1946 OUT PDIRENT
*Dirent
,
1952 Routine Description:
1954 This routine reads locates on the disk the dirent denoted by the
1959 FcbOrDcb - Supplies the FCB/DCB for the file/directory whose dirent
1960 we are trying to read in. This must not be the root dcb.
1962 Dirent - Receives a pointer to the dirent
1964 Bcb - Receives the Bcb for the dirent
1973 NTSTATUS DontCare
= STATUS_SUCCESS
;
1977 DebugTrace(+1, Dbg
, "FatGetDirentFromFcbOrDcb\n", 0);
1979 DebugTrace( 0, Dbg
, " FcbOrDcb = %p\n", FcbOrDcb
);
1980 DebugTrace( 0, Dbg
, " Dirent = %p\n", Dirent
);
1981 DebugTrace( 0, Dbg
, " Bcb = %p\n", Bcb
);
1984 // Assert that we are not attempting this on the root directory.
1987 NT_ASSERT( NodeType(FcbOrDcb
) != FAT_NTC_ROOT_DCB
);
1990 // We know the offset of the dirent within the directory file,
1991 // so we just read it (with pinning).
1994 FatReadDirectoryFile( IrpContext
,
1995 FcbOrDcb
->ParentDcb
,
1996 FcbOrDcb
->DirentOffsetWithinDirectory
,
2004 // Previous call can fail. We used to assert success, but we use this
2005 // as part of volume verification (DetermineAndMarkFcbCondition) after
2006 // media has been removed. Clearly the directory could shrink and we
2007 // would try to read beyond filesize.
2009 // The caller will note this via NULL pointers for Bcb/Buffer. Note that
2010 // both asserts below are OK since this should never happen fixed media.
2012 // This was a Prefix catch.
2015 NT_ASSERT( FlagOn( FcbOrDcb
->Vcb
->VcbState
, VCB_STATE_FLAG_REMOVABLE_MEDIA
) ||
2016 NT_SUCCESS( DontCare
));
2019 // Note also that the only way this could fail is if the Fcb was being
2020 // verified. This can't happen if the Fcb is in good condition.
2022 // Also a Prefix catch.
2025 NT_ASSERT( NT_SUCCESS( DontCare
) || FcbOrDcb
->FcbCondition
== FcbNeedsToBeVerified
);
2028 // This should never happen except in very specific cases (during volume
2029 // verify) but we'll handle and raise here to save all callers checking the
2033 if ((NULL
== *Dirent
) && !ReturnOnFailure
) {
2036 FatRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
2039 DebugTrace(-1, Dbg
, "FatGetDirentFromFcbOrDcb -> (VOID)\n", 0);
2044 _Requires_lock_held_(_Global_critical_region_
)
2046 FatIsDirectoryEmpty (
2047 IN PIRP_CONTEXT IrpContext
,
2053 Routine Description:
2055 This routine indicates to the caller if the specified directory
2056 is empty. (i.e., it is not the root dcb and it only contains
2057 the "." and ".." entries, or deleted files).
2061 Dcb - Supplies the DCB for the directory being queried.
2065 BOOLEAN - Returns TRUE if the directory is empty and
2066 FALSE if the directory and is not empty.
2073 PDIRENT Dirent
= NULL
;
2075 BOOLEAN IsDirectoryEmpty
= FALSE
;
2077 NTSTATUS Status
= STATUS_SUCCESS
;
2081 DebugTrace(+1, Dbg
, "FatIsDirectoryEmpty\n", 0);
2083 DebugTrace( 0, Dbg
, " Dcb = %p\n", Dcb
);
2084 DebugTrace( 0, Dbg
, " IsDirectoryEmpty = %08lx\n", IsDirectoryEmpty
);
2087 // Check to see if the first entry is an and of directory marker.
2088 // For the root directory we check at Vbo = 0, for normal directories
2089 // we check after the "." and ".." entries.
2092 ByteOffset
= (NodeType(Dcb
) == FAT_NTC_ROOT_DCB
) ? 0 : 2*sizeof(DIRENT
);
2095 // We just march through the directory looking for anything other
2096 // than deleted files, LFNs, an EOF, or end of directory marker.
2106 // Try to read in the dirent
2109 FatReadDirent( IrpContext
,
2117 // If End Directory dirent or EOF, set IsDirectoryEmpty to TRUE and,
2120 // Note that the order of evaluation here is important since we cannot
2121 // check the first character of the dirent until after we know we
2122 // are not beyond EOF
2125 if ((Status
== STATUS_END_OF_FILE
) ||
2126 (Dirent
->FileName
[0] == FAT_DIRENT_NEVER_USED
)) {
2128 DebugTrace( 0, Dbg
, "Empty. Last exempt entry at VBO = %08lx\n", ByteOffset
);
2130 IsDirectoryEmpty
= TRUE
;
2135 // If this dirent is NOT deleted or an LFN set IsDirectoryEmpty to
2136 // FALSE and, like, bail.
2139 if ((Dirent
->FileName
[0] != FAT_DIRENT_DELETED
) &&
2140 (Dirent
->Attributes
!= FAT_DIRENT_ATTR_LFN
)) {
2149 // Move on to the next dirent.
2152 ByteOffset
+= sizeof(DIRENT
);
2158 FatUnpinBcb( IrpContext
, Bcb
);
2161 DebugTrace(-1, Dbg
, "FatIsDirectoryEmpty -> %ld\n", IsDirectoryEmpty
);
2163 return IsDirectoryEmpty
;
2171 FatConstructDirent (
2172 IN PIRP_CONTEXT IrpContext
,
2173 IN OUT PDIRENT Dirent
,
2174 IN POEM_STRING FileName
,
2175 IN BOOLEAN ComponentReallyLowercase
,
2176 IN BOOLEAN ExtensionReallyLowercase
,
2177 IN PUNICODE_STRING Lfn OPTIONAL
,
2178 IN USHORT Attributes
,
2179 IN BOOLEAN ZeroAndSetTimeFields
,
2180 IN PLARGE_INTEGER SetCreationTime OPTIONAL
2185 Routine Description:
2187 This routine modifies the fields of a dirent.
2191 Dirent - Supplies the dirent being modified.
2193 FileName - Supplies the name to store in the Dirent. This
2194 name must not contain wildcards.
2196 ComponentReallyLowercase - This boolean indicates that the User Specified
2197 compoent name was really all a-z and < 0x80 characters. We set the
2198 magic bit in this case.
2200 ExtensionReallyLowercase - Same as above, but for the extension.
2202 Lfn - May supply a long file name.
2204 Attributes - Supplies the attributes to store in the dirent
2206 ZeroAndSetTimeFields - Tells whether or not to initially zero the dirent
2207 and update the time fields.
2209 SetCreationTime - If specified, contains a timestamp to use as the creation
2221 DebugTrace(+1, Dbg
, "FatConstructDirent\n", 0);
2223 DebugTrace( 0, Dbg
, " Dirent = %p\n", Dirent
);
2224 DebugTrace( 0, Dbg
, " FileName = %Z\n", FileName
);
2225 DebugTrace( 0, Dbg
, " Attributes = %08lx\n", Attributes
);
2227 if (ZeroAndSetTimeFields
) {
2229 RtlZeroMemory( Dirent
, sizeof(DIRENT
) );
2233 // We just merrily go and fill up the dirent with the fields given.
2236 FatStringTo8dot3( IrpContext
, *FileName
, (PFAT8DOT3
)&Dirent
->FileName
[0] );
2238 if (ZeroAndSetTimeFields
|| SetCreationTime
) {
2240 LARGE_INTEGER Time
, SaveTime
;
2242 KeQuerySystemTime( &Time
);
2244 if (FatData
.ChicagoMode
) {
2246 if (!SetCreationTime
|| !FatNtTimeToFatTime( IrpContext
,
2249 &Dirent
->CreationTime
,
2250 &Dirent
->CreationMSec
)) {
2253 // No tunneled time or the tunneled time was bogus. Since we aren't
2254 // responsible for initializing the to-be-created Fcb with creation
2255 // time, we can't do the usual thing and let NtTimeToFatTime perform
2256 // rounding on the timestamp - this would mess up converting to the
2257 // LastWriteTime below.
2262 if (!FatNtTimeToFatTime( IrpContext
,
2265 &Dirent
->CreationTime
,
2266 &Dirent
->CreationMSec
)) {
2269 // Failed again. Wow.
2272 RtlZeroMemory( &Dirent
->CreationTime
, sizeof(FAT_TIME_STAMP
));
2273 Dirent
->CreationMSec
= 0;
2278 if (ZeroAndSetTimeFields
) {
2281 // We only touch the other timestamps if we are initializing the dirent
2284 if (!FatNtTimeToFatTime( IrpContext
,
2287 &Dirent
->LastWriteTime
,
2290 DebugTrace( 0, Dbg
, "Current time invalid.\n", 0);
2292 RtlZeroMemory( &Dirent
->LastWriteTime
, sizeof(FAT_TIME_STAMP
) );
2295 if (FatData
.ChicagoMode
) {
2297 Dirent
->LastAccessDate
= Dirent
->LastWriteTime
.Date
;
2303 // Copy the attributes
2306 Dirent
->Attributes
= (UCHAR
)Attributes
;
2309 // Set the magic bit here, to tell dirctrl.c that this name is really
2315 if (ComponentReallyLowercase
) {
2317 SetFlag( Dirent
->NtByte
, FAT_DIRENT_NT_BYTE_8_LOWER_CASE
);
2320 if (ExtensionReallyLowercase
) {
2322 SetFlag( Dirent
->NtByte
, FAT_DIRENT_NT_BYTE_3_LOWER_CASE
);
2326 // See if we have to create an Lfn entry
2329 if (ARGUMENT_PRESENT(Lfn
)) {
2331 UCHAR DirentChecksum
;
2335 PLFN_DIRENT LfnDirent
;
2337 NT_ASSERT( FatData
.ChicagoMode
);
2339 DirentChecksum
= FatComputeLfnChecksum( Dirent
);
2342 DirentsInLfn
= (UCHAR
)FAT_LFN_DIRENTS_NEEDED(Lfn
);
2344 LfnBuffer
= &Lfn
->Buffer
[(DirentsInLfn
- 1) * 13];
2346 NT_ASSERT( DirentsInLfn
<= MAX_LFN_DIRENTS
);
2348 for (LfnDirent
= (PLFN_DIRENT
)Dirent
- DirentsInLfn
;
2349 LfnDirent
< (PLFN_DIRENT
)Dirent
;
2350 LfnDirent
+= 1, LfnOrdinal
-= 1, LfnBuffer
-= 13) {
2352 WCHAR FinalLfnBuffer
[13];
2356 // We need to special case the "final" dirent.
2359 if (LfnOrdinal
== DirentsInLfn
) {
2362 ULONG RemainderChars
;
2364 RemainderChars
= (Lfn
->Length
/ sizeof(WCHAR
)) % 13;
2366 LfnDirent
->Ordinal
= LfnOrdinal
| FAT_LAST_LONG_ENTRY
;
2368 if (RemainderChars
!= 0) {
2370 RtlCopyMemory( FinalLfnBuffer
,
2372 RemainderChars
* sizeof(WCHAR
) );
2374 for (i
= RemainderChars
; i
< 13; i
++) {
2377 // Figure out which character to use.
2380 if (i
== RemainderChars
) {
2382 FinalLfnBuffer
[i
] = 0x0000;
2386 FinalLfnBuffer
[i
] = 0xffff;
2390 Buffer
= FinalLfnBuffer
;
2399 LfnDirent
->Ordinal
= LfnOrdinal
;
2405 // Now fill in the name.
2408 RtlCopyMemory( &LfnDirent
->Name1
[0],
2410 5 * sizeof(WCHAR
) );
2412 RtlCopyMemory( &LfnDirent
->Name2
[0],
2414 6 * sizeof(WCHAR
) );
2416 RtlCopyMemory( &LfnDirent
->Name3
[0],
2418 2 * sizeof(WCHAR
) );
2421 // And the other fields
2424 LfnDirent
->Attributes
= FAT_DIRENT_ATTR_LFN
;
2426 LfnDirent
->Type
= 0;
2428 LfnDirent
->Checksum
= DirentChecksum
;
2430 LfnDirent
->MustBeZero
= 0;
2434 DebugTrace(-1, Dbg
, "FatConstructDirent -> (VOID)\n", 0);
2440 FatConstructLabelDirent (
2441 IN PIRP_CONTEXT IrpContext
,
2442 IN OUT PDIRENT Dirent
,
2443 IN POEM_STRING Label
2448 Routine Description:
2450 This routine modifies the fields of a dirent to be used for a label.
2454 Dirent - Supplies the dirent being modified.
2456 Label - Supplies the name to store in the Dirent. This
2457 name must not contain wildcards.
2468 DebugTrace(+1, Dbg
, "FatConstructLabelDirent\n", 0);
2470 DebugTrace( 0, Dbg
, " Dirent = %p\n", Dirent
);
2471 DebugTrace( 0, Dbg
, " Label = %Z\n", Label
);
2473 RtlZeroMemory( Dirent
, sizeof(DIRENT
) );
2476 // We just merrily go and fill up the dirent with the fields given.
2479 RtlCopyMemory( Dirent
->FileName
, Label
->Buffer
, Label
->Length
);
2482 // Pad the label with spaces, not nulls.
2485 RtlFillMemory( &Dirent
->FileName
[Label
->Length
], 11 - Label
->Length
, ' ');
2487 Dirent
->LastWriteTime
= FatGetCurrentFatTime( IrpContext
);
2489 Dirent
->Attributes
= FAT_DIRENT_ATTR_VOLUME_ID
;
2490 Dirent
->ExtendedAttributes
= 0;
2491 Dirent
->FileSize
= 0;
2493 DebugTrace(-1, Dbg
, "FatConstructLabelDirent -> (VOID)\n", 0);
2498 _Requires_lock_held_(_Global_critical_region_
)
2500 FatSetFileSizeInDirent (
2501 IN PIRP_CONTEXT IrpContext
,
2503 IN PULONG AlternativeFileSize OPTIONAL
2508 Routine Description:
2510 This routine saves the file size in an fcb into its dirent.
2514 Fcb - Supplies the Fcb being referenced
2516 AlternativeFileSize - If non-null we use the ULONG it points to as
2517 the new file size. Otherwise we use the one in the Fcb.
2531 NT_ASSERT( Fcb
->FcbCondition
== FcbGood
);
2533 FatGetDirentFromFcbOrDcb( IrpContext
,
2540 Dirent
->FileSize
= ARGUMENT_PRESENT( AlternativeFileSize
) ?
2541 *AlternativeFileSize
: Fcb
->Header
.FileSize
.LowPart
;
2544 FatSetDirtyBcb( IrpContext
, DirentBcb
, Fcb
->Vcb
, TRUE
);
2548 FatUnpinBcb( IrpContext
, DirentBcb
);
2552 _Requires_lock_held_(_Global_critical_region_
)
2554 FatSetFileSizeInDirentNoRaise (
2555 IN PIRP_CONTEXT IrpContext
,
2557 IN PULONG AlternativeFileSize OPTIONAL
2562 Routine Description:
2564 This routine saves the file size in an fcb into its dirent.
2565 All exceptions thrown from FatSetFileSizeInDirent are
2570 Fcb - Supplies the Fcb being referenced
2572 AlternativeFileSize - If non-null we use the ULONG it points to as
2573 the new file size. Otherwise we use the one in the Fcb.
2584 FatSetFileSizeInDirent( IrpContext
, Fcb
, AlternativeFileSize
);
2586 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext
, _SEH2_GetExceptionInformation() )) {
2593 _Requires_lock_held_(_Global_critical_region_
)
2595 FatUpdateDirentFromFcb (
2596 IN PIRP_CONTEXT IrpContext
,
2597 IN PFILE_OBJECT FileObject
,
2605 Routine Description:
2607 This routine modifies an objects directory entry based on the hints
2608 that have been built up over previous operations on a handle. Notify
2609 change filters are built and fired as a result of these updates.
2613 FileObject - Fileobject representing the handle involved
2615 FcbOrDcb - File/Dir involved
2617 Ccb - User context involved
2626 BOOLEAN SetArchiveBit
;
2628 BOOLEAN UpdateFileSize
;
2629 BOOLEAN UpdateLastWriteTime
;
2630 BOOLEAN UpdateLastAccessTime
;
2631 BOOLEAN UpdateDirent
= FALSE
;
2634 PBCB DirentBcb
= NULL
;
2635 ULONG NotifyFilter
= 0;
2637 FAT_TIME_STAMP CurrentFatTime
= {0};
2639 FAT_TIME_STAMP CurrentFatTime
= {{0}};
2642 LARGE_INTEGER CurrentTime
;
2644 LARGE_INTEGER CurrentDay
= {0};
2646 LARGE_INTEGER CurrentDay
= {.LowPart
= 0, .HighPart
= 0};
2648 LARGE_INTEGER LastAccessDay
;
2653 // Nothing to do if the fcb is bad, volume is readonly or we got the
2657 if (FcbOrDcb
->FcbCondition
!= FcbGood
||
2658 NodeType(FcbOrDcb
) == FAT_NTC_ROOT_DCB
||
2659 FlagOn(FcbOrDcb
->Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
)) {
2665 // Check if we should be changing the time or file size and set
2666 // the archive bit on the file.
2669 KeQuerySystemTime( &CurrentTime
);
2672 // Note that we HAVE to use BooleanFlagOn() here because
2673 // FO_FILE_SIZE_CHANGED > 0x80 (i.e., not in the first byte).
2676 SetArchiveBit
= BooleanFlagOn(FileObject
->Flags
, FO_FILE_MODIFIED
);
2678 UpdateLastWriteTime
= FlagOn(FileObject
->Flags
, FO_FILE_MODIFIED
) &&
2679 !FlagOn(Ccb
->Flags
, CCB_FLAG_USER_SET_LAST_WRITE
);
2681 UpdateFileSize
= NodeType(FcbOrDcb
) == FAT_NTC_FCB
&&
2682 BooleanFlagOn(FileObject
->Flags
, FO_FILE_SIZE_CHANGED
);
2685 // Do one further check here of access time. Only update it if
2686 // the current version is at least one day old. We know that
2687 // the current FcbOrDcb->LastAccessTime corresponds to 12 midnight local
2688 // time, so just see if the current time is on the same day.
2691 if (FatData
.ChicagoMode
&&
2692 (UpdateLastWriteTime
||
2693 FlagOn(FileObject
->Flags
, FO_FILE_FAST_IO_READ
)) &&
2694 !FlagOn(Ccb
->Flags
, CCB_FLAG_USER_SET_LAST_ACCESS
)) {
2696 ExSystemTimeToLocalTime( &FcbOrDcb
->LastAccessTime
, &LastAccessDay
);
2697 ExSystemTimeToLocalTime( &CurrentTime
, &CurrentDay
);
2699 LastAccessDay
.QuadPart
/= FatOneDay
.QuadPart
;
2700 CurrentDay
.QuadPart
/= FatOneDay
.QuadPart
;
2702 if (LastAccessDay
.LowPart
!= CurrentDay
.LowPart
) {
2704 UpdateLastAccessTime
= TRUE
;
2708 UpdateLastAccessTime
= FALSE
;
2713 UpdateLastAccessTime
= FALSE
;
2716 if (SetArchiveBit
||
2718 UpdateLastWriteTime
||
2719 UpdateLastAccessTime
2722 DebugTrace(0, Dbg
, "Update Time and/or file size on File/Dir\n", 0);
2728 #if (NTDDI_VERSION >= NTDDI_WIN8)
2730 // Break parent directory oplock. Directory oplock breaks are
2731 // always advisory, so we will never block/get STATUS_PENDING here.
2734 if (FcbOrDcb
->ParentDcb
!= NULL
) {
2736 FsRtlCheckOplockEx( FatGetFcbOplock(FcbOrDcb
->ParentDcb
),
2737 IrpContext
->OriginatingIrp
,
2738 OPLOCK_FLAG_PARENT_OBJECT
,
2749 FatGetDirentFromFcbOrDcb( IrpContext
,
2755 if (UpdateLastWriteTime
|| UpdateLastAccessTime
) {
2757 (VOID
)FatNtTimeToFatTime( IrpContext
,
2764 if (SetArchiveBit
) {
2766 Dirent
->Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
2767 FcbOrDcb
->DirentFatFlags
|= FILE_ATTRIBUTE_ARCHIVE
;
2769 NotifyFilter
|= FILE_NOTIFY_CHANGE_ATTRIBUTES
;
2770 UpdateDirent
= TRUE
;
2773 if (UpdateLastWriteTime
) {
2776 // Update its time of last write
2779 FcbOrDcb
->LastWriteTime
= CurrentTime
;
2780 Dirent
->LastWriteTime
= CurrentFatTime
;
2783 // We call the notify package to report that the
2784 // last modification time has changed.
2787 NotifyFilter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
2788 UpdateDirent
= TRUE
;
2791 if (UpdateLastAccessTime
) {
2794 // Now we have to truncate the local time down
2795 // to the current day, then convert back to UTC.
2798 FcbOrDcb
->LastAccessTime
.QuadPart
=
2799 CurrentDay
.QuadPart
* FatOneDay
.QuadPart
;
2801 ExLocalTimeToSystemTime( &FcbOrDcb
->LastAccessTime
,
2802 &FcbOrDcb
->LastAccessTime
);
2804 Dirent
->LastAccessDate
= CurrentFatTime
.Date
;
2807 // We call the notify package to report that the
2808 // last access time has changed.
2811 NotifyFilter
|= FILE_NOTIFY_CHANGE_LAST_ACCESS
;
2812 UpdateDirent
= TRUE
;
2815 if (UpdateFileSize
) {
2818 // Perhaps we were called to make certain that the
2819 // filesize on disc was updated - don't bother updating
2820 // and firing the filter if nothing changed.
2823 NT_ASSERT( NodeType(FcbOrDcb
) == FAT_NTC_FCB
);
2825 if (Dirent
->FileSize
!= FcbOrDcb
->Header
.FileSize
.LowPart
) {
2828 // Update the dirent file size
2831 Dirent
->FileSize
= FcbOrDcb
->Header
.FileSize
.LowPart
;
2834 // We call the notify package to report that the
2835 // size has changed.
2838 NotifyFilter
|= FILE_NOTIFY_CHANGE_SIZE
;
2839 UpdateDirent
= TRUE
;
2846 FatNotifyReportChange( IrpContext
,
2850 FILE_ACTION_MODIFIED
);
2855 // If all we did was update last access time,
2856 // don't mark the volume dirty.
2859 FatSetDirtyBcb( IrpContext
,
2861 NotifyFilter
== FILE_NOTIFY_CHANGE_LAST_ACCESS
?
2862 NULL
: FcbOrDcb
->Vcb
,
2866 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
2867 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
2869 FatResetExceptionState( IrpContext
);
2874 FatUnpinBcb( IrpContext
, DirentBcb
);
2882 // Internal support routine
2886 FatComputeLfnChecksum (
2892 Routine Description:
2894 This routine computes the Chicago long file name checksum.
2898 Dirent - Specifies the dirent that we are to compute a checksum for.
2912 Checksum
= Dirent
->FileName
[0];
2914 for (i
=1; i
< 11; i
++) {
2916 Checksum
= ((Checksum
& 1) ? 0x80 : 0) +
2918 Dirent
->FileName
[i
];
2926 #if 0 // It turns out Win95 is still creating short names without a ~
2929 // Internal support routine
2941 Routine Description:
2943 This routine does a few more checks to make sure that a LFN/short
2944 name pairing is legitimate. Basically this is the test:
2946 Pairing is valid if:
2948 DIRENT has a ~ character ||
2949 (LFN is 8.3 compliant &&
2950 (LFN has extended character(s) ? TRUE :
2951 LFN upcases to DIRENT))
2953 When checking for the presence of a tilda character in the short
2954 name, note that we purposely do a single byte search instead of
2955 converting the name to UNICODE and looking there for the tilda.
2956 This protects us from accidently missing the tilda if the
2957 preceding byte is a lead byte in the current Oem code page,
2958 but wasn't in the Oem code page that created the file.
2960 Also note that if the LFN is longer than 12 characters, then the
2961 second clause of the OR must be false.
2965 Lfn - Points to a buffer of UNICODE chars.
2967 LfnSize - This is the size of the LFN in characters.
2969 Dirent - Specifies the dirent we are to consider.
2973 TRUE if the Lfn/DIRENT form a legitimate pair, FALSE otherwise.
2979 BOOLEAN ExtendedChars
;
2980 ULONG DirentBuffer
[3];
2983 BOOLEAN DotEncountered
;
2986 // First, look for a tilda
2989 for (i
=0; i
<11; i
++) {
2990 if (Dirent
->FileName
[i
] == '~') {
2996 // No tilda. If the LFN is longer than 12 characters, then it can
2997 // neither upcase to the DIRENT nor be 8.3 complient.
3005 // Now see if the name is 8.3, and build an upcased DIRENT as well.
3008 DirentBuffer
[0] = 0x20202020;
3009 DirentBuffer
[1] = 0x20202020;
3010 DirentBuffer
[2] = 0x20202020;
3012 DirentName
= (PUCHAR
)DirentBuffer
;
3014 ExtendedChars
= FALSE
;
3016 DotEncountered
= FALSE
;
3018 for (i
=0; i
< LfnSize
; i
++) {
3021 // Do dot transition work
3024 if (Lfn
[i
] == L
'.') {
3025 if (DotEncountered
||
3027 ((LfnSize
- i
) > 4) ||
3028 (i
&& Lfn
[i
-1] == L
' ')) {
3031 DotEncountered
= TRUE
;
3037 // The character must be legal in order to be 8.3
3040 if ((Lfn
[i
] < 0x80) &&
3041 !FsRtlIsAnsiCharacterLegalFat((UCHAR
)Lfn
[i
], FALSE
)) {
3046 // If the name contains no extended chars, continue building DIRENT
3049 if (!ExtendedChars
) {
3050 if (Lfn
[i
] > 0x7f) {
3051 ExtendedChars
= TRUE
;
3053 DirentName
[DirentIndex
++] = (UCHAR
) (
3054 Lfn
[i
] < 'a' ? Lfn
[i
] : Lfn
[i
] <= 'z' ? Lfn
[i
] - ('a' - 'A') : Lfn
[i
]);
3060 // If the LFN ended in a space, or there was no dot and the name
3061 // has more than 8 characters, then it is not 8.3 compliant.
3064 if ((Lfn
[LfnSize
- 1] == L
' ') ||
3065 (!DotEncountered
&& (LfnSize
> 8))) {
3070 // OK, now if we got this far then the LFN is 8dot3. If there are
3071 // no extended characters, then we can also check to make sure that
3072 // the LFN is only a case varient of the DIRENT.
3075 if (!ExtendedChars
&&
3076 !RtlEqualMemory(Dirent
->FileName
, DirentName
, 11)) {
3082 // We have now verified this pairing the very best we can without
3083 // knowledge of the code page that the file was created under.
3091 // Internal support routine
3094 _Requires_lock_held_(_Global_critical_region_
)
3096 FatRescanDirectory (
3097 PIRP_CONTEXT IrpContext
,
3103 Routine Description:
3105 This routine rescans the given directory, finding the first unused
3106 dirent, first deleted dirent, and setting the free dirent bitmap
3111 Dcb - Supplies the directory to rescan.
3121 PDIRENT Dirent
= NULL
;
3122 NTSTATUS Status
= STATUS_SUCCESS
;
3127 ULONG DirentsThisRun
;
3128 ULONG StartIndexOfThisRun
;
3138 DebugTrace( 0, Dbg
, "We must scan the whole directory.\n", 0);
3141 DeletedHint
= 0xffffffff;
3144 // To start with, we have to find out if the first dirent is free.
3147 CurrentRun
= InitialRun
;
3149 StartIndexOfThisRun
= 0;
3155 BOOLEAN DirentDeleted
;
3161 FatReadDirent( IrpContext
,
3169 // If EOF, or we found a NEVER_USED entry, we exit the loop
3172 if ( (Status
== STATUS_END_OF_FILE
) ||
3173 (Dirent
->FileName
[0] == FAT_DIRENT_NEVER_USED
)) {
3179 // If the dirent is DELETED, and it is the first one we found, set
3180 // it in the deleted hint.
3183 if (Dirent
->FileName
[0] == FAT_DIRENT_DELETED
) {
3185 DirentDeleted
= TRUE
;
3187 if (DeletedHint
== 0xffffffff) {
3189 DeletedHint
= UnusedVbo
;
3194 DirentDeleted
= FALSE
;
3198 // Check for the first time through the loop, and determine
3199 // the current run type.
3202 if (CurrentRun
== InitialRun
) {
3204 CurrentRun
= DirentDeleted
?
3205 FreeDirents
: AllocatedDirents
;
3210 // Are we switching from a free run to an allocated run?
3213 if ((CurrentRun
== FreeDirents
) && !DirentDeleted
) {
3215 DirentsThisRun
= DirentIndex
- StartIndexOfThisRun
;
3217 RtlClearBits( &Dcb
->Specific
.Dcb
.FreeDirentBitmap
,
3218 StartIndexOfThisRun
,
3221 CurrentRun
= AllocatedDirents
;
3222 StartIndexOfThisRun
= DirentIndex
;
3226 // Are we switching from an allocated run to a free run?
3229 if ((CurrentRun
== AllocatedDirents
) && DirentDeleted
) {
3231 DirentsThisRun
= DirentIndex
- StartIndexOfThisRun
;
3233 RtlSetBits( &Dcb
->Specific
.Dcb
.FreeDirentBitmap
,
3234 StartIndexOfThisRun
,
3237 CurrentRun
= FreeDirents
;
3238 StartIndexOfThisRun
= DirentIndex
;
3243 // Move on to the next dirent.
3246 UnusedVbo
+= sizeof(DIRENT
);
3252 // Now we have to record the final run we encoutered
3255 DirentsThisRun
= DirentIndex
- StartIndexOfThisRun
;
3257 if ((CurrentRun
== FreeDirents
) || (CurrentRun
== InitialRun
)) {
3259 RtlClearBits( &Dcb
->Specific
.Dcb
.FreeDirentBitmap
,
3260 StartIndexOfThisRun
,
3265 RtlSetBits( &Dcb
->Specific
.Dcb
.FreeDirentBitmap
,
3266 StartIndexOfThisRun
,
3271 // Now if there we bailed prematurely out of the loop because
3272 // we hit an unused entry, set all the rest as free.
3275 if (UnusedVbo
< Dcb
->Header
.AllocationSize
.LowPart
) {
3277 StartIndexOfThisRun
= UnusedVbo
/ sizeof(DIRENT
);
3279 DirentsThisRun
= (Dcb
->Header
.AllocationSize
.LowPart
-
3280 UnusedVbo
) / sizeof(DIRENT
);
3282 RtlClearBits( &Dcb
->Specific
.Dcb
.FreeDirentBitmap
,
3283 StartIndexOfThisRun
,
3289 FatUnpinBcb( IrpContext
, Bcb
);
3293 // If there weren't any DELETED entries, set the index to our current
3297 if (DeletedHint
== 0xffffffff) { DeletedHint
= UnusedVbo
; }
3299 Dcb
->Specific
.Dcb
.UnusedDirentVbo
= UnusedVbo
;
3300 Dcb
->Specific
.Dcb
.DeletedDirentHint
= DeletedHint
;
3307 // Internal support routine
3310 _Requires_lock_held_(_Global_critical_region_
)
3312 FatDefragDirectory (
3313 IN PIRP_CONTEXT IrpContext
,
3315 IN ULONG DirentsNeeded
3320 Routine Description:
3322 This routine determines if the requested number of dirents can be found
3323 in the directory, looking for deleted dirents and orphaned LFNs. If the
3324 request can be satisifed, orphaned LFNs are marked as deleted, and deleted
3325 dirents are all grouped together at the end of the directory.
3327 Note that this routine is currently used only on the root directory, but
3328 it is completely general and could be used on any directory.
3332 Dcb - Supplies the directory to defrag.
3336 The Index of the first dirent available for use, or -1 if the
3337 request cannot be satisfied.
3342 ULONG SavedIrpContextFlag
;
3344 ULONG ReturnValue
= 0;
3348 PDIRENT Dirent
= NULL
;
3349 UNICODE_STRING Lfn
= {0,0,NULL
};
3352 BOOLEAN McbInitialized
= FALSE
;
3353 BOOLEAN InvalidateFcbs
= FALSE
;
3355 PUCHAR Directory
= NULL
;
3356 PUCHAR UnusedDirents
;
3357 PUCHAR UnusedDirentBuffer
= NULL
;
3359 PUCHAR UsedDirentBuffer
= NULL
;
3363 ULONG PagesPinned
= 0;
3366 ULONG TotalBytesAllocated
= 0;
3371 // We assume we own the Vcb.
3374 NT_ASSERT( FatVcbAcquiredExclusive(IrpContext
, Dcb
->Vcb
) );
3377 // We will only attempt this on directories less than 0x40000 bytes
3378 // long (by default on DOS the root directory is only 0x2000 long).
3379 // This is to avoid a cache manager complication.
3382 DcbSize
= Dcb
->Header
.AllocationSize
.LowPart
;
3384 if (DcbSize
> 0x40000) {
3390 // Force wait to TRUE
3393 SavedIrpContextFlag
= IrpContext
->Flags
;
3395 SetFlag( IrpContext
->Flags
,
3396 IRP_CONTEXT_FLAG_WAIT
| IRP_CONTEXT_FLAG_WRITE_THROUGH
);
3399 // Now acquire all open Fcbs in the Dcb exclusive.
3402 for (Links
= Dcb
->Specific
.Dcb
.ParentDcbQueue
.Flink
;
3403 Links
!= &Dcb
->Specific
.Dcb
.ParentDcbQueue
;
3404 Links
= Links
->Flink
) {
3406 Fcb
= CONTAINING_RECORD( Links
, FCB
, ParentDcbLinks
);
3408 (VOID
)ExAcquireResourceExclusiveLite( Fcb
->Header
.Resource
, TRUE
);
3414 ULONG QueryOffset
= 0;
3415 ULONG FoundOffset
= 0;
3416 ULONGLONG BytesUsed
= 0;
3425 // We are going to build a new bitmap that will show all orphaned
3426 // LFNs as well as deleted dirents as available.
3428 // Initialize our local CCB that will match all files and even
3429 // a label if it is here.
3432 RtlZeroMemory( &Ccb
, sizeof(CCB
) );
3433 Ccb
.Flags
= CCB_FLAG_MATCH_ALL
| CCB_FLAG_MATCH_VOLUME_ID
;
3436 // Init the Long File Name string.
3439 Lfn
.MaximumLength
= 260 * sizeof(WCHAR
);
3440 Lfn
.Buffer
= FsRtlAllocatePoolWithTag( PagedPool
,
3442 TAG_FILENAME_BUFFER
);
3445 // Initalize the Mcb. We use this structure to keep track of runs
3446 // of free and allocated dirents. Runs are identity allocations, and
3447 // holes are free dirents.
3450 FsRtlInitializeLargeMcb( &Mcb
, PagedPool
);
3452 McbInitialized
= TRUE
;
3456 FatLocateDirent( IrpContext
,
3468 if (Dirent
!= NULL
) {
3470 ULONG LfnByteOffset
;
3473 // Compute the LfnByteOffset.
3476 LfnByteOffset
= FoundOffset
-
3477 FAT_LFN_DIRENTS_NEEDED(&Lfn
) * sizeof(LFN_DIRENT
);
3479 BytesUsed
= FoundOffset
- LfnByteOffset
+ sizeof(DIRENT
);
3482 // Set a run to represent all the dirents used for this
3483 // file in the Dcb dir.
3487 #pragma prefast( suppress:28931, "needed for debug build" )
3489 Result
= FsRtlAddLargeMcbEntry( &Mcb
,
3494 NT_ASSERT( Result
);
3497 // Move on to the next dirent.
3500 TotalBytesAllocated
+= (ULONG
) BytesUsed
;
3501 QueryOffset
= FoundOffset
+ sizeof(DIRENT
);
3504 } while ((Dirent
!= NULL
) && (QueryOffset
< DcbSize
));
3508 FatUnpinBcb( IrpContext
, Bcb
);
3512 // If we need more dirents than are available, bail.
3515 if (DirentsNeeded
> (DcbSize
- TotalBytesAllocated
)/sizeof(DIRENT
)) {
3517 try_return(ReturnValue
= (ULONG
)-1);
3521 // Now we are going to copy all the used and un-used parts of the
3522 // directory to separate pool.
3524 // Allocate these buffers and pin the entire directory.
3528 UnusedDirentBuffer
= FsRtlAllocatePoolWithTag( PagedPool
,
3529 DcbSize
- TotalBytesAllocated
,
3533 UsedDirentBuffer
= FsRtlAllocatePoolWithTag( PagedPool
,
3534 TotalBytesAllocated
,
3537 PagesPinned
= (DcbSize
+ (PAGE_SIZE
- 1 )) / PAGE_SIZE
;
3539 Bcbs
= FsRtlAllocatePoolWithTag( PagedPool
,
3540 PagesPinned
* sizeof(PBCB
),
3543 RtlZeroMemory( Bcbs
, PagesPinned
* sizeof(PBCB
) );
3545 for (Page
= 0; Page
< PagesPinned
; Page
+= 1) {
3550 // Don't try to pin beyond the Dcb size.
3553 if ((Page
+ 1) * PAGE_SIZE
> DcbSize
) {
3555 PinSize
= DcbSize
- (Page
* PAGE_SIZE
);
3559 PinSize
= PAGE_SIZE
;
3562 FatPrepareWriteDirectoryFile( IrpContext
,
3577 Directory
= (PUCHAR
)Dirent
;
3581 TotalRuns
= FsRtlNumberOfRunsInLargeMcb( &Mcb
);
3583 for (Run
= 0; Run
< TotalRuns
; Run
++) {
3589 #pragma prefast( suppress:28931, "needed for debug build" )
3591 Result
= FsRtlGetNextLargeMcbEntry( &Mcb
,
3595 (PLONGLONG
)&BytesUsed
);
3600 // Copy each run to their specific pool.
3605 RtlCopyMemory( UsedDirents
,
3607 (ULONG
) BytesUsed
);
3609 UsedDirents
+= BytesUsed
;
3613 RtlCopyMemory( UnusedDirents
,
3615 (ULONG
) BytesUsed
);
3617 UnusedDirents
+= BytesUsed
;
3622 // Marking all the un-used dirents as "deleted". This will reclaim
3623 // storage used by orphaned LFNs.
3626 for (Char
= UnusedDirentBuffer
; Char
< UnusedDirents
; Char
+= sizeof(DIRENT
)) {
3628 *Char
= FAT_DIRENT_DELETED
;
3632 // Now, for the permanent step. Copy the two pool buffer back to the
3633 // real Dcb directory, and flush the Dcb directory
3636 NT_ASSERT( TotalBytesAllocated
== (ULONG
)(UsedDirents
- UsedDirentBuffer
) );
3638 RtlCopyMemory( Directory
, UsedDirentBuffer
, TotalBytesAllocated
);
3640 RtlCopyMemory( Directory
+ TotalBytesAllocated
,
3642 UnusedDirents
- UnusedDirentBuffer
);
3645 // We need to unpin here so that the UnpinRepinned won't deadlock.
3649 for (Page
= 0; Page
< PagesPinned
; Page
+= 1) {
3650 FatUnpinBcb( IrpContext
, Bcbs
[Page
] );
3657 // Now make the free dirent bitmap reflect the new state of the Dcb
3661 RtlSetBits( &Dcb
->Specific
.Dcb
.FreeDirentBitmap
,
3663 TotalBytesAllocated
/ sizeof(DIRENT
) );
3665 RtlClearBits( &Dcb
->Specific
.Dcb
.FreeDirentBitmap
,
3666 TotalBytesAllocated
/ sizeof(DIRENT
),
3667 (DcbSize
- TotalBytesAllocated
) / sizeof(DIRENT
) );
3669 ReturnValue
= TotalBytesAllocated
/ sizeof(DIRENT
);
3672 // Flush the directory to disk. If we raise, we will need to invalidate
3673 // all of the children. Sorry, guys, but I can't figure out where you are
3674 // now - if this failed I probably can't read the media either. And we
3675 // probably purged the cache to boot.
3680 FatUnpinRepinnedBcbs( IrpContext
);
3682 } _SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
3683 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
3685 InvalidateFcbs
= TRUE
;
3689 // OK, now nothing can go wrong. We have two more things to do.
3690 // First, we have to fix up all the dirent offsets in any open Fcbs.
3691 // If we cannot now find the Fcb, the file is marked invalid. Also,
3692 // we skip deleted files.
3695 for (Links
= Dcb
->Specific
.Dcb
.ParentDcbQueue
.Flink
;
3696 Links
!= &Dcb
->Specific
.Dcb
.ParentDcbQueue
;
3697 Links
= Links
->Flink
) {
3700 ULONG TmpOffset
= 0;
3701 PDIRENT TmpDirent
= NULL
;
3702 ULONG PreviousLfnSpread
;
3704 Fcb
= CONTAINING_RECORD( Links
, FCB
, ParentDcbLinks
);
3706 if (IsFileDeleted( IrpContext
, Fcb
)) {
3712 // If we aren't already giving up, safely try to pick up the dirent
3713 // to update the Fcb. If this raises, we have to give up and blow
3714 // evenyone else away too.
3717 if (!InvalidateFcbs
) {
3721 FatLocateSimpleOemDirent( IrpContext
,
3723 &Fcb
->ShortName
.Name
.Oem
,
3728 } _SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
3729 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
3731 InvalidateFcbs
= TRUE
;
3735 if (TmpBcb
== NULL
|| InvalidateFcbs
) {
3737 FatUnpinBcb( IrpContext
, TmpBcb
);
3738 FatMarkFcbCondition( IrpContext
, Fcb
, FcbBad
, TRUE
);
3742 FatUnpinBcb( IrpContext
, TmpBcb
);
3744 PreviousLfnSpread
= Fcb
->DirentOffsetWithinDirectory
-
3745 Fcb
->LfnOffsetWithinDirectory
;
3747 Fcb
->DirentOffsetWithinDirectory
= TmpOffset
;
3748 Fcb
->LfnOffsetWithinDirectory
= TmpOffset
- PreviousLfnSpread
;
3756 // Free all our resources and stuff.
3759 if (McbInitialized
) {
3760 FsRtlUninitializeLargeMcb( &Mcb
);
3764 ExFreePool( Lfn
.Buffer
);
3767 if (UnusedDirentBuffer
) {
3768 ExFreePool( UnusedDirentBuffer
);
3771 if (UsedDirentBuffer
) {
3772 ExFreePool( UsedDirentBuffer
);
3776 for (Page
= 0; Page
< PagesPinned
; Page
+= 1) {
3777 FatUnpinBcb( IrpContext
, Bcbs
[Page
] );
3782 FatUnpinBcb( IrpContext
, Bcb
);
3784 for (Links
= Dcb
->Specific
.Dcb
.ParentDcbQueue
.Flink
;
3785 Links
!= &Dcb
->Specific
.Dcb
.ParentDcbQueue
;
3786 Links
= Links
->Flink
) {
3788 Fcb
= CONTAINING_RECORD( Links
, FCB
, ParentDcbLinks
);
3790 ExReleaseResourceLite( Fcb
->Header
.Resource
);
3793 IrpContext
->Flags
= SavedIrpContextFlag
;
3797 // Now return the offset of the first free dirent to the caller.