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