1 /*++
2 
3 Copyright (c) 1989-2000 Microsoft Corporation
4 
5 Module Name:
6 
7     DirSup.c
8 
9 Abstract:
10 
11     This module implements the dirent support routines for Fat.
12 
13 
14 --*/
15 
16 #include "fatprocs.h"
17 
18 //
19 //  The Bug check file id for this module
20 //
21 
22 #define BugCheckFileId                   (FAT_BUG_CHECK_DIRSUP)
23 
24 //
25 //  Local debug trace level
26 //
27 
28 #define Dbg                              (DEBUG_TRACE_DIRSUP)
29 
30 //
31 //  The following three macro all assume the input dirent has been zeroed.
32 //
33 
34 //
35 //  VOID
36 //  FatConstructDot (
37 //      IN PIRP_CONTEXT IrpContext,
38 //      IN PDCB Directory,
39 //      IN PDIRENT ParentDirent,
40 //      IN OUT PDIRENT Dirent
41 //      );
42 //
43 //  The following macro is called to initalize the "." dirent.
44 //
45 //  Always setting FirstClusterOfFileHi is OK because it will be zero
46 //  unless we're working on a FAT 32 disk.
47 //
48 
49 #define FatConstructDot(IRPCONTEXT,DCB,PARENT,DIRENT) {                  \
50                                                                          \
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;             \
58     }                                                                    \
59     (DIRENT)->FirstClusterOfFile =                                       \
60             (USHORT)(DCB)->FirstClusterOfFile;                           \
61     (DIRENT)->FirstClusterOfFileHi =                                     \
62             (USHORT)((DCB)->FirstClusterOfFile/0x10000);                 \
63 }
64 
65 //
66 //  VOID
67 //  FatConstructDotDot (
68 //      IN PIRP_CONTEXT IrpContext,
69 //      IN PDCB Directory,
70 //      IN PDIRENT ParentDirent,
71 //      IN OUT PDIRENT Dirent
72 //      );
73 //
74 //  The following macro is called to initalize the ".." dirent.
75 //
76 //  Always setting FirstClusterOfFileHi is OK because it will be zero
77 //  unless we're working on a FAT 32 disk.
78 //
79 
80 #define FatConstructDotDot(IRPCONTEXT,DCB,PARENT,DIRENT) {   \
81                                                              \
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; \
89     }                                                        \
90     if (NodeType((DCB)->ParentDcb) == FAT_NTC_ROOT_DCB) {    \
91         (DIRENT)->FirstClusterOfFile = 0;                    \
92         (DIRENT)->FirstClusterOfFileHi = 0;                  \
93     } else {                                                 \
94         (DIRENT)->FirstClusterOfFile = (USHORT)              \
95             ((DCB)->ParentDcb->FirstClusterOfFile);          \
96         (DIRENT)->FirstClusterOfFileHi = (USHORT)            \
97             ((DCB)->ParentDcb->FirstClusterOfFile/0x10000);  \
98     }                                                        \
99 }
100 
101 //
102 //  VOID
103 //  FatConstructEndDirent (
104 //      IN PIRP_CONTEXT IrpContext,
105 //      IN OUT PDIRENT Dirent
106 //      );
107 //
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.
111 //
112 
113 #define FatConstructEndDirent(IRPCONTEXT,DIRENT) NOTHING
114 
115 //
116 //  VOID
117 //  FatReadDirent (
118 //      IN PIRP_CONTEXT IrpContext,
119 //      IN PDCB Dcb,
120 //      IN VBO Vbo,
121 //      OUT PBCB *Bcb,
122 //      OUT PVOID *Dirent,
123 //      OUT PNTSTATUS Status
124 //      );
125 //
126 
127 //
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.
130 //
131 
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),                           \
139                           (DCB),                                  \
140                           (VBO) & ~(PAGE_SIZE - 1),               \
141                           PAGE_SIZE,                              \
142                           FALSE,                                  \
143                           (BCB),                                  \
144                           (PVOID *)(DIRENT),                      \
145                           (STATUS) );                             \
146     *(DIRENT) = (PVOID)((PUCHAR)*(DIRENT) + ((VBO) % PAGE_SIZE)); \
147 }
148 
149 //
150 //  Internal support routines
151 //
152 
153 UCHAR
154 FatComputeLfnChecksum (
155     PDIRENT Dirent
156     );
157 
158 _Requires_lock_held_(_Global_critical_region_)
159 VOID
160 FatRescanDirectory (
161     PIRP_CONTEXT IrpContext,
162     PDCB Dcb
163     );
164 
165 _Requires_lock_held_(_Global_critical_region_)
166 ULONG
167 FatDefragDirectory (
168     IN PIRP_CONTEXT IrpContext,
169     IN PDCB Dcb,
170     IN ULONG DirentsNeeded
171     );
172 
173 
174 #ifdef ALLOC_PRAGMA
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)
193 
194 
195 #endif
196 
197 
_Requires_lock_held_(_Global_critical_region_)198 _Requires_lock_held_(_Global_critical_region_)
199 ULONG
200 FatCreateNewDirent (
201     IN PIRP_CONTEXT IrpContext,
202     IN PDCB ParentDirectory,
203     IN ULONG DirentsNeeded,
204     IN BOOLEAN RescanDir
205     )
206 
207 /*++
208 
209 Routine Description:
210 
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.
216 
217 Arguments:
218 
219     ParentDirectory - Supplies the DCB for the directory in which
220         to create the new dirent
221 
222     DirentsNeeded - This is the number of continginous dirents required
223 
224 Return Value:
225 
226     ByteOffset - Returns the VBO within the Parent directory where
227         the dirent has been allocated
228 
229 --*/
230 
231 {
232     VBO UnusedVbo;
233     VBO DeletedHint;
234     ULONG ByteOffset;
235 
236     PBCB Bcb = NULL;
237     PDIRENT Dirent = NULL;
238     NTSTATUS Status = STATUS_SUCCESS;
239 
240     PAGED_CODE();
241 
242     DebugTrace(+1, Dbg, "FatCreateNewDirent\n", 0);
243 
244     DebugTrace( 0, Dbg, "  ParentDirectory = %p\n", ParentDirectory);
245 
246     //
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
249     //  where to put it.
250     //
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
259     //  do just this.
260     //
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.
266     //
267 
268     UnusedVbo = ParentDirectory->Specific.Dcb.UnusedDirentVbo;
269     DeletedHint = ParentDirectory->Specific.Dcb.DeletedDirentHint;
270 
271     //
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.
274     //
275 
276     if (UnusedVbo == 0xffffffff || RescanDir) {
277 
278         FatRescanDirectory( IrpContext, ParentDirectory );
279 
280         UnusedVbo = ParentDirectory->Specific.Dcb.UnusedDirentVbo;
281         DeletedHint = ParentDirectory->Specific.Dcb.DeletedDirentHint;
282     }
283 
284     //
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.
288     //
289 
290     DebugTrace( 0, Dbg, "  UnusedVbo   = %08lx\n", UnusedVbo);
291     DebugTrace( 0, Dbg, "  DeletedHint = %08lx\n", DeletedHint);
292 
293     if (!RescanDir && ( UnusedVbo + (DirentsNeeded * sizeof(DIRENT)) <=
294          ParentDirectory->Header.AllocationSize.LowPart )) {
295 
296         //
297         //  Get this unused dirent for the caller.  We have a
298         //  sporting chance that we won't have to wait.
299         //
300 
301         DebugTrace( 0, Dbg, "There is a never used entry.\n", 0);
302 
303         ByteOffset = UnusedVbo;
304 
305         UnusedVbo += DirentsNeeded * sizeof(DIRENT);
306 
307     } else {
308 
309         //
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.
313         //
314 
315         ByteOffset =
316             RtlFindClearBits( &ParentDirectory->Specific.Dcb.FreeDirentBitmap,
317                               DirentsNeeded,
318                               DeletedHint / sizeof(DIRENT) );
319 
320         //
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.
326         //
327         //  Don't try to defrag fat32 root dirs.
328         //
329 
330         if (!FatIsFat32(ParentDirectory->Vcb) &&
331             (ByteOffset == -1) &&
332             (NodeType(ParentDirectory) == FAT_NTC_ROOT_DCB) &&
333             (ParentDirectory->Header.AllocationSize.LowPart <= 0x40000)) {
334 
335             ByteOffset = FatDefragDirectory( IrpContext, ParentDirectory, DirentsNeeded );
336         }
337 
338         if (ByteOffset != -1) {
339 
340             //
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.
344             //
345 
346             ByteOffset *= sizeof(DIRENT);
347 
348             if (ByteOffset == DeletedHint) {
349 
350                 DeletedHint += DirentsNeeded * sizeof(DIRENT);
351             }
352 
353             if (ByteOffset + DirentsNeeded * sizeof(DIRENT) > UnusedVbo) {
354 
355                 UnusedVbo = ByteOffset + DirentsNeeded * sizeof(DIRENT);
356             }
357 
358         } else {
359 
360             //
361             //  We are going to have to allocate another cluster.  Do
362             //  so, update both the UnusedVbo and the DeletedHint and bail.
363             //
364 
365             DebugTrace( 0, Dbg, "We have to allocate another cluster.\n", 0);
366 
367             //
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.
373             //
374 
375             if (ParentDirectory->Header.AllocationSize.LowPart >= (64 * 1024 * sizeof(DIRENT)) ||
376 
377                 //
378                 //  Make sure we are not trying to expand the root directory on non
379                 //  FAT32.  FAT16 and FAT12 have fixed size allocations.
380                 //
381 
382                 (!FatIsFat32(ParentDirectory->Vcb) &&
383                  NodeType(ParentDirectory) == FAT_NTC_ROOT_DCB)) {
384 
385                 DebugTrace(0, Dbg, "Full root directory or too big on FAT32.  Raise Status.\n", 0);
386 
387                 FatRaiseStatus( IrpContext, STATUS_CANNOT_MAKE );
388             }
389 
390             //
391             //  Take the last dirent(s) in this cluster.  We will allocate
392             //  more clusters below.
393             //
394 
395             ByteOffset = UnusedVbo;
396             UnusedVbo += DirentsNeeded * sizeof(DIRENT);
397 
398             //
399             //  Touch the directory file to cause space for the new dirents
400             //  to be allocated.
401             //
402 
403             Bcb = NULL;
404 
405             _SEH2_TRY {
406 
407                 PVOID Buffer;
408 
409                 FatPrepareWriteDirectoryFile( IrpContext,
410                                               ParentDirectory,
411                                               UnusedVbo,
412                                               1,
413                                               &Bcb,
414                                               &Buffer,
415                                               FALSE,
416                                               TRUE,
417                                               &Status );
418 
419             } _SEH2_FINALLY {
420 
421                 FatUnpinBcb( IrpContext, Bcb );
422             } _SEH2_END;
423         }
424     }
425 
426     //
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.
431     //
432     //  Only do this when we are in Chicago Mode.
433     //
434 
435     Bcb = NULL;
436 
437     if (FatData.ChicagoMode &&
438         (DirentsNeeded == 1) &&
439         (ByteOffset > (NodeType(ParentDirectory) == FAT_NTC_ROOT_DCB ?
440                        0 : 2 * sizeof(DIRENT)))) {
441         _SEH2_TRY {
442 
443             FatReadDirent( IrpContext,
444                            ParentDirectory,
445                            ByteOffset - sizeof(DIRENT),
446                            &Bcb,
447                            &Dirent,
448                            &Status );
449 
450             if ((Status != STATUS_SUCCESS) ||
451                 (Dirent->FileName[0] == FAT_DIRENT_NEVER_USED)) {
452 
453                 FatPopUpFileCorrupt( IrpContext, ParentDirectory );
454 
455                 FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
456             }
457 
458             if ((Dirent->Attributes == FAT_DIRENT_ATTR_LFN) &&
459                 (Dirent->FileName[0] != FAT_DIRENT_DELETED)) {
460 
461                 //
462                 //  Pin it, mark it, and set it dirty.
463                 //
464 
465                 FatPinMappedData( IrpContext,
466                                   ParentDirectory,
467                                   ByteOffset - sizeof(DIRENT),
468                                   sizeof(DIRENT),
469                                   &Bcb );
470 
471                 Dirent->FileName[0] = FAT_DIRENT_DELETED;
472 
473                 FatSetDirtyBcb( IrpContext, Bcb, ParentDirectory->Vcb, TRUE );
474 
475                 NT_ASSERT( RtlAreBitsSet( &ParentDirectory->Specific.Dcb.FreeDirentBitmap,
476                                        (ByteOffset - sizeof(DIRENT))/ sizeof(DIRENT),
477                                        DirentsNeeded ) );
478 
479                 RtlClearBits( &ParentDirectory->Specific.Dcb.FreeDirentBitmap,
480                               (ByteOffset - sizeof(DIRENT))/ sizeof(DIRENT),
481                               DirentsNeeded );
482 
483             }
484 
485         } _SEH2_FINALLY {
486 
487             FatUnpinBcb( IrpContext, Bcb );
488         } _SEH2_END;
489     }
490 
491     //
492     //  Assert that the dirents are in fact unused
493     //
494 
495     _SEH2_TRY {
496 
497         ULONG i;
498 
499         Bcb = NULL;
500 
501         for (i = 0; i < DirentsNeeded; i++) {
502 
503             FatReadDirent( IrpContext,
504                            ParentDirectory,
505                            ByteOffset + i*sizeof(DIRENT),
506                            &Bcb,
507                            &Dirent,
508                            &Status );
509 
510             if ((Status != STATUS_SUCCESS) ||
511                 ((Dirent->FileName[0] != FAT_DIRENT_NEVER_USED) &&
512                  (Dirent->FileName[0] != FAT_DIRENT_DELETED))) {
513 
514                 FatPopUpFileCorrupt( IrpContext, ParentDirectory );
515                 FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
516             }
517         }
518 
519     } _SEH2_FINALLY {
520 
521         FatUnpinBcb( IrpContext, Bcb );
522     } _SEH2_END;
523 
524     //
525     //  Set the Bits in the bitmap and move the Unused Dirent Vbo.
526     //
527 
528     NT_ASSERT( RtlAreBitsClear( &ParentDirectory->Specific.Dcb.FreeDirentBitmap,
529                              ByteOffset / sizeof(DIRENT),
530                              DirentsNeeded ) );
531 
532     RtlSetBits( &ParentDirectory->Specific.Dcb.FreeDirentBitmap,
533                 ByteOffset / sizeof(DIRENT),
534                 DirentsNeeded );
535 
536     //
537     //  Save the newly computed values in the Parent Directory Fcb
538     //
539 
540     ParentDirectory->Specific.Dcb.UnusedDirentVbo = UnusedVbo;
541     ParentDirectory->Specific.Dcb.DeletedDirentHint = DeletedHint;
542 
543     DebugTrace(-1, Dbg, "FatCreateNewDirent -> (VOID)\n", 0);
544 
545     return ByteOffset;
546 }
547 
548 
549 
_Requires_lock_held_(_Global_critical_region_)550 _Requires_lock_held_(_Global_critical_region_)
551 VOID
552 FatInitializeDirectoryDirent (
553     IN PIRP_CONTEXT IrpContext,
554     IN PDCB Dcb,
555     IN PDIRENT ParentDirent
556     )
557 
558 /*++
559 
560 Routine Description:
561 
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.
565 
566     If a new dirent cannot be allocated (i.e., because the disk is full) then
567     it raises the appropriate status.
568 
569 Arguments:
570 
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.
574 
575     ParentDirent - Provides the parent Dirent for a time-stamp model.
576 
577 Return Value:
578 
579     None.
580 
581 --*/
582 
583 {
584     PBCB Bcb;
585     PVOID Buffer;
586     NTSTATUS DontCare = STATUS_SUCCESS;
587 
588     PAGED_CODE();
589 
590     DebugTrace(+1, Dbg, "FatInitializeDirectoryDirent\n", 0);
591 
592     DebugTrace( 0, Dbg, "  Dcb = %p\n", Dcb);
593 
594     //
595     //  Assert that we are not attempting this on the root directory.
596     //
597 
598     NT_ASSERT( NodeType(Dcb) != FAT_NTC_ROOT_DCB );
599 
600     //
601     //  Assert that this is only attempted on newly created directories.
602     //
603 
604     NT_ASSERT( Dcb->Header.AllocationSize.LowPart == 0 );
605 
606     //
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).
613     //
614 
615     FatPrepareWriteDirectoryFile( IrpContext,
616                                   Dcb,
617                                   0,
618                                   2 * sizeof(DIRENT),
619                                   &Bcb,
620                                   &Buffer,
621                                   FALSE,
622                                   TRUE,
623                                   &DontCare );
624 
625     NT_ASSERT( NT_SUCCESS( DontCare ));
626 
627     //
628     //  Add the . and .. entries
629     //
630 
631     _SEH2_TRY {
632 
633         FatConstructDot( IrpContext, Dcb, ParentDirent, (PDIRENT)Buffer + 0);
634 
635         FatConstructDotDot( IrpContext, Dcb, ParentDirent, (PDIRENT)Buffer + 1);
636 
637     //
638     //  Unpin the buffer and return to the caller.
639     //
640 
641     } _SEH2_FINALLY {
642 
643         FatUnpinBcb( IrpContext, Bcb );
644     } _SEH2_END;
645 
646     DebugTrace(-1, Dbg, "FatInitializeDirectoryDirent -> (VOID)\n", 0);
647     return;
648 }
649 
650 
651 VOID
FatTunnelFcbOrDcb(IN PFCB FcbOrDcb,IN PCCB Ccb OPTIONAL)652 FatTunnelFcbOrDcb (
653     IN PFCB FcbOrDcb,
654     IN PCCB Ccb OPTIONAL
655     )
656 /*++
657 
658 Routine Description:
659 
660     This routine handles tunneling of an Fcb or Dcb associated with
661     an object whose name is disappearing from a directory.
662 
663 Arguments:
664 
665     FcbOrDcb - Supplies the Fcb/Dcb whose name will be going away
666 
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
669 
670 Return Value:
671 
672     None.
673 
674 --*/
675 {
676     UNICODE_STRING ShortNameWithCase = {0};
677     UNICODE_STRING DownCaseSeg;
678     WCHAR ShortNameBuffer[8+1+3];
679     NTSTATUS Status;
680     USHORT i;
681 
682     PAGED_CODE();
683 
684     DebugTrace(+1, Dbg, "FatTunnelFcbOrDcb\n", 0);
685 
686     if (NodeType(FcbOrDcb) == FAT_NTC_DCB) {
687 
688         //
689         //  Directory deletion. Flush all entries from this directory in
690         //  the cache for this volume
691         //
692 
693         FsRtlDeleteKeyFromTunnelCache( &FcbOrDcb->Vcb->Tunnel,
694                                        FatDirectoryKey(FcbOrDcb) );
695 
696     } else {
697 
698         //
699         //  Was a file, so throw it into the tunnel cache
700         //
701 
702         //
703         //  Get the short name into UNICODE
704         //
705 
706         ShortNameWithCase.Length = 0;
707         ShortNameWithCase.MaximumLength = sizeof(ShortNameBuffer);
708         ShortNameWithCase.Buffer = ShortNameBuffer;
709 
710 #ifdef _MSC_VER
711 #pragma prefast( suppress:28931, "needed for debug build" )
712 #endif
713         Status = RtlOemStringToCountedUnicodeString( &ShortNameWithCase,
714                                                      &FcbOrDcb->ShortName.Name.Oem,
715                                                      FALSE);
716 
717         NT_ASSERT(ShortNameWithCase.Length != 0);
718 
719         NT_ASSERT(NT_SUCCESS(Status));
720 
721         if (FlagOn(FcbOrDcb->FcbState, FCB_STATE_8_LOWER_CASE | FCB_STATE_3_LOWER_CASE)) {
722 
723             //
724             //  Have to repair the case of the short name
725             //
726 
727             for (i = 0; i < (ShortNameWithCase.Length/sizeof(WCHAR)) &&
728                         ShortNameWithCase.Buffer[i] != L'.'; i++);
729 
730             //
731             //  Now pointing at the '.', or otherwise the end of name component
732             //
733 
734             if (FlagOn(FcbOrDcb->FcbState, FCB_STATE_8_LOWER_CASE)) {
735 
736                 DownCaseSeg.Buffer = ShortNameWithCase.Buffer;
737                 DownCaseSeg.MaximumLength = DownCaseSeg.Length = i*sizeof(WCHAR);
738 
739                 RtlDowncaseUnicodeString(&DownCaseSeg, &DownCaseSeg, FALSE);
740             }
741 
742             i++;
743 
744             //
745             //  Now pointing at first wchar of the extension.
746             //
747 
748             if (FlagOn(FcbOrDcb->FcbState, FCB_STATE_3_LOWER_CASE)) {
749 
750                 //
751                 //  It is not neccesarily the case that we can rely on the flag
752                 //  indicating that we really have an extension.
753                 //
754 
755                 if ((i*sizeof(WCHAR)) < ShortNameWithCase.Length) {
756                     DownCaseSeg.Buffer = &ShortNameWithCase.Buffer[i];
757                     DownCaseSeg.MaximumLength = DownCaseSeg.Length = ShortNameWithCase.Length - i*sizeof(WCHAR);
758 
759                     RtlDowncaseUnicodeString(&DownCaseSeg, &DownCaseSeg, FALSE);
760                 }
761             }
762         }
763 
764         //
765         //  ... and add it in
766         //
767 
768         FsRtlAddToTunnelCache( &FcbOrDcb->Vcb->Tunnel,
769                                FatDirectoryKey(FcbOrDcb->ParentDcb),
770                                &ShortNameWithCase,
771                                &FcbOrDcb->ExactCaseLongName,
772                                BooleanFlagOn(Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME),
773                                sizeof(LARGE_INTEGER),
774                                &FcbOrDcb->CreationTime );
775     }
776 
777     DebugTrace(-1, Dbg, "FatTunnelFcbOrDcb -> (VOID)\n", 0);
778 
779     return;
780 }
781 
782 
783 
_Requires_lock_held_(_Global_critical_region_)784 _Requires_lock_held_(_Global_critical_region_)
785 VOID
786 FatDeleteDirent (
787     IN PIRP_CONTEXT IrpContext,
788     IN PFCB FcbOrDcb,
789     IN PDELETE_CONTEXT DeleteContext OPTIONAL,
790     IN BOOLEAN DeleteEa
791     )
792 
793 /*++
794 
795 Routine Description:
796 
797     This routine Deletes on the disk the indicated dirent.  It does
798     this by marking the dirent as deleted.
799 
800 Arguments:
801 
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.
806 
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.
810 
811     DeleteEa - Tells us whether to delete the EA and whether to check
812         for no allocation/  Mainly TRUE.  FALSE passed in from rename.
813 
814 Return Value:
815 
816     None.
817 
818 --*/
819 
820 {
821     PBCB Bcb = NULL;
822     PDIRENT Dirent = NULL;
823     NTSTATUS DontCare;
824     ULONG Offset;
825     ULONG DirentsToDelete;
826 
827     PAGED_CODE();
828 
829     DebugTrace(+1, Dbg, "FatDeleteDirent\n", 0);
830 
831     DebugTrace( 0, Dbg, "  FcbOrDcb = %p\n", FcbOrDcb);
832 
833     //
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.
837     //
838     //  Among other reasons, it'd be unfortunate if this raced with the
839     //  rename path.
840     //
841 
842     NT_ASSERT( ExIsResourceAcquiredExclusiveLite( &FcbOrDcb->Vcb->Resource ));
843 
844     //
845     //  Assert that we are not attempting this on the root directory.
846     //
847 
848     NT_ASSERT( NodeType(FcbOrDcb) != FAT_NTC_ROOT_DCB );
849 
850     //
851     //  Make sure all requests have zero allocation/file size
852     //
853 
854     if (DeleteEa &&
855         ((FcbOrDcb->Header.AllocationSize.LowPart != 0) ||
856          ((NodeType(FcbOrDcb) == FAT_NTC_FCB) &&
857           (FcbOrDcb->Header.FileSize.LowPart != 0)))) {
858 
859         DebugTrace( 0, Dbg, "Called with non zero allocation/file size.\n", 0);
860 
861 #ifdef _MSC_VER
862 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
863 #endif
864         FatBugCheck( 0, 0, 0 );
865     }
866 
867     //
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.
870     //
871     //  Note that this loop will end with Dirent pointing to the short name.
872     //
873 
874     _SEH2_TRY {
875 
876         //
877         //  We must acquire our parent exclusive to synchronize with enumerators
878         //  who do not hold the vcb (ex: dirctrl).
879         //
880         //  This relies on our bottom up lockorder.
881         //
882 
883         ExAcquireResourceExclusiveLite( FcbOrDcb->ParentDcb->Header.Resource, TRUE );
884 
885         for ( Offset = FcbOrDcb->LfnOffsetWithinDirectory;
886               Offset <= FcbOrDcb->DirentOffsetWithinDirectory;
887               Offset += sizeof(DIRENT), Dirent += 1 ) {
888 
889             //
890             //  If we stepped onto a new page, or this is the first iteration,
891             //  unpin the old page, and pin the new one.
892             //
893 
894             if ((Offset == FcbOrDcb->LfnOffsetWithinDirectory) ||
895                 ((Offset & (PAGE_SIZE - 1)) == 0)) {
896 
897                 FatUnpinBcb( IrpContext, Bcb );
898 
899                 FatPrepareWriteDirectoryFile( IrpContext,
900                                               FcbOrDcb->ParentDcb,
901                                               Offset,
902                                               sizeof(DIRENT),
903                                               &Bcb,
904                                               (PVOID *)&Dirent,
905                                               FALSE,
906                                               TRUE,
907                                               &DontCare );
908             }
909 
910             NT_ASSERT( (Dirent->FirstClusterOfFile == 0) || !DeleteEa );
911             Dirent->FileName[0] = FAT_DIRENT_DELETED;
912         }
913 
914         //
915         //  Back Dirent off by one to point back to the short dirent.
916         //
917 
918         Dirent -= 1;
919 
920         //
921         //  If there are extended attributes for this dirent, we will attempt
922         //  to remove them.  We ignore any errors in removing Eas.
923         //
924 
925         if (!FatIsFat32(FcbOrDcb->Vcb) &&
926             DeleteEa && (Dirent->ExtendedAttributes != 0)) {
927 
928             _SEH2_TRY {
929 
930                 FatDeleteEa( IrpContext,
931                              FcbOrDcb->Vcb,
932                              Dirent->ExtendedAttributes,
933                              &FcbOrDcb->ShortName.Name.Oem );
934 
935             } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
936 
937                 //
938                 //  We catch all exceptions that Fat catches, but don't do
939                 //  anything with them.
940                 //
941             } _SEH2_END;
942         }
943 
944         //
945         //  Now clear the bits in the free dirent mask.
946         //
947 
948         DirentsToDelete = (FcbOrDcb->DirentOffsetWithinDirectory -
949                            FcbOrDcb->LfnOffsetWithinDirectory) / sizeof(DIRENT) + 1;
950 
951 
952         NT_ASSERT( (FcbOrDcb->ParentDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) ||
953                 RtlAreBitsSet( &FcbOrDcb->ParentDcb->Specific.Dcb.FreeDirentBitmap,
954                                FcbOrDcb->LfnOffsetWithinDirectory / sizeof(DIRENT),
955                                DirentsToDelete ) );
956 
957         RtlClearBits( &FcbOrDcb->ParentDcb->Specific.Dcb.FreeDirentBitmap,
958                       FcbOrDcb->LfnOffsetWithinDirectory / sizeof(DIRENT),
959                       DirentsToDelete );
960 
961         //
962         //  Now, if the caller specified a DeleteContext, use it.
963         //
964 
965         if ( ARGUMENT_PRESENT( DeleteContext ) ) {
966 
967             Dirent->FileSize = DeleteContext->FileSize;
968 
969 
970             Dirent->FirstClusterOfFile = (USHORT)DeleteContext->FirstClusterOfFile;
971         }
972 
973         //
974         //  If this newly deleted dirent is before the DeletedDirentHint, change
975         //  the DeletedDirentHint to point here.
976         //
977 
978         if (FcbOrDcb->DirentOffsetWithinDirectory <
979                             FcbOrDcb->ParentDcb->Specific.Dcb.DeletedDirentHint) {
980 
981             FcbOrDcb->ParentDcb->Specific.Dcb.DeletedDirentHint =
982                                             FcbOrDcb->LfnOffsetWithinDirectory;
983         }
984 
985     } _SEH2_FINALLY {
986 
987         FatUnpinBcb( IrpContext, Bcb );
988 
989         //
990         //  Release our parent.
991         //
992 
993         ExReleaseResourceLite( FcbOrDcb->ParentDcb->Header.Resource );
994     } _SEH2_END;
995 
996     DebugTrace(-1, Dbg, "FatDeleteDirent -> (VOID)\n", 0);
997     return;
998 }
999 
_Requires_lock_held_(_Global_critical_region_)1000 _Requires_lock_held_(_Global_critical_region_)
1001 BOOLEAN
1002 FatLfnDirentExists (
1003     IN PIRP_CONTEXT IrpContext,
1004     IN PDCB Dcb,
1005     IN PUNICODE_STRING Lfn,
1006     IN PUNICODE_STRING LfnTmp
1007     )
1008 /*++
1009 
1010 Routine Description:
1011 
1012     This routine looks for a given Lfn in a directory
1013 
1014 Arguments:
1015 
1016     Dcb - The directory to search
1017 
1018     Lfn - The Lfn to look for
1019 
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.
1022 
1023 Retrn Value:
1024 
1025     BOOLEAN TRUE if it exists, FALSE if not
1026 
1027 --*/
1028 {
1029     CCB Ccb;
1030     PDIRENT Dirent;
1031     PBCB DirentBcb = NULL;
1032     VBO DirentByteOffset;
1033     BOOLEAN Result = FALSE;
1034     ULONG Flags = 0;
1035 
1036     PAGED_CODE();
1037 
1038     //
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.
1041     //
1042 
1043     Ccb.UnicodeQueryTemplate =  *Lfn;
1044     Ccb.ContainsWildCards = FALSE;
1045     Ccb.Flags = CCB_FLAG_SKIP_SHORT_NAME_COMPARE | CCB_FLAG_QUERY_TEMPLATE_MIXED;
1046 
1047     _SEH2_TRY {
1048 
1049         FatLocateDirent( IrpContext,
1050                          Dcb,
1051                          &Ccb,
1052                          0,
1053                          &Flags,
1054                          &Dirent,
1055                          &DirentBcb,
1056                          &DirentByteOffset,
1057                          NULL,
1058                          LfnTmp,
1059                          NULL );
1060 
1061     } _SEH2_FINALLY {
1062 
1063         if (DirentBcb) {
1064 
1065             Result = TRUE;
1066         }
1067 
1068         FatUnpinBcb(IrpContext, DirentBcb);
1069     } _SEH2_END;
1070 
1071     return Result;
1072 }
1073 
1074 
_Requires_lock_held_(_Global_critical_region_)1075 _Requires_lock_held_(_Global_critical_region_)
1076 VOID
1077 FatLocateDirent (
1078     IN PIRP_CONTEXT IrpContext,
1079     IN PDCB ParentDirectory,
1080     IN PCCB Ccb,
1081     IN VBO OffsetToStartSearchFrom,
1082     IN OUT PULONG Flags,
1083     OUT PDIRENT *Dirent,
1084     OUT PBCB *Bcb,
1085     OUT PVBO ByteOffset,
1086     OUT PBOOLEAN FileNameDos OPTIONAL,
1087     IN OUT PUNICODE_STRING LongFileName OPTIONAL,
1088     IN OUT PUNICODE_STRING OrigLongFileName OPTIONAL
1089     )
1090 
1091 /*++
1092 
1093 Routine Description:
1094 
1095     This routine locates on the disk an undeleted dirent matching a given name.
1096 
1097 Arguments:
1098 
1099     ParentDirectory - Supplies the DCB for the directory to search
1100 
1101     Ccb - Contains a context control block with all matching information.
1102 
1103     OffsetToStartSearchFrom - Supplies the VBO within the parent directory
1104         from which to start looking for another real dirent.
1105 
1106     Dirent - Receives a pointer to the located dirent if one was found
1107         or NULL otherwise.
1108 
1109     Bcb - Receives the Bcb for the located dirent if one was found or
1110         NULL otherwise.
1111 
1112     ByteOffset - Receives the VBO within the Parent directory for
1113         the located dirent if one was found, or 0 otherwise.
1114 
1115     FileNameDos - Receives TRUE if the element of the dirent we hit on
1116         was the short (non LFN) side
1117 
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.
1124 
1125 Return Value:
1126 
1127     None.
1128 
1129 --*/
1130 
1131 {
1132     NTSTATUS Status = STATUS_SUCCESS;
1133 
1134     OEM_STRING Name;
1135     UCHAR NameBuffer[12];
1136 
1137     BOOLEAN UpcasedLfnValid = FALSE;
1138     UNICODE_STRING UpcasedLfn = {0};
1139     WCHAR LocalLfnBuffer[32];
1140 
1141 
1142     BOOLEAN LfnInProgress = FALSE;
1143     UCHAR LfnChecksum = 0;
1144     ULONG LfnSize = 0;
1145     ULONG LfnIndex = 0;
1146     UCHAR Ordinal = 0;
1147     VBO LfnByteOffset = 0;
1148 
1149     TimerStart(Dbg);
1150 
1151     PAGED_CODE();
1152 
1153     DebugTrace(+1, Dbg, "FatLocateDirent\n", 0);
1154 
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);
1160 
1161     //
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.
1165     //
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.
1168     //
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.
1173     //
1174 
1175     NT_ASSERT( ExIsResourceAcquiredSharedLite( ParentDirectory->Header.Resource ) ||
1176             ExIsResourceAcquiredExclusiveLite( ParentDirectory->Header.Resource ) ||
1177             ExIsResourceAcquiredSharedLite( &ParentDirectory->Vcb->Resource ) ||
1178             ExIsResourceAcquiredExclusiveLite( &ParentDirectory->Vcb->Resource ));
1179 
1180     //
1181     //  The algorithm here is pretty simple.  We just walk through the
1182     //  parent directory until we:
1183     //
1184     //      A)  Find a matching entry.
1185     //      B)  Can't Wait
1186     //      C)  Hit the End of Directory
1187     //      D)  Hit Eof
1188     //
1189     //  In the first case we found it, in the latter three cases we did not.
1190     //
1191 
1192     UNREFERENCED_PARAMETER( Flags ); // future use
1193 
1194 
1195     Name.MaximumLength = 12;
1196     Name.Buffer = (PCHAR)NameBuffer;
1197 
1198     UpcasedLfn.Length = 0;
1199     UpcasedLfn.MaximumLength = sizeof( LocalLfnBuffer);
1200     UpcasedLfn.Buffer = LocalLfnBuffer;
1201 
1202 
1203     //
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.
1206     //
1207 
1208     if (*Bcb != NULL) {
1209 
1210         if ((OffsetToStartSearchFrom / PAGE_SIZE) == (*ByteOffset / PAGE_SIZE)) {
1211 
1212             *Dirent += (OffsetToStartSearchFrom - *ByteOffset) / sizeof(DIRENT);
1213 
1214         } else {
1215 
1216             FatUnpinBcb( IrpContext, *Bcb );
1217         }
1218     }
1219 
1220     //
1221     //  Init the Lfn if we were given one.
1222     //
1223 
1224     if (ARGUMENT_PRESENT(LongFileName)) {
1225 
1226         LongFileName->Length = 0;
1227     }
1228 
1229     if (ARGUMENT_PRESENT(OrigLongFileName)) {
1230 
1231         OrigLongFileName->Length = 0;
1232     }
1233 
1234     //
1235     //  Init the FileNameDos flag
1236     //
1237 
1238     if (FileNameDos) {
1239 
1240         *FileNameDos = FALSE;
1241     }
1242 
1243     //
1244     //  Round up OffsetToStartSearchFrom to the nearest Dirent, and store
1245     //  in ByteOffset.  Note that this wipes out the prior value.
1246     //
1247 
1248     *ByteOffset = (OffsetToStartSearchFrom +  (sizeof(DIRENT) - 1))
1249                                            & ~(sizeof(DIRENT) - 1);
1250 
1251     _SEH2_TRY {
1252 
1253         while ( TRUE ) {
1254 
1255             BOOLEAN FoundValidLfn;
1256 
1257             UpcasedLfnValid = FALSE;
1258 
1259 
1260             //
1261             //  Try to read in the dirent
1262             //
1263 
1264             FatReadDirent( IrpContext,
1265                            ParentDirectory,
1266                            *ByteOffset,
1267                            Bcb,
1268                            Dirent,
1269                            &Status );
1270 
1271             //
1272             //  If End Directory dirent or EOF, set all out parameters to
1273             //  indicate entry not found and, like, bail.
1274             //
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
1278             //
1279 
1280             if ((Status == STATUS_END_OF_FILE) ||
1281                 ((*Dirent)->FileName[0] == FAT_DIRENT_NEVER_USED)) {
1282 
1283                 DebugTrace( 0, Dbg, "End of directory: entry not found.\n", 0);
1284 
1285                 //
1286                 //  If there is a Bcb, unpin it and set it to null
1287                 //
1288 
1289                 FatUnpinBcb( IrpContext, *Bcb );
1290 
1291                 *Dirent = NULL;
1292                 *ByteOffset = 0;
1293                 break;
1294             }
1295 
1296             //
1297             //  If the entry is marked deleted, skip.  If there was an Lfn in
1298             //  progress we throw it out at this point.
1299             //
1300 
1301             if ((*Dirent)->FileName[0] == FAT_DIRENT_DELETED) {
1302 
1303                 LfnInProgress = FALSE;
1304                 goto GetNextDirent;
1305             }
1306 
1307             //
1308             //  If we have wandered onto an LFN entry, try to interpret it.
1309             //
1310 
1311             if (FatData.ChicagoMode &&
1312                 ARGUMENT_PRESENT(LongFileName) &&
1313                 ((*Dirent)->Attributes == FAT_DIRENT_ATTR_LFN)) {
1314 
1315                 PLFN_DIRENT Lfn;
1316 
1317                 Lfn = (PLFN_DIRENT)*Dirent;
1318 
1319                 if (LfnInProgress) {
1320 
1321                     //
1322                     //  Check for a proper continuation of the Lfn in progress.
1323                     //
1324 
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)) {
1330 
1331                         //
1332                         //  The Lfn is not proper, stop constructing it.
1333                         //
1334 
1335                         LfnInProgress = FALSE;
1336 
1337                     } else {
1338 
1339                         NT_ASSERT( ((LfnIndex % 13) == 0) && LfnIndex );
1340 
1341                         LfnIndex -= 13;
1342 
1343                         RtlCopyMemory( &LongFileName->Buffer[LfnIndex+0],
1344                                        &Lfn->Name1[0],
1345                                        5*sizeof(WCHAR) );
1346 
1347                         RtlCopyMemory( &LongFileName->Buffer[LfnIndex+5],
1348                                        &Lfn->Name2[0],
1349                                        6 * sizeof(WCHAR) );
1350 
1351                         RtlCopyMemory( &LongFileName->Buffer[LfnIndex+11],
1352                                        &Lfn->Name3[0],
1353                                        2 * sizeof(WCHAR) );
1354 
1355                         Ordinal = Lfn->Ordinal;
1356                         LfnByteOffset = *ByteOffset;
1357                     }
1358                 }
1359 
1360                 //
1361                 //  Now check (maybe again) if we should analyze this entry
1362                 //  for a possible last entry.
1363                 //
1364 
1365                 if ((!LfnInProgress) &&
1366                     (Lfn->Ordinal & FAT_LAST_LONG_ENTRY) &&
1367                     ((Lfn->Ordinal & ~FAT_LAST_LONG_ENTRY) <= MAX_LFN_DIRENTS) &&
1368                     (Lfn->MustBeZero == 0)) {
1369 
1370                     BOOLEAN CheckTail = FALSE;
1371 
1372                     Ordinal = Lfn->Ordinal & ~FAT_LAST_LONG_ENTRY;
1373 
1374                     //
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
1379                     //  future.
1380                     //
1381 
1382                     if (Ordinal == 0) {
1383 
1384                         //
1385                         //  First LFN in the pile was zero marked as the last. This is never
1386                         //  possible since oridinals are 1-based.
1387                         //
1388 
1389                         FatPopUpFileCorrupt( IrpContext, ParentDirectory );
1390                         FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
1391                     }
1392 
1393                     LfnIndex = (Ordinal - 1) * 13;
1394 
1395                     FatEnsureStringBufferEnough( LongFileName,
1396                                                  (USHORT)((LfnIndex + 13) << 1));
1397 
1398                     RtlCopyMemory( &LongFileName->Buffer[LfnIndex+0],
1399                                    &Lfn->Name1[0],
1400                                    5*sizeof(WCHAR));
1401 
1402                     RtlCopyMemory( &LongFileName->Buffer[LfnIndex+5],
1403                                    &Lfn->Name2[0],
1404                                    6 * sizeof(WCHAR) );
1405 
1406                     RtlCopyMemory( &LongFileName->Buffer[LfnIndex+11],
1407                                    &Lfn->Name3[0],
1408                                    2 * sizeof(WCHAR) );
1409 
1410                     //
1411                     //  Now compute the Lfn size and make sure that the tail
1412                     //  bytes are correct.
1413                     //
1414 
1415                     while (LfnIndex != (ULONG)Ordinal * 13) {
1416 
1417                         if (!CheckTail) {
1418 
1419                             if (LongFileName->Buffer[LfnIndex] == 0x0000) {
1420 
1421                                 LfnSize = LfnIndex;
1422                                 CheckTail = TRUE;
1423                             }
1424 
1425                         } else {
1426 
1427                             if (LongFileName->Buffer[LfnIndex] != 0xffff) {
1428 
1429                                 break;
1430                             }
1431                         }
1432 
1433                         LfnIndex += 1;
1434                     }
1435 
1436                     //
1437                     //  If we exited this loop prematurely, the LFN is not valid.
1438                     //
1439 
1440                     if (LfnIndex == (ULONG)Ordinal * 13) {
1441 
1442                         //
1443                         //  If we didn't find the NULL terminator, then the size
1444                         //  is LfnIndex.
1445                         //
1446 
1447                         if (!CheckTail) {
1448 
1449                             LfnSize = LfnIndex;
1450                         }
1451 
1452                         LfnIndex -= 13;
1453                         LfnInProgress = TRUE;
1454                         LfnChecksum = Lfn->Checksum;
1455                         LfnByteOffset = *ByteOffset;
1456                     }
1457                 }
1458 
1459                 //
1460                 //  Move on to the next dirent.
1461                 //
1462 
1463                 goto GetNextDirent;
1464             }
1465 
1466             //
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.
1470             //
1471 
1472             if (FlagOn((*Dirent)->Attributes, FAT_DIRENT_ATTR_VOLUME_ID)) {
1473 
1474                 //
1475                 //  If we actually were asked to hand back volume labels,
1476                 //  do it.
1477                 //
1478 
1479                 if (FlagOn(Ccb->Flags, CCB_FLAG_MATCH_VOLUME_ID)) {
1480 
1481                     break;
1482                 }
1483 
1484                 goto GetNextDirent;
1485             }
1486 
1487             //
1488             //  We may have just stepped off a valid Lfn run.  Check to see if
1489             //  it is indeed valid for the following dirent.
1490             //
1491 
1492             if (LfnInProgress &&
1493                 (*ByteOffset == LfnByteOffset + sizeof(DIRENT)) &&
1494                 (LfnIndex == 0) &&
1495                 (FatComputeLfnChecksum(*Dirent) == LfnChecksum)) {
1496 
1497                 NT_ASSERT( Ordinal == 1);
1498 
1499                 FoundValidLfn = TRUE;
1500                 LongFileName->Length = (USHORT)(LfnSize * sizeof(WCHAR));
1501 
1502 
1503                 if (ARGUMENT_PRESENT(OrigLongFileName)) {
1504                     *OrigLongFileName = *LongFileName;
1505                 }
1506 
1507             } else {
1508 
1509                 FoundValidLfn = FALSE;
1510             }
1511 
1512 
1513 
1514             //
1515             //  If we are supposed to match all entries, then match this entry.
1516             //
1517 
1518             if (FlagOn(Ccb->Flags, CCB_FLAG_MATCH_ALL)) {
1519 
1520                 break;
1521             }
1522 
1523             //
1524             //  Check against the short name given if one was.
1525             //
1526 
1527             if (!FlagOn( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE )) {
1528 
1529                 if (Ccb->ContainsWildCards) {
1530 
1531                     //
1532                     //  If we get one, note that all out parameters are already set.
1533                     //
1534 
1535                     (VOID)Fat8dot3ToString( IrpContext, (*Dirent), FALSE, &Name );
1536 
1537                     //
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.
1542                     //
1543 
1544                     if ((Name.Length == 2) &&
1545                         (Name.Buffer[0] == '.') &&
1546                         (Name.Buffer[1] == '.') &&
1547                         (Ccb->OemQueryTemplate.Wild.Length > 1)) {
1548 
1549                         Name.Length = 1;
1550                     }
1551 
1552                     if (FatIsNameInExpression( IrpContext,
1553                                                Ccb->OemQueryTemplate.Wild,
1554                                                Name)) {
1555 
1556                         DebugTrace( 0, Dbg, "Entry found: Name = \"%Z\"\n", &Name);
1557                         DebugTrace( 0, Dbg, "             VBO  = %08lx\n", *ByteOffset);
1558 
1559                         if (FileNameDos) {
1560 
1561                             *FileNameDos = TRUE;
1562                         }
1563 
1564                         SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME );
1565 
1566                         break;
1567                     }
1568 
1569                 } else {
1570 
1571                     //
1572                     //  Do the quickest 8.3 equivalency check possible
1573                     //
1574 
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]))) {
1580 
1581                         DebugTrace( 0, Dbg, "Entry found.\n", 0);
1582 
1583                         if (FileNameDos) {
1584 
1585                             *FileNameDos = TRUE;
1586                         }
1587 
1588                         SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME );
1589 
1590                         break;
1591                     }
1592                 }
1593             }
1594 
1595             //
1596             //  No matches were found with the short name.  If an LFN exists,
1597             //  use it for the search.
1598             //
1599 
1600             if (FoundValidLfn) {
1601 
1602 
1603                 //
1604                 //  First do a quick check here for different sized constant
1605                 //  name and expression before upcasing.
1606                 //
1607 
1608                 if (!Ccb->ContainsWildCards &&
1609                     (Ccb->UnicodeQueryTemplate.Length != (USHORT)(LfnSize * sizeof(WCHAR)))) {
1610 
1611                     //
1612                     //  Move on to the next dirent.
1613                     //
1614 
1615                     FoundValidLfn = FALSE;
1616                     LongFileName->Length = 0;
1617                     if (OrigLongFileName) {
1618                         OrigLongFileName->Length = 0;
1619                     }
1620 
1621                     goto GetNextDirent;
1622                 }
1623 
1624 
1625 
1626                 if (!UpcasedLfnValid) {
1627 
1628                     //
1629                     //  We need to upcase the name we found on disk.
1630                     //  We need a buffer.  Try to avoid doing an allocation.
1631                     //
1632 
1633                     FatEnsureStringBufferEnough( &UpcasedLfn,
1634                                                  LongFileName->Length);
1635 
1636                     Status = RtlUpcaseUnicodeString( &UpcasedLfn,
1637                                                      LongFileName,
1638                                                      FALSE );
1639 
1640                     if (!NT_SUCCESS(Status)) {
1641 
1642                         FatNormalizeAndRaiseStatus( IrpContext, Status );
1643                     }
1644 
1645 
1646                     UpcasedLfnValid = TRUE;
1647 
1648                 }
1649 
1650                 //
1651                 //  Do the compare
1652                 //
1653 
1654                 if (Ccb->ContainsWildCards) {
1655 
1656                     if (FsRtlIsNameInExpression( &Ccb->UnicodeQueryTemplate,
1657                                                  &UpcasedLfn,
1658                                                  TRUE,
1659                                                  NULL )) {
1660 
1661                         break;
1662                     }
1663 
1664                 } else {
1665 
1666                     if (FsRtlAreNamesEqual( &Ccb->UnicodeQueryTemplate,
1667                                             &UpcasedLfn,
1668                                             BooleanFlagOn( Ccb->Flags, CCB_FLAG_QUERY_TEMPLATE_MIXED ),
1669                                             NULL )) {
1670 
1671                         break;
1672                     }
1673 
1674 
1675                 }
1676 
1677             }
1678 
1679             //
1680             //  This long name was not a match.  Zero out the Length field.
1681             //
1682 
1683             if (FoundValidLfn) {
1684 
1685                 FoundValidLfn = FALSE;
1686                 LongFileName->Length = 0;
1687 
1688 
1689                 if (OrigLongFileName) {
1690                     OrigLongFileName->Length = 0;
1691                 }
1692             }
1693 
1694 GetNextDirent:
1695 
1696             //
1697             //  Move on to the next dirent.
1698             //
1699 
1700             *ByteOffset += sizeof(DIRENT);
1701             *Dirent += 1;
1702         }
1703 
1704     } _SEH2_FINALLY {
1705 
1706         FatFreeStringBuffer( &UpcasedLfn );
1707 
1708 
1709     } _SEH2_END;
1710 
1711     DebugTrace(-1, Dbg, "FatLocateDirent -> (VOID)\n", 0);
1712 
1713     TimerStop(Dbg,"FatLocateDirent");
1714 
1715     return;
1716 }
1717 
1718 
_Requires_lock_held_(_Global_critical_region_)1719 _Requires_lock_held_(_Global_critical_region_)
1720 VOID
1721 FatLocateSimpleOemDirent (
1722     IN PIRP_CONTEXT IrpContext,
1723     IN PDCB ParentDirectory,
1724     IN POEM_STRING FileName,
1725     OUT PDIRENT *Dirent,
1726     OUT PBCB *Bcb,
1727     OUT PVBO ByteOffset
1728     )
1729 
1730 /*++
1731 
1732 Routine Description:
1733 
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.
1737 
1738 Arguments:
1739 
1740     ParentDirectory - Supplies the DCB for the directory in which
1741         to search
1742 
1743     FileName - Supplies the filename to search for.  The name may contain
1744         wild cards
1745 
1746     OffsetToStartSearchFrom - Supplies the VBO within the parent directory
1747         from which to start looking for another real dirent.
1748 
1749     Dirent - Receives a pointer to the located dirent if one was found
1750         or NULL otherwise.
1751 
1752     Bcb - Receives the Bcb for the located dirent if one was found or
1753         NULL otherwise.
1754 
1755     ByteOffset - Receives the VBO within the Parent directory for
1756         the located dirent if one was found, or 0 otherwise.
1757 
1758 Return Value:
1759 
1760     None.
1761 
1762 --*/
1763 
1764 {
1765     CCB LocalCcb;
1766 
1767     PAGED_CODE();
1768 
1769     //
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
1772     //  required.
1773     //
1774 
1775     FatStringTo8dot3( IrpContext,
1776                       *FileName,
1777                       &LocalCcb.OemQueryTemplate.Constant );
1778     LocalCcb.ContainsWildCards = FALSE;
1779     LocalCcb.Flags = 0;
1780 
1781     FatLocateDirent( IrpContext,
1782                      ParentDirectory,
1783                      &LocalCcb,
1784                      0,
1785                      NULL,
1786                      Dirent,
1787                      Bcb,
1788                      ByteOffset,
1789                      NULL,
1790                      NULL,
1791                      NULL );
1792 
1793     return;
1794 }
1795 
1796 
1797 
_Requires_lock_held_(_Global_critical_region_)1798 _Requires_lock_held_(_Global_critical_region_)
1799 VOID
1800 FatLocateVolumeLabel (
1801     IN PIRP_CONTEXT IrpContext,
1802     IN PVCB Vcb,
1803     OUT PDIRENT *Dirent,
1804     OUT PBCB *Bcb,
1805     OUT PVBO ByteOffset
1806     )
1807 
1808 /*++
1809 
1810 Routine Description:
1811 
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.
1815 
1816 Arguments:
1817 
1818     Vcb - Supplies the VCB for the volume to search
1819 
1820     Dirent - Receives a pointer to the located dirent if one was found
1821         or NULL otherwise.
1822 
1823     Bcb - Receives the Bcb for the located dirent if one was found or
1824         NULL otherwise.
1825 
1826     ByteOffset - Receives the VBO within the Parent directory for
1827         the located dirent if one was found, or 0 otherwise.
1828 
1829 Return Value:
1830 
1831     None.
1832 
1833 --*/
1834 
1835 {
1836     NTSTATUS Status = STATUS_SUCCESS;
1837 
1838     PAGED_CODE();
1839 
1840     DebugTrace(+1, Dbg, "FatLocateVolumeLabel\n", 0);
1841 
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);
1846 
1847     //
1848     //  The algorithm here is really simple.  We just walk through the
1849     //  root directory until we:
1850     //
1851     //      A)  Find the non-deleted volume label
1852     //      B)  Can't Wait
1853     //      C)  Hit the End of Directory
1854     //      D)  Hit Eof
1855     //
1856     //  In the first case we found it, in the latter three cases we did not.
1857     //
1858 
1859     *Bcb = NULL;
1860     *ByteOffset = 0;
1861 
1862     while ( TRUE ) {
1863 
1864         //
1865         //  Try to read in the dirent
1866         //
1867 
1868         FatReadDirent( IrpContext,
1869                        Vcb->RootDcb,
1870                        *ByteOffset,
1871                        Bcb,
1872                        Dirent,
1873                        &Status );
1874 
1875         //
1876         //  If End Directory dirent or EOF, set all out parameters to
1877         //  indicate volume label not found and, like, bail.
1878         //
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
1882         //
1883 
1884         if ((Status == STATUS_END_OF_FILE) ||
1885             ((*Dirent)->FileName[0] == FAT_DIRENT_NEVER_USED)) {
1886 
1887             DebugTrace( 0, Dbg, "Volume label not found.\n", 0);
1888 
1889             //
1890             //  If there is a Bcb, unpin it and set it to null
1891             //
1892 
1893             FatUnpinBcb( IrpContext, *Bcb );
1894 
1895             *Dirent = NULL;
1896             *ByteOffset = 0;
1897             break;
1898         }
1899 
1900         //
1901         //  If the entry is the non-deleted volume label break from the loop.
1902         //
1903         //  Note that all out parameters are already correctly set.
1904         //
1905 
1906         if ((((*Dirent)->Attributes & ~FAT_DIRENT_ATTR_ARCHIVE) == FAT_DIRENT_ATTR_VOLUME_ID) &&
1907             ((*Dirent)->FileName[0] != FAT_DIRENT_DELETED)) {
1908 
1909             DebugTrace( 0, Dbg, "Volume label found at VBO = %08lx\n", *ByteOffset);
1910 
1911             //
1912             //  We may set this dirty, so pin it.
1913             //
1914 
1915             FatPinMappedData( IrpContext,
1916                               Vcb->RootDcb,
1917                               *ByteOffset,
1918                               sizeof(DIRENT),
1919                               Bcb );
1920 
1921             break;
1922         }
1923 
1924         //
1925         //  Move on to the next dirent.
1926         //
1927 
1928         *ByteOffset += sizeof(DIRENT);
1929         *Dirent += 1;
1930     }
1931 
1932 
1933     DebugTrace(-1, Dbg, "FatLocateVolumeLabel -> (VOID)\n", 0);
1934 
1935     return;
1936 }
1937 
1938 
1939 
_Requires_lock_held_(_Global_critical_region_)1940 _Requires_lock_held_(_Global_critical_region_)
1941 VOID
1942 FatGetDirentFromFcbOrDcb (
1943     IN PIRP_CONTEXT IrpContext,
1944     IN PFCB FcbOrDcb,
1945     IN BOOLEAN ReturnOnFailure,
1946     OUT PDIRENT *Dirent,
1947     OUT PBCB *Bcb
1948     )
1949 
1950 /*++
1951 
1952 Routine Description:
1953 
1954     This routine reads locates on the disk the dirent denoted by the
1955     specified Fcb/Dcb.
1956 
1957 Arguments:
1958 
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.
1961 
1962     Dirent - Receives a pointer to the dirent
1963 
1964     Bcb - Receives the Bcb for the dirent
1965 
1966 Return Value:
1967 
1968     None.
1969 
1970 --*/
1971 
1972 {
1973     NTSTATUS DontCare = STATUS_SUCCESS;
1974 
1975     PAGED_CODE();
1976 
1977     DebugTrace(+1, Dbg, "FatGetDirentFromFcbOrDcb\n", 0);
1978 
1979     DebugTrace( 0, Dbg, "  FcbOrDcb = %p\n", FcbOrDcb);
1980     DebugTrace( 0, Dbg, "  Dirent   = %p\n", Dirent);
1981     DebugTrace( 0, Dbg, "  Bcb      = %p\n", Bcb);
1982 
1983     //
1984     //  Assert that we are not attempting this on the root directory.
1985     //
1986 
1987     NT_ASSERT( NodeType(FcbOrDcb) != FAT_NTC_ROOT_DCB );
1988 
1989     //
1990     //  We know the offset of the dirent within the directory file,
1991     //  so we just read it (with pinning).
1992     //
1993 
1994     FatReadDirectoryFile( IrpContext,
1995                           FcbOrDcb->ParentDcb,
1996                           FcbOrDcb->DirentOffsetWithinDirectory,
1997                           sizeof(DIRENT),
1998                           TRUE,
1999                           Bcb,
2000                           (PVOID *)Dirent,
2001                           &DontCare );
2002 
2003     //
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.
2008     //
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.
2011     //
2012     //  This was a Prefix catch.
2013     //
2014 
2015     NT_ASSERT( FlagOn( FcbOrDcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) ||
2016             NT_SUCCESS( DontCare ));
2017 
2018     //
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.
2021     //
2022     //  Also a Prefix catch.
2023     //
2024 
2025     NT_ASSERT( NT_SUCCESS( DontCare ) || FcbOrDcb->FcbCondition == FcbNeedsToBeVerified );
2026 
2027     //
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
2030     //  pointers.
2031     //
2032 
2033     if ((NULL == *Dirent) && !ReturnOnFailure) {
2034 
2035         NT_ASSERT( FALSE);
2036         FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR);
2037     }
2038 
2039     DebugTrace(-1, Dbg, "FatGetDirentFromFcbOrDcb -> (VOID)\n", 0);
2040 }
2041 
2042 
2043 
_Requires_lock_held_(_Global_critical_region_)2044 _Requires_lock_held_(_Global_critical_region_)
2045 BOOLEAN
2046 FatIsDirectoryEmpty (
2047     IN PIRP_CONTEXT IrpContext,
2048     IN PDCB Dcb
2049     )
2050 
2051 /*++
2052 
2053 Routine Description:
2054 
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).
2058 
2059 Arguments:
2060 
2061     Dcb - Supplies the DCB for the directory being queried.
2062 
2063 Return Value:
2064 
2065     BOOLEAN - Returns TRUE if the directory is empty and
2066         FALSE if the directory and is not empty.
2067 
2068 --*/
2069 
2070 {
2071     PBCB Bcb;
2072     ULONG ByteOffset;
2073     PDIRENT Dirent = NULL;
2074 
2075     BOOLEAN IsDirectoryEmpty = FALSE;
2076 
2077     NTSTATUS Status = STATUS_SUCCESS;
2078 
2079     PAGED_CODE();
2080 
2081     DebugTrace(+1, Dbg, "FatIsDirectoryEmpty\n", 0);
2082 
2083     DebugTrace( 0, Dbg, "  Dcb              = %p\n", Dcb);
2084     DebugTrace( 0, Dbg, "  IsDirectoryEmpty = %08lx\n", IsDirectoryEmpty);
2085 
2086     //
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.
2090     //
2091 
2092     ByteOffset = (NodeType(Dcb) == FAT_NTC_ROOT_DCB) ? 0 : 2*sizeof(DIRENT);
2093 
2094     //
2095     //  We just march through the directory looking for anything other
2096     //  than deleted files, LFNs, an EOF, or end of directory marker.
2097     //
2098 
2099     Bcb = NULL;
2100 
2101     _SEH2_TRY {
2102 
2103         while ( TRUE ) {
2104 
2105             //
2106             //  Try to read in the dirent
2107             //
2108 
2109             FatReadDirent( IrpContext,
2110                            Dcb,
2111                            ByteOffset,
2112                            &Bcb,
2113                            &Dirent,
2114                            &Status );
2115 
2116             //
2117             //  If End Directory dirent or EOF, set IsDirectoryEmpty to TRUE and,
2118             //  like, bail.
2119             //
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
2123             //
2124 
2125             if ((Status == STATUS_END_OF_FILE) ||
2126                 (Dirent->FileName[0] == FAT_DIRENT_NEVER_USED)) {
2127 
2128                 DebugTrace( 0, Dbg, "Empty.  Last exempt entry at VBO = %08lx\n", ByteOffset);
2129 
2130                 IsDirectoryEmpty = TRUE;
2131                 break;
2132             }
2133 
2134             //
2135             //  If this dirent is NOT deleted or an LFN set IsDirectoryEmpty to
2136             //  FALSE and, like, bail.
2137             //
2138 
2139             if ((Dirent->FileName[0] != FAT_DIRENT_DELETED) &&
2140                 (Dirent->Attributes != FAT_DIRENT_ATTR_LFN)) {
2141 
2142 
2143                     break;
2144 
2145 
2146             }
2147 
2148             //
2149             //  Move on to the next dirent.
2150             //
2151 
2152             ByteOffset += sizeof(DIRENT);
2153             Dirent += 1;
2154         }
2155 
2156     } _SEH2_FINALLY {
2157 
2158         FatUnpinBcb( IrpContext, Bcb );
2159     } _SEH2_END;
2160 
2161     DebugTrace(-1, Dbg, "FatIsDirectoryEmpty -> %ld\n", IsDirectoryEmpty);
2162 
2163     return IsDirectoryEmpty;
2164 }
2165 
2166 
2167 
2168 
2169 
2170 VOID
FatConstructDirent(IN PIRP_CONTEXT IrpContext,IN OUT PDIRENT Dirent,IN POEM_STRING FileName,IN BOOLEAN ComponentReallyLowercase,IN BOOLEAN ExtensionReallyLowercase,IN PUNICODE_STRING Lfn OPTIONAL,IN USHORT Attributes,IN BOOLEAN ZeroAndSetTimeFields,IN PLARGE_INTEGER SetCreationTime OPTIONAL)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
2181     )
2182 
2183 /*++
2184 
2185 Routine Description:
2186 
2187     This routine modifies the fields of a dirent.
2188 
2189 Arguments:
2190 
2191     Dirent - Supplies the dirent being modified.
2192 
2193     FileName - Supplies the name to store in the Dirent.  This
2194         name must not contain wildcards.
2195 
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.
2199 
2200     ExtensionReallyLowercase - Same as above, but for the extension.
2201 
2202     Lfn - May supply a long file name.
2203 
2204     Attributes - Supplies the attributes to store in the dirent
2205 
2206     ZeroAndSetTimeFields - Tells whether or not to initially zero the dirent
2207         and update the time fields.
2208 
2209     SetCreationTime - If specified, contains a timestamp to use as the creation
2210         time of this dirent
2211 
2212 Return Value:
2213 
2214     None.
2215 
2216 --*/
2217 
2218 {
2219     PAGED_CODE();
2220 
2221     DebugTrace(+1, Dbg, "FatConstructDirent\n", 0);
2222 
2223     DebugTrace( 0, Dbg, "  Dirent             = %p\n", Dirent);
2224     DebugTrace( 0, Dbg, "  FileName           = %Z\n", FileName);
2225     DebugTrace( 0, Dbg, "  Attributes         = %08lx\n", Attributes);
2226 
2227     if (ZeroAndSetTimeFields) {
2228 
2229         RtlZeroMemory( Dirent, sizeof(DIRENT) );
2230     }
2231 
2232     //
2233     //  We just merrily go and fill up the dirent with the fields given.
2234     //
2235 
2236     FatStringTo8dot3( IrpContext, *FileName, (PFAT8DOT3)&Dirent->FileName[0] );
2237 
2238     if (ZeroAndSetTimeFields || SetCreationTime) {
2239 
2240         LARGE_INTEGER Time, SaveTime;
2241 
2242         KeQuerySystemTime( &Time );
2243 
2244         if (FatData.ChicagoMode) {
2245 
2246             if (!SetCreationTime || !FatNtTimeToFatTime( IrpContext,
2247                                                          SetCreationTime,
2248                                                          FALSE,
2249                                                          &Dirent->CreationTime,
2250                                                          &Dirent->CreationMSec )) {
2251 
2252                 //
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.
2258                 //
2259 
2260                 SaveTime = Time;
2261 
2262                 if (!FatNtTimeToFatTime( IrpContext,
2263                                          &SaveTime,
2264                                          FALSE,
2265                                          &Dirent->CreationTime,
2266                                          &Dirent->CreationMSec )) {
2267 
2268                     //
2269                     //  Failed again. Wow.
2270                     //
2271 
2272                     RtlZeroMemory( &Dirent->CreationTime, sizeof(FAT_TIME_STAMP));
2273                     Dirent->CreationMSec = 0;
2274                 }
2275             }
2276         }
2277 
2278         if (ZeroAndSetTimeFields) {
2279 
2280             //
2281             //  We only touch the other timestamps if we are initializing the dirent
2282             //
2283 
2284             if (!FatNtTimeToFatTime( IrpContext,
2285                                      &Time,
2286                                      TRUE,
2287                                      &Dirent->LastWriteTime,
2288                                      NULL )) {
2289 
2290                 DebugTrace( 0, Dbg, "Current time invalid.\n", 0);
2291 
2292                 RtlZeroMemory( &Dirent->LastWriteTime, sizeof(FAT_TIME_STAMP) );
2293             }
2294 
2295             if (FatData.ChicagoMode) {
2296 
2297                 Dirent->LastAccessDate = Dirent->LastWriteTime.Date;
2298             }
2299         }
2300     }
2301 
2302     //
2303     //  Copy the attributes
2304     //
2305 
2306     Dirent->Attributes = (UCHAR)Attributes;
2307 
2308     //
2309     //  Set the magic bit here, to tell dirctrl.c that this name is really
2310     //  lowercase.
2311     //
2312 
2313     Dirent->NtByte = 0;
2314 
2315     if (ComponentReallyLowercase) {
2316 
2317         SetFlag( Dirent->NtByte, FAT_DIRENT_NT_BYTE_8_LOWER_CASE );
2318     }
2319 
2320     if (ExtensionReallyLowercase) {
2321 
2322         SetFlag( Dirent->NtByte, FAT_DIRENT_NT_BYTE_3_LOWER_CASE );
2323     }
2324 
2325     //
2326     //  See if we have to create an Lfn entry
2327     //
2328 
2329     if (ARGUMENT_PRESENT(Lfn)) {
2330 
2331         UCHAR DirentChecksum;
2332         UCHAR DirentsInLfn;
2333         UCHAR LfnOrdinal;
2334         PWCHAR LfnBuffer;
2335         PLFN_DIRENT LfnDirent;
2336 
2337         NT_ASSERT( FatData.ChicagoMode );
2338 
2339         DirentChecksum = FatComputeLfnChecksum( Dirent );
2340 
2341         LfnOrdinal =
2342         DirentsInLfn = (UCHAR)FAT_LFN_DIRENTS_NEEDED(Lfn);
2343 
2344         LfnBuffer = &Lfn->Buffer[(DirentsInLfn - 1) * 13];
2345 
2346         NT_ASSERT( DirentsInLfn <= MAX_LFN_DIRENTS );
2347 
2348         for (LfnDirent = (PLFN_DIRENT)Dirent - DirentsInLfn;
2349              LfnDirent < (PLFN_DIRENT)Dirent;
2350              LfnDirent += 1, LfnOrdinal -= 1, LfnBuffer -= 13) {
2351 
2352             WCHAR FinalLfnBuffer[13];
2353             PWCHAR Buffer;
2354 
2355             //
2356             //  We need to special case the "final" dirent.
2357             //
2358 
2359             if (LfnOrdinal == DirentsInLfn) {
2360 
2361                 ULONG i;
2362                 ULONG RemainderChars;
2363 
2364                 RemainderChars = (Lfn->Length / sizeof(WCHAR)) % 13;
2365 
2366                 LfnDirent->Ordinal = LfnOrdinal | FAT_LAST_LONG_ENTRY;
2367 
2368                 if (RemainderChars != 0) {
2369 
2370                     RtlCopyMemory( FinalLfnBuffer,
2371                                    LfnBuffer,
2372                                    RemainderChars * sizeof(WCHAR) );
2373 
2374                     for (i = RemainderChars; i < 13; i++) {
2375 
2376                         //
2377                         //  Figure out which character to use.
2378                         //
2379 
2380                         if (i == RemainderChars) {
2381 
2382                             FinalLfnBuffer[i] = 0x0000;
2383 
2384                         } else {
2385 
2386                             FinalLfnBuffer[i] = 0xffff;
2387                         }
2388                     }
2389 
2390                     Buffer = FinalLfnBuffer;
2391 
2392                 } else {
2393 
2394                     Buffer = LfnBuffer;
2395                 }
2396 
2397             } else {
2398 
2399                 LfnDirent->Ordinal = LfnOrdinal;
2400 
2401                 Buffer = LfnBuffer;
2402             }
2403 
2404             //
2405             //  Now fill in the name.
2406             //
2407 
2408             RtlCopyMemory( &LfnDirent->Name1[0],
2409                            &Buffer[0],
2410                            5 * sizeof(WCHAR) );
2411 
2412             RtlCopyMemory( &LfnDirent->Name2[0],
2413                            &Buffer[5],
2414                            6 * sizeof(WCHAR) );
2415 
2416             RtlCopyMemory( &LfnDirent->Name3[0],
2417                            &Buffer[11],
2418                            2 * sizeof(WCHAR) );
2419 
2420             //
2421             //  And the other fields
2422             //
2423 
2424             LfnDirent->Attributes = FAT_DIRENT_ATTR_LFN;
2425 
2426             LfnDirent->Type = 0;
2427 
2428             LfnDirent->Checksum = DirentChecksum;
2429 
2430             LfnDirent->MustBeZero = 0;
2431         }
2432     }
2433 
2434     DebugTrace(-1, Dbg, "FatConstructDirent -> (VOID)\n", 0);
2435     return;
2436 }
2437 
2438 
2439 VOID
FatConstructLabelDirent(IN PIRP_CONTEXT IrpContext,IN OUT PDIRENT Dirent,IN POEM_STRING Label)2440 FatConstructLabelDirent (
2441     IN PIRP_CONTEXT IrpContext,
2442     IN OUT PDIRENT Dirent,
2443     IN POEM_STRING Label
2444     )
2445 
2446 /*++
2447 
2448 Routine Description:
2449 
2450     This routine modifies the fields of a dirent to be used for a label.
2451 
2452 Arguments:
2453 
2454     Dirent - Supplies the dirent being modified.
2455 
2456     Label - Supplies the name to store in the Dirent.  This
2457             name must not contain wildcards.
2458 
2459 Return Value:
2460 
2461     None.
2462 
2463 --*/
2464 
2465 {
2466     PAGED_CODE();
2467 
2468     DebugTrace(+1, Dbg, "FatConstructLabelDirent\n", 0);
2469 
2470     DebugTrace( 0, Dbg, "  Dirent             = %p\n", Dirent);
2471     DebugTrace( 0, Dbg, "  Label              = %Z\n", Label);
2472 
2473     RtlZeroMemory( Dirent, sizeof(DIRENT) );
2474 
2475     //
2476     //  We just merrily go and fill up the dirent with the fields given.
2477     //
2478 
2479     RtlCopyMemory( Dirent->FileName, Label->Buffer, Label->Length );
2480 
2481     //
2482     // Pad the label with spaces, not nulls.
2483     //
2484 
2485     RtlFillMemory( &Dirent->FileName[Label->Length], 11 - Label->Length, ' ');
2486 
2487     Dirent->LastWriteTime = FatGetCurrentFatTime( IrpContext );
2488 
2489     Dirent->Attributes = FAT_DIRENT_ATTR_VOLUME_ID;
2490     Dirent->ExtendedAttributes = 0;
2491     Dirent->FileSize = 0;
2492 
2493     DebugTrace(-1, Dbg, "FatConstructLabelDirent -> (VOID)\n", 0);
2494     return;
2495 }
2496 
2497 
_Requires_lock_held_(_Global_critical_region_)2498 _Requires_lock_held_(_Global_critical_region_)
2499 VOID
2500 FatSetFileSizeInDirent (
2501     IN PIRP_CONTEXT IrpContext,
2502     IN PFCB Fcb,
2503     IN PULONG AlternativeFileSize OPTIONAL
2504     )
2505 
2506 /*++
2507 
2508 Routine Description:
2509 
2510     This routine saves the file size in an fcb into its dirent.
2511 
2512 Arguments:
2513 
2514     Fcb - Supplies the Fcb being referenced
2515 
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.
2518 
2519 Return Value:
2520 
2521     None.
2522 
2523 --*/
2524 
2525 {
2526     PDIRENT Dirent;
2527     PBCB DirentBcb;
2528 
2529     PAGED_CODE();
2530 
2531     NT_ASSERT( Fcb->FcbCondition == FcbGood );
2532 
2533     FatGetDirentFromFcbOrDcb( IrpContext,
2534                               Fcb,
2535                               FALSE,
2536                               &Dirent,
2537                               &DirentBcb );
2538     _SEH2_TRY {
2539 
2540         Dirent->FileSize = ARGUMENT_PRESENT( AlternativeFileSize ) ?
2541                            *AlternativeFileSize : Fcb->Header.FileSize.LowPart;
2542 
2543 
2544         FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE );
2545 
2546     } _SEH2_FINALLY {
2547 
2548         FatUnpinBcb( IrpContext, DirentBcb );
2549     } _SEH2_END;
2550 }
2551 
_Requires_lock_held_(_Global_critical_region_)2552 _Requires_lock_held_(_Global_critical_region_)
2553 VOID
2554 FatSetFileSizeInDirentNoRaise (
2555     IN PIRP_CONTEXT IrpContext,
2556     IN PFCB Fcb,
2557     IN PULONG AlternativeFileSize OPTIONAL
2558     )
2559 
2560 /*++
2561 
2562 Routine Description:
2563 
2564     This routine saves the file size in an fcb into its dirent.
2565     All exceptions thrown from FatSetFileSizeInDirent are
2566     silently swallowed.
2567 
2568 Arguments:
2569 
2570     Fcb - Supplies the Fcb being referenced
2571 
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.
2574 
2575 Return Value:
2576 
2577     None.
2578 
2579 --*/
2580 
2581 {
2582     _SEH2_TRY {
2583 
2584         FatSetFileSizeInDirent( IrpContext, Fcb, AlternativeFileSize );
2585 
2586     } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
2587 
2588         NOTHING;
2589     } _SEH2_END;
2590 }
2591 
2592 
_Requires_lock_held_(_Global_critical_region_)2593 _Requires_lock_held_(_Global_critical_region_)
2594 VOID
2595 FatUpdateDirentFromFcb (
2596    IN PIRP_CONTEXT IrpContext,
2597    IN PFILE_OBJECT FileObject,
2598    IN PFCB FcbOrDcb,
2599    IN PCCB Ccb
2600    )
2601 
2602 
2603 /*++
2604 
2605 Routine Description:
2606 
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.
2610 
2611 Arguments:
2612 
2613     FileObject - Fileobject representing the handle involved
2614 
2615     FcbOrDcb - File/Dir involved
2616 
2617     Ccb - User context involved
2618 
2619 Return Value:
2620 
2621     None.
2622 
2623 --*/
2624 
2625 {
2626     BOOLEAN SetArchiveBit;
2627 
2628     BOOLEAN UpdateFileSize;
2629     BOOLEAN UpdateLastWriteTime;
2630     BOOLEAN UpdateLastAccessTime;
2631     BOOLEAN UpdateDirent = FALSE;
2632 
2633     PDIRENT Dirent;
2634     PBCB DirentBcb = NULL;
2635     ULONG NotifyFilter = 0;
2636     FAT_TIME_STAMP CurrentFatTime = {0};
2637 
2638     LARGE_INTEGER CurrentTime;
2639     LARGE_INTEGER CurrentDay = {0};
2640     LARGE_INTEGER LastAccessDay;
2641 
2642     PAGED_CODE();
2643 
2644     //
2645     //  Nothing to do if the fcb is bad, volume is readonly or we got the
2646     //  root dir.
2647     //
2648 
2649     if (FcbOrDcb->FcbCondition != FcbGood ||
2650         NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB ||
2651         FlagOn(FcbOrDcb->Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
2652 
2653         return;
2654     }
2655 
2656     //
2657     //  Check if we should be changing the time or file size and set
2658     //  the archive bit on the file.
2659     //
2660 
2661     KeQuerySystemTime( &CurrentTime );
2662 
2663     //
2664     //  Note that we HAVE to use BooleanFlagOn() here because
2665     //  FO_FILE_SIZE_CHANGED > 0x80 (i.e., not in the first byte).
2666     //
2667 
2668     SetArchiveBit = BooleanFlagOn(FileObject->Flags, FO_FILE_MODIFIED);
2669 
2670     UpdateLastWriteTime = FlagOn(FileObject->Flags, FO_FILE_MODIFIED) &&
2671                           !FlagOn(Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE);
2672 
2673     UpdateFileSize = NodeType(FcbOrDcb) == FAT_NTC_FCB &&
2674                      BooleanFlagOn(FileObject->Flags, FO_FILE_SIZE_CHANGED);
2675 
2676     //
2677     //  Do one further check here of access time.  Only update it if
2678     //  the current version is at least one day old.  We know that
2679     //  the current FcbOrDcb->LastAccessTime corresponds to 12 midnight local
2680     //  time, so just see if the current time is on the same day.
2681     //
2682 
2683     if (FatData.ChicagoMode &&
2684         (UpdateLastWriteTime ||
2685          FlagOn(FileObject->Flags, FO_FILE_FAST_IO_READ)) &&
2686         !FlagOn(Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS)) {
2687 
2688         ExSystemTimeToLocalTime( &FcbOrDcb->LastAccessTime, &LastAccessDay );
2689         ExSystemTimeToLocalTime( &CurrentTime, &CurrentDay );
2690 
2691         LastAccessDay.QuadPart /= FatOneDay.QuadPart;
2692         CurrentDay.QuadPart /= FatOneDay.QuadPart;
2693 
2694         if (LastAccessDay.LowPart != CurrentDay.LowPart) {
2695 
2696             UpdateLastAccessTime = TRUE;
2697 
2698         } else {
2699 
2700             UpdateLastAccessTime = FALSE;
2701         }
2702 
2703     } else {
2704 
2705         UpdateLastAccessTime = FALSE;
2706     }
2707 
2708     if (SetArchiveBit ||
2709         UpdateFileSize ||
2710         UpdateLastWriteTime ||
2711         UpdateLastAccessTime
2712         ) {
2713 
2714         DebugTrace(0, Dbg, "Update Time and/or file size on File/Dir\n", 0);
2715 
2716         _SEH2_TRY {
2717 
2718             _SEH2_TRY {
2719 
2720 #if (NTDDI_VERSION >= NTDDI_WIN8)
2721                 //
2722                 //  Break parent directory oplock.  Directory oplock breaks are
2723                 //  always advisory, so we will never block/get STATUS_PENDING here.
2724                 //
2725 
2726                 if (FcbOrDcb->ParentDcb != NULL) {
2727 
2728                     FsRtlCheckOplockEx( FatGetFcbOplock(FcbOrDcb->ParentDcb),
2729                                         IrpContext->OriginatingIrp,
2730                                         OPLOCK_FLAG_PARENT_OBJECT,
2731                                         NULL,
2732                                         NULL,
2733                                         NULL );
2734                 }
2735 #endif
2736 
2737                 //
2738                 //  Get the dirent
2739                 //
2740 
2741                 FatGetDirentFromFcbOrDcb( IrpContext,
2742                                           FcbOrDcb,
2743                                           FALSE,
2744                                           &Dirent,
2745                                           &DirentBcb );
2746 
2747                 if (UpdateLastWriteTime || UpdateLastAccessTime) {
2748 
2749                     (VOID)FatNtTimeToFatTime( IrpContext,
2750                                               &CurrentTime,
2751                                               TRUE,
2752                                               &CurrentFatTime,
2753                                               NULL );
2754                 }
2755 
2756                 if (SetArchiveBit) {
2757 
2758                     Dirent->Attributes |= FILE_ATTRIBUTE_ARCHIVE;
2759                     FcbOrDcb->DirentFatFlags |= FILE_ATTRIBUTE_ARCHIVE;
2760 
2761                     NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
2762                     UpdateDirent = TRUE;
2763                 }
2764 
2765                 if (UpdateLastWriteTime) {
2766 
2767                     //
2768                     //  Update its time of last write
2769                     //
2770 
2771                     FcbOrDcb->LastWriteTime = CurrentTime;
2772                     Dirent->LastWriteTime = CurrentFatTime;
2773 
2774                     //
2775                     //  We call the notify package to report that the
2776                     //  last modification time has changed.
2777                     //
2778 
2779                     NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
2780                     UpdateDirent = TRUE;
2781                 }
2782 
2783                 if (UpdateLastAccessTime) {
2784 
2785                     //
2786                     //  Now we have to truncate the local time down
2787                     //  to the current day, then convert back to UTC.
2788                     //
2789 
2790                     FcbOrDcb->LastAccessTime.QuadPart =
2791                         CurrentDay.QuadPart * FatOneDay.QuadPart;
2792 
2793                     ExLocalTimeToSystemTime( &FcbOrDcb->LastAccessTime,
2794                                              &FcbOrDcb->LastAccessTime );
2795 
2796                     Dirent->LastAccessDate = CurrentFatTime.Date;
2797 
2798                     //
2799                     //  We call the notify package to report that the
2800                     //  last access time has changed.
2801                     //
2802 
2803                     NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
2804                     UpdateDirent = TRUE;
2805                 }
2806 
2807                 if (UpdateFileSize) {
2808 
2809                     //
2810                     //  Perhaps we were called to make certain that the
2811                     //  filesize on disc was updated - don't bother updating
2812                     //  and firing the filter if nothing changed.
2813                     //
2814 
2815                     NT_ASSERT( NodeType(FcbOrDcb) == FAT_NTC_FCB );
2816 
2817                     if (Dirent->FileSize != FcbOrDcb->Header.FileSize.LowPart) {
2818 
2819                         //
2820                         //  Update the dirent file size
2821                         //
2822 
2823                         Dirent->FileSize = FcbOrDcb->Header.FileSize.LowPart;
2824 
2825                         //
2826                         //  We call the notify package to report that the
2827                         //  size has changed.
2828                         //
2829 
2830                         NotifyFilter |= FILE_NOTIFY_CHANGE_SIZE;
2831                         UpdateDirent = TRUE;
2832                     }
2833 
2834 
2835                 }
2836 
2837 
2838                 FatNotifyReportChange( IrpContext,
2839                                        FcbOrDcb->Vcb,
2840                                        FcbOrDcb,
2841                                        NotifyFilter,
2842                                        FILE_ACTION_MODIFIED );
2843 
2844                 if (UpdateDirent) {
2845 
2846                     //
2847                     //  If all we did was update last access time,
2848                     //  don't mark the volume dirty.
2849                     //
2850 
2851                     FatSetDirtyBcb( IrpContext,
2852                                     DirentBcb,
2853                                     NotifyFilter == FILE_NOTIFY_CHANGE_LAST_ACCESS ?
2854                                     NULL : FcbOrDcb->Vcb,
2855                                     TRUE );
2856                 }
2857 
2858             } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
2859                       EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
2860 
2861                   FatResetExceptionState( IrpContext );
2862             } _SEH2_END;
2863 
2864         } _SEH2_FINALLY {
2865 
2866             FatUnpinBcb( IrpContext, DirentBcb );
2867         } _SEH2_END;
2868     }
2869 
2870 }
2871 
2872 
2873 //
2874 //  Internal support routine
2875 //
2876 
2877 UCHAR
FatComputeLfnChecksum(PDIRENT Dirent)2878 FatComputeLfnChecksum (
2879     PDIRENT Dirent
2880     )
2881 
2882 /*++
2883 
2884 Routine Description:
2885 
2886     This routine computes the Chicago long file name checksum.
2887 
2888 Arguments:
2889 
2890     Dirent - Specifies the dirent that we are to compute a checksum for.
2891 
2892 Return Value:
2893 
2894     The checksum.
2895 
2896 --*/
2897 
2898 {
2899     ULONG i;
2900     UCHAR Checksum;
2901 
2902     PAGED_CODE();
2903 
2904     Checksum = Dirent->FileName[0];
2905 
2906     for (i=1; i < 11; i++) {
2907 
2908         Checksum = ((Checksum & 1) ? 0x80 : 0) +
2909                     (Checksum >> 1) +
2910                     Dirent->FileName[i];
2911     }
2912 
2913     return Checksum;
2914 }
2915 
2916 
2917 
2918 #if 0 // It turns out Win95 is still creating short names without a ~
2919 
2920 //
2921 //  Internal support routine
2922 //
2923 
2924 BOOLEAN
2925 FatIsLfnPairValid (
2926     PWCHAR Lfn,
2927     ULONG LfnSize,
2928     PDIRENT Dirent
2929     )
2930 
2931 /*++
2932 
2933 Routine Description:
2934 
2935     This routine does a few more checks to make sure that a LFN/short
2936     name pairing is legitimate.  Basically this is the test:
2937 
2938         Pairing is valid if:
2939 
2940         DIRENT has a ~ character ||
2941         (LFN is 8.3 compliant &&
2942          (LFN has extended character(s) ? TRUE :
2943           LFN upcases to DIRENT))
2944 
2945     When checking for the presence of a tilda character in the short
2946     name, note that we purposely do a single byte search instead of
2947     converting the name to UNICODE and looking there for the tilda.
2948     This protects us from accidently missing the tilda if the
2949     preceding byte is a lead byte in the current Oem code page,
2950     but wasn't in the Oem code page that created the file.
2951 
2952     Also note that if the LFN is longer than 12 characters, then the
2953     second clause of the OR must be false.
2954 
2955 Arguments:
2956 
2957     Lfn - Points to a buffer of UNICODE chars.
2958 
2959     LfnSize - This is the size of the LFN in characters.
2960 
2961     Dirent - Specifies the dirent we are to consider.
2962 
2963 Return Value:
2964 
2965     TRUE if the Lfn/DIRENT form a legitimate pair, FALSE otherwise.
2966 
2967 --*/
2968 
2969 {
2970     ULONG i;
2971     BOOLEAN ExtendedChars;
2972     ULONG DirentBuffer[3];
2973     PUCHAR DirentName;
2974     ULONG DirentIndex;
2975     BOOLEAN DotEncountered;
2976 
2977     //
2978     //  First, look for a tilda
2979     //
2980 
2981     for (i=0; i<11; i++) {
2982         if (Dirent->FileName[i] == '~') {
2983             return TRUE;
2984         }
2985     }
2986 
2987     //
2988     //  No tilda.  If the LFN is longer than 12 characters, then it can
2989     //  neither upcase to the DIRENT nor be 8.3 complient.
2990     //
2991 
2992     if (LfnSize > 12) {
2993         return FALSE;
2994     }
2995 
2996     //
2997     //  Now see if the name is 8.3, and build an upcased DIRENT as well.
2998     //
2999 
3000     DirentBuffer[0] = 0x20202020;
3001     DirentBuffer[1] = 0x20202020;
3002     DirentBuffer[2] = 0x20202020;
3003 
3004     DirentName = (PUCHAR)DirentBuffer;
3005 
3006     ExtendedChars = FALSE;
3007     DirentIndex = 0;
3008     DotEncountered = FALSE;
3009 
3010     for (i=0; i < LfnSize; i++) {
3011 
3012         //
3013         //  Do dot transition work
3014         //
3015 
3016         if (Lfn[i] == L'.') {
3017             if (DotEncountered ||
3018                 (i > 8) ||
3019                 ((LfnSize - i) > 4) ||
3020                 (i && Lfn[i-1] == L' ')) {
3021                 return FALSE;
3022             }
3023             DotEncountered = TRUE;
3024             DirentIndex = 8;
3025             continue;
3026         }
3027 
3028         //
3029         //  The character must be legal in order to be 8.3
3030         //
3031 
3032         if ((Lfn[i] < 0x80) &&
3033             !FsRtlIsAnsiCharacterLegalFat((UCHAR)Lfn[i], FALSE)) {
3034             return FALSE;
3035         }
3036 
3037         //
3038         //  If the name contains no extended chars, continue building DIRENT
3039         //
3040 
3041         if (!ExtendedChars) {
3042             if (Lfn[i] > 0x7f) {
3043                 ExtendedChars = TRUE;
3044             } else {
3045                 DirentName[DirentIndex++] = (UCHAR) (
3046                 Lfn[i] < 'a' ? Lfn[i] : Lfn[i] <= 'z' ? Lfn[i] - ('a' - 'A') : Lfn[i]);
3047             }
3048         }
3049     }
3050 
3051     //
3052     //  If the LFN ended in a space, or there was no dot and the name
3053     //  has more than 8 characters, then it is not 8.3 compliant.
3054     //
3055 
3056     if ((Lfn[LfnSize - 1] == L' ') ||
3057         (!DotEncountered && (LfnSize > 8))) {
3058         return FALSE;
3059     }
3060 
3061     //
3062     //  OK, now if we got this far then the LFN is 8dot3.  If there are
3063     //  no extended characters, then we can also check to make sure that
3064     //  the LFN is only a case varient of the DIRENT.
3065     //
3066 
3067     if (!ExtendedChars &&
3068         !RtlEqualMemory(Dirent->FileName, DirentName, 11)) {
3069 
3070         return FALSE;
3071     }
3072 
3073     //
3074     //  We have now verified this pairing the very best we can without
3075     //  knowledge of the code page that the file was created under.
3076     //
3077 
3078     return TRUE;
3079 }
3080 #endif //0
3081 
3082 //
3083 //  Internal support routine
3084 //
3085 
_Requires_lock_held_(_Global_critical_region_)3086 _Requires_lock_held_(_Global_critical_region_)
3087 VOID
3088 FatRescanDirectory (
3089     PIRP_CONTEXT IrpContext,
3090     PDCB Dcb
3091     )
3092 
3093 /*++
3094 
3095 Routine Description:
3096 
3097     This routine rescans the given directory, finding the first unused
3098     dirent, first deleted dirent, and setting the free dirent bitmap
3099     appropriately.
3100 
3101 Arguments:
3102 
3103     Dcb - Supplies the directory to rescan.
3104 
3105 Return Value:
3106 
3107     None.
3108 
3109 --*/
3110 
3111 {
3112     PBCB Bcb = NULL;
3113     PDIRENT Dirent = NULL;
3114     NTSTATUS Status = STATUS_SUCCESS;
3115 
3116     ULONG UnusedVbo;
3117     ULONG DeletedHint;
3118     ULONG DirentIndex;
3119     ULONG DirentsThisRun;
3120     ULONG StartIndexOfThisRun;
3121 
3122     enum RunType {
3123         InitialRun,
3124         FreeDirents,
3125         AllocatedDirents,
3126     } CurrentRun;
3127 
3128     PAGED_CODE();
3129 
3130     DebugTrace( 0, Dbg, "We must scan the whole directory.\n", 0);
3131 
3132     UnusedVbo = 0;
3133     DeletedHint = 0xffffffff;
3134 
3135     //
3136     //  To start with, we have to find out if the first dirent is free.
3137     //
3138 
3139     CurrentRun = InitialRun;
3140     DirentIndex =
3141     StartIndexOfThisRun = 0;
3142 
3143     _SEH2_TRY {
3144 
3145         while ( TRUE ) {
3146 
3147             BOOLEAN DirentDeleted;
3148 
3149             //
3150             //  Read a dirent
3151             //
3152 
3153             FatReadDirent( IrpContext,
3154                            Dcb,
3155                            UnusedVbo,
3156                            &Bcb,
3157                            &Dirent,
3158                            &Status );
3159 
3160             //
3161             //  If EOF, or we found a NEVER_USED entry, we exit the loop
3162             //
3163 
3164             if ( (Status == STATUS_END_OF_FILE ) ||
3165                  (Dirent->FileName[0] == FAT_DIRENT_NEVER_USED)) {
3166 
3167                 break;
3168             }
3169 
3170             //
3171             //  If the dirent is DELETED, and it is the first one we found, set
3172             //  it in the deleted hint.
3173             //
3174 
3175             if (Dirent->FileName[0] == FAT_DIRENT_DELETED) {
3176 
3177                 DirentDeleted = TRUE;
3178 
3179                 if (DeletedHint == 0xffffffff) {
3180 
3181                     DeletedHint = UnusedVbo;
3182                 }
3183 
3184             } else {
3185 
3186                 DirentDeleted = FALSE;
3187             }
3188 
3189             //
3190             //  Check for the first time through the loop, and determine
3191             //  the current run type.
3192             //
3193 
3194             if (CurrentRun == InitialRun) {
3195 
3196                 CurrentRun = DirentDeleted ?
3197                              FreeDirents : AllocatedDirents;
3198 
3199             } else {
3200 
3201                 //
3202                 //  Are we switching from a free run to an allocated run?
3203                 //
3204 
3205                 if ((CurrentRun == FreeDirents) && !DirentDeleted) {
3206 
3207                     DirentsThisRun = DirentIndex - StartIndexOfThisRun;
3208 
3209                     RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
3210                                   StartIndexOfThisRun,
3211                                   DirentsThisRun );
3212 
3213                     CurrentRun = AllocatedDirents;
3214                     StartIndexOfThisRun = DirentIndex;
3215                 }
3216 
3217                 //
3218                 //  Are we switching from an allocated run to a free run?
3219                 //
3220 
3221                 if ((CurrentRun == AllocatedDirents) && DirentDeleted) {
3222 
3223                     DirentsThisRun = DirentIndex - StartIndexOfThisRun;
3224 
3225                     RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
3226                                 StartIndexOfThisRun,
3227                                 DirentsThisRun );
3228 
3229                     CurrentRun = FreeDirents;
3230                     StartIndexOfThisRun = DirentIndex;
3231                 }
3232             }
3233 
3234             //
3235             //  Move on to the next dirent.
3236             //
3237 
3238             UnusedVbo += sizeof(DIRENT);
3239             Dirent += 1;
3240             DirentIndex += 1;
3241         }
3242 
3243         //
3244         //  Now we have to record the final run we encoutered
3245         //
3246 
3247         DirentsThisRun = DirentIndex - StartIndexOfThisRun;
3248 
3249         if ((CurrentRun == FreeDirents) || (CurrentRun == InitialRun)) {
3250 
3251             RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
3252                           StartIndexOfThisRun,
3253                           DirentsThisRun );
3254 
3255         } else {
3256 
3257             RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
3258                         StartIndexOfThisRun,
3259                         DirentsThisRun );
3260         }
3261 
3262         //
3263         //  Now if there we bailed prematurely out of the loop because
3264         //  we hit an unused entry, set all the rest as free.
3265         //
3266 
3267         if (UnusedVbo < Dcb->Header.AllocationSize.LowPart) {
3268 
3269             StartIndexOfThisRun = UnusedVbo / sizeof(DIRENT);
3270 
3271             DirentsThisRun = (Dcb->Header.AllocationSize.LowPart -
3272                               UnusedVbo) / sizeof(DIRENT);
3273 
3274             RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
3275                           StartIndexOfThisRun,
3276                           DirentsThisRun);
3277         }
3278 
3279     } _SEH2_FINALLY {
3280 
3281         FatUnpinBcb( IrpContext, Bcb );
3282     } _SEH2_END;
3283 
3284     //
3285     //  If there weren't any DELETED entries, set the index to our current
3286     //  position.
3287     //
3288 
3289     if (DeletedHint == 0xffffffff) { DeletedHint = UnusedVbo; }
3290 
3291     Dcb->Specific.Dcb.UnusedDirentVbo = UnusedVbo;
3292     Dcb->Specific.Dcb.DeletedDirentHint = DeletedHint;
3293 
3294     return;
3295 }
3296 
3297 
3298 //
3299 //  Internal support routine
3300 //
3301 
_Requires_lock_held_(_Global_critical_region_)3302 _Requires_lock_held_(_Global_critical_region_)
3303 ULONG
3304 FatDefragDirectory (
3305     IN PIRP_CONTEXT IrpContext,
3306     IN PDCB Dcb,
3307     IN ULONG DirentsNeeded
3308     )
3309 
3310 /*++
3311 
3312 Routine Description:
3313 
3314     This routine determines if the requested number of dirents can be found
3315     in the directory, looking for deleted dirents and orphaned LFNs.  If the
3316     request can be satisifed, orphaned LFNs are marked as deleted, and deleted
3317     dirents are all grouped together at the end of the directory.
3318 
3319     Note that this routine is currently used only on the root directory, but
3320     it is completely general and could be used on any directory.
3321 
3322 Arguments:
3323 
3324     Dcb - Supplies the directory to defrag.
3325 
3326 Return Value:
3327 
3328     The Index of the first dirent available for use, or -1 if the
3329     request cannot be satisfied.
3330 
3331 --*/
3332 
3333 {
3334     ULONG SavedIrpContextFlag;
3335     PLIST_ENTRY Links;
3336     ULONG ReturnValue = 0;
3337     PFCB Fcb;
3338 
3339     PBCB Bcb = NULL;
3340     PDIRENT Dirent = NULL;
3341     UNICODE_STRING Lfn = {0,0,NULL};
3342 
3343     LARGE_MCB Mcb;
3344     BOOLEAN McbInitialized = FALSE;
3345     BOOLEAN InvalidateFcbs = FALSE;
3346 
3347     PUCHAR Directory = NULL;
3348     PUCHAR UnusedDirents;
3349     PUCHAR UnusedDirentBuffer = NULL;
3350     PUCHAR UsedDirents;
3351     PUCHAR UsedDirentBuffer = NULL;
3352 
3353     PBCB *Bcbs = NULL;
3354     ULONG Page;
3355     ULONG PagesPinned = 0;
3356 
3357     ULONG DcbSize;
3358     ULONG TotalBytesAllocated = 0;
3359 
3360     PAGED_CODE();
3361 
3362     //
3363     //  We assume we own the Vcb.
3364     //
3365 
3366     NT_ASSERT( FatVcbAcquiredExclusive(IrpContext, Dcb->Vcb) );
3367 
3368     //
3369     //  We will only attempt this on directories less than 0x40000 bytes
3370     //  long (by default on DOS the root directory is only 0x2000 long).
3371     //  This is to avoid a cache manager complication.
3372     //
3373 
3374     DcbSize = Dcb->Header.AllocationSize.LowPart;
3375 
3376     if (DcbSize > 0x40000) {
3377 
3378         return (ULONG)-1;
3379     }
3380 
3381     //
3382     //  Force wait to TRUE
3383     //
3384 
3385     SavedIrpContextFlag = IrpContext->Flags;
3386 
3387     SetFlag( IrpContext->Flags,
3388              IRP_CONTEXT_FLAG_WAIT | IRP_CONTEXT_FLAG_WRITE_THROUGH );
3389 
3390     //
3391     //  Now acquire all open Fcbs in the Dcb exclusive.
3392     //
3393 
3394     for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink;
3395          Links != &Dcb->Specific.Dcb.ParentDcbQueue;
3396          Links = Links->Flink) {
3397 
3398         Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
3399 
3400         (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.Resource, TRUE );
3401     }
3402 
3403     _SEH2_TRY {
3404 
3405         CCB Ccb;
3406         ULONG QueryOffset = 0;
3407         ULONG FoundOffset = 0;
3408         ULONGLONG BytesUsed = 0;
3409 
3410         NTSTATUS DontCare;
3411         ULONG Run;
3412         ULONG TotalRuns;
3413         BOOLEAN Result;
3414         PUCHAR Char;
3415 
3416         //
3417         //  We are going to build a new bitmap that will show all orphaned
3418         //  LFNs as well as deleted dirents as available.
3419         //
3420         //  Initialize our local CCB that will match all files and even
3421         //  a label if it is here.
3422         //
3423 
3424         RtlZeroMemory( &Ccb, sizeof(CCB) );
3425         Ccb.Flags = CCB_FLAG_MATCH_ALL | CCB_FLAG_MATCH_VOLUME_ID;
3426 
3427         //
3428         //  Init the Long File Name string.
3429         //
3430 
3431         Lfn.MaximumLength = 260 * sizeof(WCHAR);
3432         Lfn.Buffer = FsRtlAllocatePoolWithTag( PagedPool,
3433                                                260*sizeof(WCHAR),
3434                                                TAG_FILENAME_BUFFER );
3435 
3436         //
3437         //  Initalize the Mcb.  We use this structure to keep track of runs
3438         //  of free and allocated dirents.  Runs are identity allocations, and
3439         //  holes are free dirents.
3440         //
3441 
3442         FsRtlInitializeLargeMcb( &Mcb, PagedPool );
3443 
3444         McbInitialized = TRUE;
3445 
3446         do {
3447 
3448             FatLocateDirent( IrpContext,
3449                              Dcb,
3450                              &Ccb,
3451                              QueryOffset,
3452                              NULL,
3453                              &Dirent,
3454                              &Bcb,
3455                              (PVBO)&FoundOffset,
3456                              NULL,
3457                              &Lfn,
3458                              NULL );
3459 
3460             if (Dirent != NULL) {
3461 
3462                 ULONG LfnByteOffset;
3463 
3464                 //
3465                 //  Compute the LfnByteOffset.
3466                 //
3467 
3468                 LfnByteOffset = FoundOffset -
3469                                 FAT_LFN_DIRENTS_NEEDED(&Lfn) * sizeof(LFN_DIRENT);
3470 
3471                 BytesUsed = FoundOffset - LfnByteOffset + sizeof(DIRENT);
3472 
3473                 //
3474                 //  Set a run to represent all the dirents used for this
3475                 //  file in the Dcb dir.
3476                 //
3477 
3478 #ifdef _MSC_VER
3479 #pragma prefast( suppress:28931, "needed for debug build" )
3480 #endif
3481                 Result = FsRtlAddLargeMcbEntry( &Mcb,
3482                                                 LfnByteOffset,
3483                                                 LfnByteOffset,
3484                                                 BytesUsed );
3485 
3486                 NT_ASSERT( Result );
3487 
3488                 //
3489                 //  Move on to the next dirent.
3490                 //
3491 
3492                 TotalBytesAllocated += (ULONG) BytesUsed;
3493                 QueryOffset = FoundOffset + sizeof(DIRENT);
3494             }
3495 
3496         } while ((Dirent != NULL) && (QueryOffset < DcbSize));
3497 
3498         if (Bcb != NULL) {
3499 
3500             FatUnpinBcb( IrpContext, Bcb );
3501         }
3502 
3503         //
3504         //  If we need more dirents than are available, bail.
3505         //
3506 
3507         if (DirentsNeeded > (DcbSize - TotalBytesAllocated)/sizeof(DIRENT)) {
3508 
3509             try_return(ReturnValue = (ULONG)-1);
3510         }
3511 
3512         //
3513         //  Now we are going to copy all the used and un-used parts of the
3514         //  directory to separate pool.
3515         //
3516         //  Allocate these buffers and pin the entire directory.
3517         //
3518 
3519         UnusedDirents =
3520         UnusedDirentBuffer = FsRtlAllocatePoolWithTag( PagedPool,
3521                                                        DcbSize - TotalBytesAllocated,
3522                                                        TAG_DIRENT );
3523 
3524         UsedDirents =
3525         UsedDirentBuffer = FsRtlAllocatePoolWithTag( PagedPool,
3526                                                      TotalBytesAllocated,
3527                                                      TAG_DIRENT );
3528 
3529         PagesPinned = (DcbSize + (PAGE_SIZE - 1 )) / PAGE_SIZE;
3530 
3531         Bcbs = FsRtlAllocatePoolWithTag( PagedPool,
3532                                          PagesPinned * sizeof(PBCB),
3533                                          TAG_BCB );
3534 
3535         RtlZeroMemory( Bcbs, PagesPinned * sizeof(PBCB) );
3536 
3537         for (Page = 0; Page < PagesPinned; Page += 1) {
3538 
3539             ULONG PinSize;
3540 
3541             //
3542             //  Don't try to pin beyond the Dcb size.
3543             //
3544 
3545             if ((Page + 1) * PAGE_SIZE > DcbSize) {
3546 
3547                 PinSize = DcbSize - (Page * PAGE_SIZE);
3548 
3549             } else {
3550 
3551                 PinSize = PAGE_SIZE;
3552             }
3553 
3554             FatPrepareWriteDirectoryFile( IrpContext,
3555                                           Dcb,
3556                                           Page * PAGE_SIZE,
3557                                           PinSize,
3558                                           &Bcbs[Page],
3559 #ifndef __REACTOS__
3560                                           &Dirent,
3561 #else
3562                                           (PVOID *)&Dirent,
3563 #endif
3564                                           FALSE,
3565                                           TRUE,
3566                                           &DontCare );
3567 
3568             if (Page == 0) {
3569                 Directory = (PUCHAR)Dirent;
3570             }
3571         }
3572 
3573         TotalRuns = FsRtlNumberOfRunsInLargeMcb( &Mcb );
3574 
3575         for (Run = 0; Run < TotalRuns; Run++) {
3576 
3577             LBO Vbo;
3578             LBO Lbo;
3579 
3580 #ifdef _MSC_VER
3581 #pragma prefast( suppress:28931, "needed for debug build" )
3582 #endif
3583             Result = FsRtlGetNextLargeMcbEntry( &Mcb,
3584                                                 Run,
3585                                                 &Vbo,
3586                                                 &Lbo,
3587                                                 (PLONGLONG)&BytesUsed );
3588 
3589             NT_ASSERT(Result);
3590 
3591             //
3592             //  Copy each run to their specific pool.
3593             //
3594 
3595             if (Lbo != -1) {
3596 
3597                 RtlCopyMemory( UsedDirents,
3598                                Directory + Vbo,
3599                                (ULONG) BytesUsed );
3600 
3601                 UsedDirents += BytesUsed;
3602 
3603             } else {
3604 
3605                 RtlCopyMemory( UnusedDirents,
3606                                Directory + Vbo,
3607                                (ULONG) BytesUsed );
3608 
3609                 UnusedDirents += BytesUsed;
3610             }
3611         }
3612 
3613         //
3614         //  Marking all the un-used dirents as "deleted".  This will reclaim
3615         //  storage used by orphaned LFNs.
3616         //
3617 
3618         for (Char = UnusedDirentBuffer; Char < UnusedDirents; Char += sizeof(DIRENT)) {
3619 
3620             *Char = FAT_DIRENT_DELETED;
3621         }
3622 
3623         //
3624         //  Now, for the permanent step.  Copy the two pool buffer back to the
3625         //  real Dcb directory, and flush the Dcb directory
3626         //
3627 
3628         NT_ASSERT( TotalBytesAllocated == (ULONG)(UsedDirents - UsedDirentBuffer) );
3629 
3630         RtlCopyMemory( Directory, UsedDirentBuffer, TotalBytesAllocated );
3631 
3632         RtlCopyMemory( Directory + TotalBytesAllocated,
3633                        UnusedDirentBuffer,
3634                        UnusedDirents - UnusedDirentBuffer );
3635 
3636         //
3637         //  We need to unpin here so that the UnpinRepinned won't deadlock.
3638         //
3639 
3640         if (Bcbs) {
3641             for (Page = 0; Page < PagesPinned; Page += 1) {
3642                 FatUnpinBcb( IrpContext, Bcbs[Page] );
3643             }
3644             ExFreePool(Bcbs);
3645             Bcbs = NULL;
3646         }
3647 
3648         //
3649         //  Now make the free dirent bitmap reflect the new state of the Dcb
3650         //  directory.
3651         //
3652 
3653         RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
3654                     0,
3655                     TotalBytesAllocated / sizeof(DIRENT) );
3656 
3657         RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
3658                       TotalBytesAllocated / sizeof(DIRENT),
3659                       (DcbSize - TotalBytesAllocated) / sizeof(DIRENT) );
3660 
3661         ReturnValue = TotalBytesAllocated / sizeof(DIRENT);
3662 
3663         //
3664         //  Flush the directory to disk.  If we raise, we will need to invalidate
3665         //  all of the children.  Sorry, guys, but I can't figure out where you are
3666         //  now - if this failed I probably can't read the media either.  And we
3667         //  probably purged the cache to boot.
3668         //
3669 
3670         _SEH2_TRY {
3671 
3672             FatUnpinRepinnedBcbs( IrpContext );
3673 
3674         } _SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
3675                  EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
3676 
3677               InvalidateFcbs = TRUE;
3678         } _SEH2_END;
3679 
3680         //
3681         //  OK, now nothing can go wrong.  We have two more things to do.
3682         //  First, we have to fix up all the dirent offsets in any open Fcbs.
3683         //  If we cannot now find the Fcb, the file is marked invalid.  Also,
3684         //  we skip deleted files.
3685         //
3686 
3687         for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink;
3688              Links != &Dcb->Specific.Dcb.ParentDcbQueue;
3689              Links = Links->Flink) {
3690 
3691             PBCB TmpBcb = NULL;
3692             ULONG TmpOffset = 0;
3693             PDIRENT TmpDirent = NULL;
3694             ULONG PreviousLfnSpread;
3695 
3696             Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
3697 
3698             if (IsFileDeleted( IrpContext, Fcb )) {
3699 
3700                 continue;
3701             }
3702 
3703             //
3704             //  If we aren't already giving up, safely try to pick up the dirent
3705             //  to update the Fcb.  If this raises, we have to give up and blow
3706             //  evenyone else away too.
3707             //
3708 
3709             if (!InvalidateFcbs) {
3710 
3711                 _SEH2_TRY {
3712 
3713                     FatLocateSimpleOemDirent( IrpContext,
3714                                               Dcb,
3715                                               &Fcb->ShortName.Name.Oem,
3716                                               &TmpDirent,
3717                                               &TmpBcb,
3718                                               (PVBO)&TmpOffset );
3719 
3720                 } _SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
3721                          EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
3722 
3723                       InvalidateFcbs = TRUE;
3724                 } _SEH2_END;
3725             }
3726 
3727             if (TmpBcb == NULL || InvalidateFcbs) {
3728 
3729                 FatUnpinBcb( IrpContext, TmpBcb );
3730                 FatMarkFcbCondition( IrpContext, Fcb, FcbBad, TRUE );
3731 
3732             } else {
3733 
3734                 FatUnpinBcb( IrpContext, TmpBcb );
3735 
3736                 PreviousLfnSpread = Fcb->DirentOffsetWithinDirectory -
3737                                     Fcb->LfnOffsetWithinDirectory;
3738 
3739                 Fcb->DirentOffsetWithinDirectory = TmpOffset;
3740                 Fcb->LfnOffsetWithinDirectory = TmpOffset - PreviousLfnSpread;
3741             }
3742         }
3743 
3744     try_exit: NOTHING;
3745     } _SEH2_FINALLY {
3746 
3747         //
3748         //  Free all our resources and stuff.
3749         //
3750 
3751         if (McbInitialized) {
3752             FsRtlUninitializeLargeMcb( &Mcb );
3753         }
3754 
3755         if (Lfn.Buffer) {
3756             ExFreePool( Lfn.Buffer );
3757         }
3758 
3759         if (UnusedDirentBuffer) {
3760             ExFreePool( UnusedDirentBuffer );
3761         }
3762 
3763         if (UsedDirentBuffer) {
3764             ExFreePool( UsedDirentBuffer );
3765         }
3766 
3767         if (Bcbs) {
3768             for (Page = 0; Page < PagesPinned; Page += 1) {
3769                 FatUnpinBcb( IrpContext, Bcbs[Page] );
3770             }
3771             ExFreePool(Bcbs);
3772         }
3773 
3774         FatUnpinBcb( IrpContext, Bcb );
3775 
3776         for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink;
3777              Links != &Dcb->Specific.Dcb.ParentDcbQueue;
3778              Links = Links->Flink) {
3779 
3780             Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
3781 
3782             ExReleaseResourceLite( Fcb->Header.Resource );
3783         }
3784 
3785         IrpContext->Flags = SavedIrpContextFlag;
3786     } _SEH2_END;
3787 
3788     //
3789     //  Now return the offset of the first free dirent to the caller.
3790     //
3791 
3792     return ReturnValue;
3793 }
3794 
3795 
3796 
3797