[FASTFAT] When overwriting a file, also update its write time
[reactos.git] / drivers / filesystems / fastfat_new / dirsup.c
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 \f
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 \f
549
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 \f
651 VOID
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 \f
783
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
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 \f
1074
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 \f
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 \f
1797
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 \f
1939
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 \f
2043
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 \f
2167
2168
2169 \f
2170 VOID
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 \f
2439 VOID
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 \f
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
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
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 #ifndef __REACTOS__
2637 FAT_TIME_STAMP CurrentFatTime = {0};
2638 #else
2639 FAT_TIME_STAMP CurrentFatTime = {{0}};
2640 #endif
2641
2642 LARGE_INTEGER CurrentTime;
2643 #ifndef __REACTOS__
2644 LARGE_INTEGER CurrentDay = {0};
2645 #else
2646 LARGE_INTEGER CurrentDay = {.LowPart = 0, .HighPart = 0};
2647 #endif
2648 LARGE_INTEGER LastAccessDay;
2649
2650 PAGED_CODE();
2651
2652 //
2653 // Nothing to do if the fcb is bad, volume is readonly or we got the
2654 // root dir.
2655 //
2656
2657 if (FcbOrDcb->FcbCondition != FcbGood ||
2658 NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB ||
2659 FlagOn(FcbOrDcb->Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
2660
2661 return;
2662 }
2663
2664 //
2665 // Check if we should be changing the time or file size and set
2666 // the archive bit on the file.
2667 //
2668
2669 KeQuerySystemTime( &CurrentTime );
2670
2671 //
2672 // Note that we HAVE to use BooleanFlagOn() here because
2673 // FO_FILE_SIZE_CHANGED > 0x80 (i.e., not in the first byte).
2674 //
2675
2676 SetArchiveBit = BooleanFlagOn(FileObject->Flags, FO_FILE_MODIFIED);
2677
2678 UpdateLastWriteTime = FlagOn(FileObject->Flags, FO_FILE_MODIFIED) &&
2679 !FlagOn(Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE);
2680
2681 UpdateFileSize = NodeType(FcbOrDcb) == FAT_NTC_FCB &&
2682 BooleanFlagOn(FileObject->Flags, FO_FILE_SIZE_CHANGED);
2683
2684 //
2685 // Do one further check here of access time. Only update it if
2686 // the current version is at least one day old. We know that
2687 // the current FcbOrDcb->LastAccessTime corresponds to 12 midnight local
2688 // time, so just see if the current time is on the same day.
2689 //
2690
2691 if (FatData.ChicagoMode &&
2692 (UpdateLastWriteTime ||
2693 FlagOn(FileObject->Flags, FO_FILE_FAST_IO_READ)) &&
2694 !FlagOn(Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS)) {
2695
2696 ExSystemTimeToLocalTime( &FcbOrDcb->LastAccessTime, &LastAccessDay );
2697 ExSystemTimeToLocalTime( &CurrentTime, &CurrentDay );
2698
2699 LastAccessDay.QuadPart /= FatOneDay.QuadPart;
2700 CurrentDay.QuadPart /= FatOneDay.QuadPart;
2701
2702 if (LastAccessDay.LowPart != CurrentDay.LowPart) {
2703
2704 UpdateLastAccessTime = TRUE;
2705
2706 } else {
2707
2708 UpdateLastAccessTime = FALSE;
2709 }
2710
2711 } else {
2712
2713 UpdateLastAccessTime = FALSE;
2714 }
2715
2716 if (SetArchiveBit ||
2717 UpdateFileSize ||
2718 UpdateLastWriteTime ||
2719 UpdateLastAccessTime
2720 ) {
2721
2722 DebugTrace(0, Dbg, "Update Time and/or file size on File/Dir\n", 0);
2723
2724 _SEH2_TRY {
2725
2726 _SEH2_TRY {
2727
2728 #if (NTDDI_VERSION >= NTDDI_WIN8)
2729 //
2730 // Break parent directory oplock. Directory oplock breaks are
2731 // always advisory, so we will never block/get STATUS_PENDING here.
2732 //
2733
2734 if (FcbOrDcb->ParentDcb != NULL) {
2735
2736 FsRtlCheckOplockEx( FatGetFcbOplock(FcbOrDcb->ParentDcb),
2737 IrpContext->OriginatingIrp,
2738 OPLOCK_FLAG_PARENT_OBJECT,
2739 NULL,
2740 NULL,
2741 NULL );
2742 }
2743 #endif
2744
2745 //
2746 // Get the dirent
2747 //
2748
2749 FatGetDirentFromFcbOrDcb( IrpContext,
2750 FcbOrDcb,
2751 FALSE,
2752 &Dirent,
2753 &DirentBcb );
2754
2755 if (UpdateLastWriteTime || UpdateLastAccessTime) {
2756
2757 (VOID)FatNtTimeToFatTime( IrpContext,
2758 &CurrentTime,
2759 TRUE,
2760 &CurrentFatTime,
2761 NULL );
2762 }
2763
2764 if (SetArchiveBit) {
2765
2766 Dirent->Attributes |= FILE_ATTRIBUTE_ARCHIVE;
2767 FcbOrDcb->DirentFatFlags |= FILE_ATTRIBUTE_ARCHIVE;
2768
2769 NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
2770 UpdateDirent = TRUE;
2771 }
2772
2773 if (UpdateLastWriteTime) {
2774
2775 //
2776 // Update its time of last write
2777 //
2778
2779 FcbOrDcb->LastWriteTime = CurrentTime;
2780 Dirent->LastWriteTime = CurrentFatTime;
2781
2782 //
2783 // We call the notify package to report that the
2784 // last modification time has changed.
2785 //
2786
2787 NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
2788 UpdateDirent = TRUE;
2789 }
2790
2791 if (UpdateLastAccessTime) {
2792
2793 //
2794 // Now we have to truncate the local time down
2795 // to the current day, then convert back to UTC.
2796 //
2797
2798 FcbOrDcb->LastAccessTime.QuadPart =
2799 CurrentDay.QuadPart * FatOneDay.QuadPart;
2800
2801 ExLocalTimeToSystemTime( &FcbOrDcb->LastAccessTime,
2802 &FcbOrDcb->LastAccessTime );
2803
2804 Dirent->LastAccessDate = CurrentFatTime.Date;
2805
2806 //
2807 // We call the notify package to report that the
2808 // last access time has changed.
2809 //
2810
2811 NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
2812 UpdateDirent = TRUE;
2813 }
2814
2815 if (UpdateFileSize) {
2816
2817 //
2818 // Perhaps we were called to make certain that the
2819 // filesize on disc was updated - don't bother updating
2820 // and firing the filter if nothing changed.
2821 //
2822
2823 NT_ASSERT( NodeType(FcbOrDcb) == FAT_NTC_FCB );
2824
2825 if (Dirent->FileSize != FcbOrDcb->Header.FileSize.LowPart) {
2826
2827 //
2828 // Update the dirent file size
2829 //
2830
2831 Dirent->FileSize = FcbOrDcb->Header.FileSize.LowPart;
2832
2833 //
2834 // We call the notify package to report that the
2835 // size has changed.
2836 //
2837
2838 NotifyFilter |= FILE_NOTIFY_CHANGE_SIZE;
2839 UpdateDirent = TRUE;
2840 }
2841
2842
2843 }
2844
2845
2846 FatNotifyReportChange( IrpContext,
2847 FcbOrDcb->Vcb,
2848 FcbOrDcb,
2849 NotifyFilter,
2850 FILE_ACTION_MODIFIED );
2851
2852 if (UpdateDirent) {
2853
2854 //
2855 // If all we did was update last access time,
2856 // don't mark the volume dirty.
2857 //
2858
2859 FatSetDirtyBcb( IrpContext,
2860 DirentBcb,
2861 NotifyFilter == FILE_NOTIFY_CHANGE_LAST_ACCESS ?
2862 NULL : FcbOrDcb->Vcb,
2863 TRUE );
2864 }
2865
2866 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
2867 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
2868
2869 FatResetExceptionState( IrpContext );
2870 } _SEH2_END;
2871
2872 } _SEH2_FINALLY {
2873
2874 FatUnpinBcb( IrpContext, DirentBcb );
2875 } _SEH2_END;
2876 }
2877
2878 }
2879
2880 \f
2881 //
2882 // Internal support routine
2883 //
2884
2885 UCHAR
2886 FatComputeLfnChecksum (
2887 PDIRENT Dirent
2888 )
2889
2890 /*++
2891
2892 Routine Description:
2893
2894 This routine computes the Chicago long file name checksum.
2895
2896 Arguments:
2897
2898 Dirent - Specifies the dirent that we are to compute a checksum for.
2899
2900 Return Value:
2901
2902 The checksum.
2903
2904 --*/
2905
2906 {
2907 ULONG i;
2908 UCHAR Checksum;
2909
2910 PAGED_CODE();
2911
2912 Checksum = Dirent->FileName[0];
2913
2914 for (i=1; i < 11; i++) {
2915
2916 Checksum = ((Checksum & 1) ? 0x80 : 0) +
2917 (Checksum >> 1) +
2918 Dirent->FileName[i];
2919 }
2920
2921 return Checksum;
2922 }
2923
2924 \f
2925
2926 #if 0 // It turns out Win95 is still creating short names without a ~
2927
2928 //
2929 // Internal support routine
2930 //
2931
2932 BOOLEAN
2933 FatIsLfnPairValid (
2934 PWCHAR Lfn,
2935 ULONG LfnSize,
2936 PDIRENT Dirent
2937 )
2938
2939 /*++
2940
2941 Routine Description:
2942
2943 This routine does a few more checks to make sure that a LFN/short
2944 name pairing is legitimate. Basically this is the test:
2945
2946 Pairing is valid if:
2947
2948 DIRENT has a ~ character ||
2949 (LFN is 8.3 compliant &&
2950 (LFN has extended character(s) ? TRUE :
2951 LFN upcases to DIRENT))
2952
2953 When checking for the presence of a tilda character in the short
2954 name, note that we purposely do a single byte search instead of
2955 converting the name to UNICODE and looking there for the tilda.
2956 This protects us from accidently missing the tilda if the
2957 preceding byte is a lead byte in the current Oem code page,
2958 but wasn't in the Oem code page that created the file.
2959
2960 Also note that if the LFN is longer than 12 characters, then the
2961 second clause of the OR must be false.
2962
2963 Arguments:
2964
2965 Lfn - Points to a buffer of UNICODE chars.
2966
2967 LfnSize - This is the size of the LFN in characters.
2968
2969 Dirent - Specifies the dirent we are to consider.
2970
2971 Return Value:
2972
2973 TRUE if the Lfn/DIRENT form a legitimate pair, FALSE otherwise.
2974
2975 --*/
2976
2977 {
2978 ULONG i;
2979 BOOLEAN ExtendedChars;
2980 ULONG DirentBuffer[3];
2981 PUCHAR DirentName;
2982 ULONG DirentIndex;
2983 BOOLEAN DotEncountered;
2984
2985 //
2986 // First, look for a tilda
2987 //
2988
2989 for (i=0; i<11; i++) {
2990 if (Dirent->FileName[i] == '~') {
2991 return TRUE;
2992 }
2993 }
2994
2995 //
2996 // No tilda. If the LFN is longer than 12 characters, then it can
2997 // neither upcase to the DIRENT nor be 8.3 complient.
2998 //
2999
3000 if (LfnSize > 12) {
3001 return FALSE;
3002 }
3003
3004 //
3005 // Now see if the name is 8.3, and build an upcased DIRENT as well.
3006 //
3007
3008 DirentBuffer[0] = 0x20202020;
3009 DirentBuffer[1] = 0x20202020;
3010 DirentBuffer[2] = 0x20202020;
3011
3012 DirentName = (PUCHAR)DirentBuffer;
3013
3014 ExtendedChars = FALSE;
3015 DirentIndex = 0;
3016 DotEncountered = FALSE;
3017
3018 for (i=0; i < LfnSize; i++) {
3019
3020 //
3021 // Do dot transition work
3022 //
3023
3024 if (Lfn[i] == L'.') {
3025 if (DotEncountered ||
3026 (i > 8) ||
3027 ((LfnSize - i) > 4) ||
3028 (i && Lfn[i-1] == L' ')) {
3029 return FALSE;
3030 }
3031 DotEncountered = TRUE;
3032 DirentIndex = 8;
3033 continue;
3034 }
3035
3036 //
3037 // The character must be legal in order to be 8.3
3038 //
3039
3040 if ((Lfn[i] < 0x80) &&
3041 !FsRtlIsAnsiCharacterLegalFat((UCHAR)Lfn[i], FALSE)) {
3042 return FALSE;
3043 }
3044
3045 //
3046 // If the name contains no extended chars, continue building DIRENT
3047 //
3048
3049 if (!ExtendedChars) {
3050 if (Lfn[i] > 0x7f) {
3051 ExtendedChars = TRUE;
3052 } else {
3053 DirentName[DirentIndex++] = (UCHAR) (
3054 Lfn[i] < 'a' ? Lfn[i] : Lfn[i] <= 'z' ? Lfn[i] - ('a' - 'A') : Lfn[i]);
3055 }
3056 }
3057 }
3058
3059 //
3060 // If the LFN ended in a space, or there was no dot and the name
3061 // has more than 8 characters, then it is not 8.3 compliant.
3062 //
3063
3064 if ((Lfn[LfnSize - 1] == L' ') ||
3065 (!DotEncountered && (LfnSize > 8))) {
3066 return FALSE;
3067 }
3068
3069 //
3070 // OK, now if we got this far then the LFN is 8dot3. If there are
3071 // no extended characters, then we can also check to make sure that
3072 // the LFN is only a case varient of the DIRENT.
3073 //
3074
3075 if (!ExtendedChars &&
3076 !RtlEqualMemory(Dirent->FileName, DirentName, 11)) {
3077
3078 return FALSE;
3079 }
3080
3081 //
3082 // We have now verified this pairing the very best we can without
3083 // knowledge of the code page that the file was created under.
3084 //
3085
3086 return TRUE;
3087 }
3088 #endif //0
3089 \f
3090 //
3091 // Internal support routine
3092 //
3093
3094 _Requires_lock_held_(_Global_critical_region_)
3095 VOID
3096 FatRescanDirectory (
3097 PIRP_CONTEXT IrpContext,
3098 PDCB Dcb
3099 )
3100
3101 /*++
3102
3103 Routine Description:
3104
3105 This routine rescans the given directory, finding the first unused
3106 dirent, first deleted dirent, and setting the free dirent bitmap
3107 appropriately.
3108
3109 Arguments:
3110
3111 Dcb - Supplies the directory to rescan.
3112
3113 Return Value:
3114
3115 None.
3116
3117 --*/
3118
3119 {
3120 PBCB Bcb = NULL;
3121 PDIRENT Dirent = NULL;
3122 NTSTATUS Status = STATUS_SUCCESS;
3123
3124 ULONG UnusedVbo;
3125 ULONG DeletedHint;
3126 ULONG DirentIndex;
3127 ULONG DirentsThisRun;
3128 ULONG StartIndexOfThisRun;
3129
3130 enum RunType {
3131 InitialRun,
3132 FreeDirents,
3133 AllocatedDirents,
3134 } CurrentRun;
3135
3136 PAGED_CODE();
3137
3138 DebugTrace( 0, Dbg, "We must scan the whole directory.\n", 0);
3139
3140 UnusedVbo = 0;
3141 DeletedHint = 0xffffffff;
3142
3143 //
3144 // To start with, we have to find out if the first dirent is free.
3145 //
3146
3147 CurrentRun = InitialRun;
3148 DirentIndex =
3149 StartIndexOfThisRun = 0;
3150
3151 _SEH2_TRY {
3152
3153 while ( TRUE ) {
3154
3155 BOOLEAN DirentDeleted;
3156
3157 //
3158 // Read a dirent
3159 //
3160
3161 FatReadDirent( IrpContext,
3162 Dcb,
3163 UnusedVbo,
3164 &Bcb,
3165 &Dirent,
3166 &Status );
3167
3168 //
3169 // If EOF, or we found a NEVER_USED entry, we exit the loop
3170 //
3171
3172 if ( (Status == STATUS_END_OF_FILE ) ||
3173 (Dirent->FileName[0] == FAT_DIRENT_NEVER_USED)) {
3174
3175 break;
3176 }
3177
3178 //
3179 // If the dirent is DELETED, and it is the first one we found, set
3180 // it in the deleted hint.
3181 //
3182
3183 if (Dirent->FileName[0] == FAT_DIRENT_DELETED) {
3184
3185 DirentDeleted = TRUE;
3186
3187 if (DeletedHint == 0xffffffff) {
3188
3189 DeletedHint = UnusedVbo;
3190 }
3191
3192 } else {
3193
3194 DirentDeleted = FALSE;
3195 }
3196
3197 //
3198 // Check for the first time through the loop, and determine
3199 // the current run type.
3200 //
3201
3202 if (CurrentRun == InitialRun) {
3203
3204 CurrentRun = DirentDeleted ?
3205 FreeDirents : AllocatedDirents;
3206
3207 } else {
3208
3209 //
3210 // Are we switching from a free run to an allocated run?
3211 //
3212
3213 if ((CurrentRun == FreeDirents) && !DirentDeleted) {
3214
3215 DirentsThisRun = DirentIndex - StartIndexOfThisRun;
3216
3217 RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
3218 StartIndexOfThisRun,
3219 DirentsThisRun );
3220
3221 CurrentRun = AllocatedDirents;
3222 StartIndexOfThisRun = DirentIndex;
3223 }
3224
3225 //
3226 // Are we switching from an allocated run to a free run?
3227 //
3228
3229 if ((CurrentRun == AllocatedDirents) && DirentDeleted) {
3230
3231 DirentsThisRun = DirentIndex - StartIndexOfThisRun;
3232
3233 RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
3234 StartIndexOfThisRun,
3235 DirentsThisRun );
3236
3237 CurrentRun = FreeDirents;
3238 StartIndexOfThisRun = DirentIndex;
3239 }
3240 }
3241
3242 //
3243 // Move on to the next dirent.
3244 //
3245
3246 UnusedVbo += sizeof(DIRENT);
3247 Dirent += 1;
3248 DirentIndex += 1;
3249 }
3250
3251 //
3252 // Now we have to record the final run we encoutered
3253 //
3254
3255 DirentsThisRun = DirentIndex - StartIndexOfThisRun;
3256
3257 if ((CurrentRun == FreeDirents) || (CurrentRun == InitialRun)) {
3258
3259 RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
3260 StartIndexOfThisRun,
3261 DirentsThisRun );
3262
3263 } else {
3264
3265 RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
3266 StartIndexOfThisRun,
3267 DirentsThisRun );
3268 }
3269
3270 //
3271 // Now if there we bailed prematurely out of the loop because
3272 // we hit an unused entry, set all the rest as free.
3273 //
3274
3275 if (UnusedVbo < Dcb->Header.AllocationSize.LowPart) {
3276
3277 StartIndexOfThisRun = UnusedVbo / sizeof(DIRENT);
3278
3279 DirentsThisRun = (Dcb->Header.AllocationSize.LowPart -
3280 UnusedVbo) / sizeof(DIRENT);
3281
3282 RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
3283 StartIndexOfThisRun,
3284 DirentsThisRun);
3285 }
3286
3287 } _SEH2_FINALLY {
3288
3289 FatUnpinBcb( IrpContext, Bcb );
3290 } _SEH2_END;
3291
3292 //
3293 // If there weren't any DELETED entries, set the index to our current
3294 // position.
3295 //
3296
3297 if (DeletedHint == 0xffffffff) { DeletedHint = UnusedVbo; }
3298
3299 Dcb->Specific.Dcb.UnusedDirentVbo = UnusedVbo;
3300 Dcb->Specific.Dcb.DeletedDirentHint = DeletedHint;
3301
3302 return;
3303 }
3304
3305 \f
3306 //
3307 // Internal support routine
3308 //
3309
3310 _Requires_lock_held_(_Global_critical_region_)
3311 ULONG
3312 FatDefragDirectory (
3313 IN PIRP_CONTEXT IrpContext,
3314 IN PDCB Dcb,
3315 IN ULONG DirentsNeeded
3316 )
3317
3318 /*++
3319
3320 Routine Description:
3321
3322 This routine determines if the requested number of dirents can be found
3323 in the directory, looking for deleted dirents and orphaned LFNs. If the
3324 request can be satisifed, orphaned LFNs are marked as deleted, and deleted
3325 dirents are all grouped together at the end of the directory.
3326
3327 Note that this routine is currently used only on the root directory, but
3328 it is completely general and could be used on any directory.
3329
3330 Arguments:
3331
3332 Dcb - Supplies the directory to defrag.
3333
3334 Return Value:
3335
3336 The Index of the first dirent available for use, or -1 if the
3337 request cannot be satisfied.
3338
3339 --*/
3340
3341 {
3342 ULONG SavedIrpContextFlag;
3343 PLIST_ENTRY Links;
3344 ULONG ReturnValue = 0;
3345 PFCB Fcb;
3346
3347 PBCB Bcb = NULL;
3348 PDIRENT Dirent = NULL;
3349 UNICODE_STRING Lfn = {0,0,NULL};
3350
3351 LARGE_MCB Mcb;
3352 BOOLEAN McbInitialized = FALSE;
3353 BOOLEAN InvalidateFcbs = FALSE;
3354
3355 PUCHAR Directory = NULL;
3356 PUCHAR UnusedDirents;
3357 PUCHAR UnusedDirentBuffer = NULL;
3358 PUCHAR UsedDirents;
3359 PUCHAR UsedDirentBuffer = NULL;
3360
3361 PBCB *Bcbs = NULL;
3362 ULONG Page;
3363 ULONG PagesPinned = 0;
3364
3365 ULONG DcbSize;
3366 ULONG TotalBytesAllocated = 0;
3367
3368 PAGED_CODE();
3369
3370 //
3371 // We assume we own the Vcb.
3372 //
3373
3374 NT_ASSERT( FatVcbAcquiredExclusive(IrpContext, Dcb->Vcb) );
3375
3376 //
3377 // We will only attempt this on directories less than 0x40000 bytes
3378 // long (by default on DOS the root directory is only 0x2000 long).
3379 // This is to avoid a cache manager complication.
3380 //
3381
3382 DcbSize = Dcb->Header.AllocationSize.LowPart;
3383
3384 if (DcbSize > 0x40000) {
3385
3386 return (ULONG)-1;
3387 }
3388
3389 //
3390 // Force wait to TRUE
3391 //
3392
3393 SavedIrpContextFlag = IrpContext->Flags;
3394
3395 SetFlag( IrpContext->Flags,
3396 IRP_CONTEXT_FLAG_WAIT | IRP_CONTEXT_FLAG_WRITE_THROUGH );
3397
3398 //
3399 // Now acquire all open Fcbs in the Dcb exclusive.
3400 //
3401
3402 for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink;
3403 Links != &Dcb->Specific.Dcb.ParentDcbQueue;
3404 Links = Links->Flink) {
3405
3406 Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
3407
3408 (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.Resource, TRUE );
3409 }
3410
3411 _SEH2_TRY {
3412
3413 CCB Ccb;
3414 ULONG QueryOffset = 0;
3415 ULONG FoundOffset = 0;
3416 ULONGLONG BytesUsed = 0;
3417
3418 NTSTATUS DontCare;
3419 ULONG Run;
3420 ULONG TotalRuns;
3421 BOOLEAN Result;
3422 PUCHAR Char;
3423
3424 //
3425 // We are going to build a new bitmap that will show all orphaned
3426 // LFNs as well as deleted dirents as available.
3427 //
3428 // Initialize our local CCB that will match all files and even
3429 // a label if it is here.
3430 //
3431
3432 RtlZeroMemory( &Ccb, sizeof(CCB) );
3433 Ccb.Flags = CCB_FLAG_MATCH_ALL | CCB_FLAG_MATCH_VOLUME_ID;
3434
3435 //
3436 // Init the Long File Name string.
3437 //
3438
3439 Lfn.MaximumLength = 260 * sizeof(WCHAR);
3440 Lfn.Buffer = FsRtlAllocatePoolWithTag( PagedPool,
3441 260*sizeof(WCHAR),
3442 TAG_FILENAME_BUFFER );
3443
3444 //
3445 // Initalize the Mcb. We use this structure to keep track of runs
3446 // of free and allocated dirents. Runs are identity allocations, and
3447 // holes are free dirents.
3448 //
3449
3450 FsRtlInitializeLargeMcb( &Mcb, PagedPool );
3451
3452 McbInitialized = TRUE;
3453
3454 do {
3455
3456 FatLocateDirent( IrpContext,
3457 Dcb,
3458 &Ccb,
3459 QueryOffset,
3460 NULL,
3461 &Dirent,
3462 &Bcb,
3463 (PVBO)&FoundOffset,
3464 NULL,
3465 &Lfn,
3466 NULL );
3467
3468 if (Dirent != NULL) {
3469
3470 ULONG LfnByteOffset;
3471
3472 //
3473 // Compute the LfnByteOffset.
3474 //
3475
3476 LfnByteOffset = FoundOffset -
3477 FAT_LFN_DIRENTS_NEEDED(&Lfn) * sizeof(LFN_DIRENT);
3478
3479 BytesUsed = FoundOffset - LfnByteOffset + sizeof(DIRENT);
3480
3481 //
3482 // Set a run to represent all the dirents used for this
3483 // file in the Dcb dir.
3484 //
3485
3486 #ifdef _MSC_VER
3487 #pragma prefast( suppress:28931, "needed for debug build" )
3488 #endif
3489 Result = FsRtlAddLargeMcbEntry( &Mcb,
3490 LfnByteOffset,
3491 LfnByteOffset,
3492 BytesUsed );
3493
3494 NT_ASSERT( Result );
3495
3496 //
3497 // Move on to the next dirent.
3498 //
3499
3500 TotalBytesAllocated += (ULONG) BytesUsed;
3501 QueryOffset = FoundOffset + sizeof(DIRENT);
3502 }
3503
3504 } while ((Dirent != NULL) && (QueryOffset < DcbSize));
3505
3506 if (Bcb != NULL) {
3507
3508 FatUnpinBcb( IrpContext, Bcb );
3509 }
3510
3511 //
3512 // If we need more dirents than are available, bail.
3513 //
3514
3515 if (DirentsNeeded > (DcbSize - TotalBytesAllocated)/sizeof(DIRENT)) {
3516
3517 try_return(ReturnValue = (ULONG)-1);
3518 }
3519
3520 //
3521 // Now we are going to copy all the used and un-used parts of the
3522 // directory to separate pool.
3523 //
3524 // Allocate these buffers and pin the entire directory.
3525 //
3526
3527 UnusedDirents =
3528 UnusedDirentBuffer = FsRtlAllocatePoolWithTag( PagedPool,
3529 DcbSize - TotalBytesAllocated,
3530 TAG_DIRENT );
3531
3532 UsedDirents =
3533 UsedDirentBuffer = FsRtlAllocatePoolWithTag( PagedPool,
3534 TotalBytesAllocated,
3535 TAG_DIRENT );
3536
3537 PagesPinned = (DcbSize + (PAGE_SIZE - 1 )) / PAGE_SIZE;
3538
3539 Bcbs = FsRtlAllocatePoolWithTag( PagedPool,
3540 PagesPinned * sizeof(PBCB),
3541 TAG_BCB );
3542
3543 RtlZeroMemory( Bcbs, PagesPinned * sizeof(PBCB) );
3544
3545 for (Page = 0; Page < PagesPinned; Page += 1) {
3546
3547 ULONG PinSize;
3548
3549 //
3550 // Don't try to pin beyond the Dcb size.
3551 //
3552
3553 if ((Page + 1) * PAGE_SIZE > DcbSize) {
3554
3555 PinSize = DcbSize - (Page * PAGE_SIZE);
3556
3557 } else {
3558
3559 PinSize = PAGE_SIZE;
3560 }
3561
3562 FatPrepareWriteDirectoryFile( IrpContext,
3563 Dcb,
3564 Page * PAGE_SIZE,
3565 PinSize,
3566 &Bcbs[Page],
3567 #ifndef __REACTOS__
3568 &Dirent,
3569 #else
3570 (PVOID *)&Dirent,
3571 #endif
3572 FALSE,
3573 TRUE,
3574 &DontCare );
3575
3576 if (Page == 0) {
3577 Directory = (PUCHAR)Dirent;
3578 }
3579 }
3580
3581 TotalRuns = FsRtlNumberOfRunsInLargeMcb( &Mcb );
3582
3583 for (Run = 0; Run < TotalRuns; Run++) {
3584
3585 LBO Vbo;
3586 LBO Lbo;
3587
3588 #ifdef _MSC_VER
3589 #pragma prefast( suppress:28931, "needed for debug build" )
3590 #endif
3591 Result = FsRtlGetNextLargeMcbEntry( &Mcb,
3592 Run,
3593 &Vbo,
3594 &Lbo,
3595 (PLONGLONG)&BytesUsed );
3596
3597 NT_ASSERT(Result);
3598
3599 //
3600 // Copy each run to their specific pool.
3601 //
3602
3603 if (Lbo != -1) {
3604
3605 RtlCopyMemory( UsedDirents,
3606 Directory + Vbo,
3607 (ULONG) BytesUsed );
3608
3609 UsedDirents += BytesUsed;
3610
3611 } else {
3612
3613 RtlCopyMemory( UnusedDirents,
3614 Directory + Vbo,
3615 (ULONG) BytesUsed );
3616
3617 UnusedDirents += BytesUsed;
3618 }
3619 }
3620
3621 //
3622 // Marking all the un-used dirents as "deleted". This will reclaim
3623 // storage used by orphaned LFNs.
3624 //
3625
3626 for (Char = UnusedDirentBuffer; Char < UnusedDirents; Char += sizeof(DIRENT)) {
3627
3628 *Char = FAT_DIRENT_DELETED;
3629 }
3630
3631 //
3632 // Now, for the permanent step. Copy the two pool buffer back to the
3633 // real Dcb directory, and flush the Dcb directory
3634 //
3635
3636 NT_ASSERT( TotalBytesAllocated == (ULONG)(UsedDirents - UsedDirentBuffer) );
3637
3638 RtlCopyMemory( Directory, UsedDirentBuffer, TotalBytesAllocated );
3639
3640 RtlCopyMemory( Directory + TotalBytesAllocated,
3641 UnusedDirentBuffer,
3642 UnusedDirents - UnusedDirentBuffer );
3643
3644 //
3645 // We need to unpin here so that the UnpinRepinned won't deadlock.
3646 //
3647
3648 if (Bcbs) {
3649 for (Page = 0; Page < PagesPinned; Page += 1) {
3650 FatUnpinBcb( IrpContext, Bcbs[Page] );
3651 }
3652 ExFreePool(Bcbs);
3653 Bcbs = NULL;
3654 }
3655
3656 //
3657 // Now make the free dirent bitmap reflect the new state of the Dcb
3658 // directory.
3659 //
3660
3661 RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
3662 0,
3663 TotalBytesAllocated / sizeof(DIRENT) );
3664
3665 RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
3666 TotalBytesAllocated / sizeof(DIRENT),
3667 (DcbSize - TotalBytesAllocated) / sizeof(DIRENT) );
3668
3669 ReturnValue = TotalBytesAllocated / sizeof(DIRENT);
3670
3671 //
3672 // Flush the directory to disk. If we raise, we will need to invalidate
3673 // all of the children. Sorry, guys, but I can't figure out where you are
3674 // now - if this failed I probably can't read the media either. And we
3675 // probably purged the cache to boot.
3676 //
3677
3678 _SEH2_TRY {
3679
3680 FatUnpinRepinnedBcbs( IrpContext );
3681
3682 } _SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
3683 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
3684
3685 InvalidateFcbs = TRUE;
3686 } _SEH2_END;
3687
3688 //
3689 // OK, now nothing can go wrong. We have two more things to do.
3690 // First, we have to fix up all the dirent offsets in any open Fcbs.
3691 // If we cannot now find the Fcb, the file is marked invalid. Also,
3692 // we skip deleted files.
3693 //
3694
3695 for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink;
3696 Links != &Dcb->Specific.Dcb.ParentDcbQueue;
3697 Links = Links->Flink) {
3698
3699 PBCB TmpBcb = NULL;
3700 ULONG TmpOffset = 0;
3701 PDIRENT TmpDirent = NULL;
3702 ULONG PreviousLfnSpread;
3703
3704 Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
3705
3706 if (IsFileDeleted( IrpContext, Fcb )) {
3707
3708 continue;
3709 }
3710
3711 //
3712 // If we aren't already giving up, safely try to pick up the dirent
3713 // to update the Fcb. If this raises, we have to give up and blow
3714 // evenyone else away too.
3715 //
3716
3717 if (!InvalidateFcbs) {
3718
3719 _SEH2_TRY {
3720
3721 FatLocateSimpleOemDirent( IrpContext,
3722 Dcb,
3723 &Fcb->ShortName.Name.Oem,
3724 &TmpDirent,
3725 &TmpBcb,
3726 (PVBO)&TmpOffset );
3727
3728 } _SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
3729 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
3730
3731 InvalidateFcbs = TRUE;
3732 } _SEH2_END;
3733 }
3734
3735 if (TmpBcb == NULL || InvalidateFcbs) {
3736
3737 FatUnpinBcb( IrpContext, TmpBcb );
3738 FatMarkFcbCondition( IrpContext, Fcb, FcbBad, TRUE );
3739
3740 } else {
3741
3742 FatUnpinBcb( IrpContext, TmpBcb );
3743
3744 PreviousLfnSpread = Fcb->DirentOffsetWithinDirectory -
3745 Fcb->LfnOffsetWithinDirectory;
3746
3747 Fcb->DirentOffsetWithinDirectory = TmpOffset;
3748 Fcb->LfnOffsetWithinDirectory = TmpOffset - PreviousLfnSpread;
3749 }
3750 }
3751
3752 try_exit: NOTHING;
3753 } _SEH2_FINALLY {
3754
3755 //
3756 // Free all our resources and stuff.
3757 //
3758
3759 if (McbInitialized) {
3760 FsRtlUninitializeLargeMcb( &Mcb );
3761 }
3762
3763 if (Lfn.Buffer) {
3764 ExFreePool( Lfn.Buffer );
3765 }
3766
3767 if (UnusedDirentBuffer) {
3768 ExFreePool( UnusedDirentBuffer );
3769 }
3770
3771 if (UsedDirentBuffer) {
3772 ExFreePool( UsedDirentBuffer );
3773 }
3774
3775 if (Bcbs) {
3776 for (Page = 0; Page < PagesPinned; Page += 1) {
3777 FatUnpinBcb( IrpContext, Bcbs[Page] );
3778 }
3779 ExFreePool(Bcbs);
3780 }
3781
3782 FatUnpinBcb( IrpContext, Bcb );
3783
3784 for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink;
3785 Links != &Dcb->Specific.Dcb.ParentDcbQueue;
3786 Links = Links->Flink) {
3787
3788 Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
3789
3790 ExReleaseResourceLite( Fcb->Header.Resource );
3791 }
3792
3793 IrpContext->Flags = SavedIrpContextFlag;
3794 } _SEH2_END;
3795
3796 //
3797 // Now return the offset of the first free dirent to the caller.
3798 //
3799
3800 return ReturnValue;
3801 }
3802
3803
3804