1 /*++
2
3 Copyright (c) 1990-2000 Microsoft Corporation
4
5 Module Name:
6
7 EaSup.c
8
9 Abstract:
10
11 This module implements the cluster operations on the EA file for Fat.
12
13
14 --*/
15
16 #include "fatprocs.h"
17
18 //
19 // Local debug trace level
20 //
21
22 #define Dbg (DEBUG_TRACE_EA)
23
24 #ifdef ALLOC_PRAGMA
25 #pragma alloc_text(PAGE, FatAddEaSet)
26 #pragma alloc_text(PAGE, FatAppendPackedEa)
27 #pragma alloc_text(PAGE, FatCreateEa)
28 #pragma alloc_text(PAGE, FatDeleteEa)
29 #pragma alloc_text(PAGE, FatDeleteEaSet)
30 #pragma alloc_text(PAGE, FatDeletePackedEa)
31 #pragma alloc_text(PAGE, FatGetEaFile)
32 #pragma alloc_text(PAGE, FatGetEaLength)
33 #pragma alloc_text(PAGE, FatGetNeedEaCount)
34 #pragma alloc_text(PAGE, FatIsEaNameValid)
35 #pragma alloc_text(PAGE, FatLocateEaByName)
36 #pragma alloc_text(PAGE, FatLocateNextEa)
37 #pragma alloc_text(PAGE, FatReadEaSet)
38 #pragma alloc_text(PAGE, FatPinEaRange)
39 #pragma alloc_text(PAGE, FatMarkEaRangeDirty)
40 #pragma alloc_text(PAGE, FatUnpinEaRange)
41 #endif
42
43
44 //
45 // Any access to the Ea file must recognize when a section boundary is being
46 // crossed.
47 //
48
49 #define EA_SECTION_SIZE (0x00040000)
50
51
_Requires_lock_held_(_Global_critical_region_)52 _Requires_lock_held_(_Global_critical_region_)
53 VOID
54 FatGetEaLength (
55 IN PIRP_CONTEXT IrpContext,
56 IN PVCB Vcb,
57 IN PDIRENT Dirent,
58 OUT PULONG EaLength
59 )
60
61 /*++
62
63 Routine Description:
64
65 This routine looks up the Ea length for the Eas of the file. This
66 length is the length of the packed eas, including the 4 bytes which
67 contain the Ea length.
68
69 This routine pins down the Ea set for the desired file and copies
70 this field from the Ea set header.
71
72 Arguments:
73
74 Vcb - Vcb for the volume containing the Eas.
75
76 Dirent - Supplies a pointer to the dirent for the file in question.
77
78 EaLength - Supplies the address to store the length of the Eas.
79
80 Return Value:
81
82 None
83
84 --*/
85
86 {
87 PBCB EaBcb = NULL;
88 BOOLEAN LockedEaFcb = FALSE;
89 EA_RANGE EaSetRange;
90
91 PAGED_CODE();
92
93 DebugTrace(+1, Dbg, "FatGetEaLength ...\n", 0);
94
95 //
96 // If this is Fat32 volume, or if the handle is 0 then the Ea length is 0.
97 //
98
99 if (FatIsFat32( Vcb ) ||
100 Dirent->ExtendedAttributes == 0) {
101
102 *EaLength = 0;
103 DebugTrace(-1, Dbg, "FatGetEaLength -> %08lx\n", TRUE);
104 return;
105 }
106
107 RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
108
109 //
110 // Use a try to facilitate cleanup.
111 //
112
113 _SEH2_TRY {
114
115 PDIRENT EaDirent;
116 OEM_STRING ThisFilename;
117 UCHAR Buffer[12];
118 PEA_SET_HEADER EaSetHeader;
119
120 //
121 // Initial the local values.
122 //
123
124 EaBcb = NULL;
125 LockedEaFcb = FALSE;
126
127 //
128 // Try to get the Ea file object. Return FALSE on failure.
129 //
130
131 FatGetEaFile( IrpContext,
132 Vcb,
133 &EaDirent,
134 &EaBcb,
135 FALSE,
136 FALSE );
137
138 LockedEaFcb = TRUE;
139
140 //
141 // If we didn't get the file because it doesn't exist, then the
142 // disk is corrupted.
143 //
144
145 if (Vcb->VirtualEaFile == NULL) {
146
147 DebugTrace(0, Dbg, "FatGetEaLength: Ea file doesn't exist\n", 0);
148 FatRaiseStatus( IrpContext, STATUS_NO_EAS_ON_FILE );
149 }
150
151 //
152 // Try to pin down the Ea set header for the index in the
153 // dirent. If the operation doesn't complete, return FALSE
154 // from this routine.
155 //
156
157 ThisFilename.Buffer = (PCHAR)Buffer;
158 Fat8dot3ToString( IrpContext, Dirent, FALSE, &ThisFilename );
159
160 FatReadEaSet( IrpContext,
161 Vcb,
162 Dirent->ExtendedAttributes,
163 &ThisFilename,
164 FALSE,
165 &EaSetRange );
166
167 EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
168
169 //
170 // We now have the Ea set header for this file. We simply copy
171 // the Ea length field.
172 //
173
174 CopyUchar4( EaLength, EaSetHeader->cbList );
175 DebugTrace(0, Dbg, "FatGetEaLength: Length of Ea is -> %08lx\n",
176 *EaLength);
177
178 } _SEH2_FINALLY {
179
180 DebugUnwind( FatGetEaLength );
181
182 //
183 // Unpin the EaDirent and the EaSetHeader if pinned.
184 //
185
186 FatUnpinBcb( IrpContext, EaBcb );
187
188 FatUnpinEaRange( IrpContext, &EaSetRange );
189
190 //
191 // Release the Fcb for the Ea file if locked.
192 //
193
194 if (LockedEaFcb) {
195
196 FatReleaseFcb( IrpContext, Vcb->EaFcb );
197 }
198
199 DebugTrace(-1, Dbg, "FatGetEaLength: Ea length -> %08lx\n", *EaLength);
200 } _SEH2_END;
201
202 return;
203 }
204
205
_Requires_lock_held_(_Global_critical_region_)206 _Requires_lock_held_(_Global_critical_region_)
207 VOID
208 FatGetNeedEaCount (
209 IN PIRP_CONTEXT IrpContext,
210 IN PVCB Vcb,
211 IN PDIRENT Dirent,
212 OUT PULONG NeedEaCount
213 )
214
215 /*++
216
217 Routine Description:
218
219 This routine looks up the Need Ea count for the file. The value is the
220 in the ea header for the file.
221
222 Arguments:
223
224 Vcb - Vcb for the volume containing the Eas.
225
226 Dirent - Supplies a pointer to the dirent for the file in question.
227
228 NeedEaCount - Supplies the address to store the Need Ea count.
229
230 Return Value:
231
232 None
233
234 --*/
235
236 {
237 PBCB EaBcb = NULL;
238 BOOLEAN LockedEaFcb = FALSE;
239 EA_RANGE EaSetRange;
240
241 PAGED_CODE();
242
243 DebugTrace(+1, Dbg, "FatGetNeedEaCount ...\n", 0);
244
245 //
246 // If the handle is 0 then the Need Ea count is 0.
247 //
248
249 if (Dirent->ExtendedAttributes == 0) {
250
251 *NeedEaCount = 0;
252 DebugTrace(-1, Dbg, "FatGetNeedEaCount -> %08lx\n", TRUE);
253 return;
254 }
255
256 RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
257
258 //
259 // Use a try to facilitate cleanup.
260 //
261
262 _SEH2_TRY {
263
264 PDIRENT EaDirent;
265 OEM_STRING ThisFilename;
266 UCHAR Buffer[12];
267 PEA_SET_HEADER EaSetHeader;
268
269 //
270 // Initial the local values.
271 //
272
273 EaBcb = NULL;
274 LockedEaFcb = FALSE;
275
276 //
277 // Try to get the Ea file object. Return FALSE on failure.
278 //
279
280 FatGetEaFile( IrpContext,
281 Vcb,
282 &EaDirent,
283 &EaBcb,
284 FALSE,
285 FALSE );
286
287 LockedEaFcb = TRUE;
288
289 //
290 // If we didn't get the file because it doesn't exist, then the
291 // disk is corrupted.
292 //
293
294 if (Vcb->VirtualEaFile == NULL) {
295
296 DebugTrace(0, Dbg, "FatGetNeedEaCount: Ea file doesn't exist\n", 0);
297 FatRaiseStatus( IrpContext, STATUS_NO_EAS_ON_FILE );
298 }
299
300 //
301 // Try to pin down the Ea set header for the index in the
302 // dirent. If the operation doesn't complete, return FALSE
303 // from this routine.
304 //
305
306 ThisFilename.Buffer = (PCHAR)Buffer;
307 Fat8dot3ToString( IrpContext, Dirent, FALSE, &ThisFilename );
308
309 FatReadEaSet( IrpContext,
310 Vcb,
311 Dirent->ExtendedAttributes,
312 &ThisFilename,
313 FALSE,
314 &EaSetRange );
315
316 EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
317
318 //
319 // We now have the Ea set header for this file. We simply copy
320 // the Need Ea field.
321 //
322
323 *NeedEaCount = EaSetHeader->NeedEaCount;
324
325 } _SEH2_FINALLY {
326
327 DebugUnwind( FatGetNeedEaCount );
328
329 //
330 // Unpin the EaDirent and the EaSetHeader if pinned.
331 //
332
333 FatUnpinBcb( IrpContext, EaBcb );
334
335 FatUnpinEaRange( IrpContext, &EaSetRange );
336
337 //
338 // Release the Fcb for the Ea file if locked.
339 //
340
341 if (LockedEaFcb) {
342
343 FatReleaseFcb( IrpContext, Vcb->EaFcb );
344 }
345
346 DebugTrace(-1, Dbg, "FatGetNeedEaCount: NeedEaCount -> %08lx\n", *NeedEaCount);
347 } _SEH2_END;
348
349 return;
350 }
351
352
_Requires_lock_held_(_Global_critical_region_)353 _Requires_lock_held_(_Global_critical_region_)
354 VOID
355 FatCreateEa (
356 IN PIRP_CONTEXT IrpContext,
357 IN PVCB Vcb,
358 IN PUCHAR Buffer,
359 IN ULONG Length,
360 IN POEM_STRING FileName,
361 OUT PUSHORT EaHandle
362 )
363
364 /*++
365
366 Routine Description:
367
368 This routine adds an entire ea set to the Ea file. The owning file
369 is specified in 'FileName'. This is used to replace the Ea set attached
370 to an existing file during a supersede operation.
371
372 NOTE: This routine may block, it should not be called unless the
373 thread is waitable.
374
375 Arguments:
376
377 Vcb - Supplies the Vcb for the volume.
378
379 Buffer - Buffer with the Ea list to add.
380
381 Length - Length of the buffer.
382
383 FileName - The Ea's will be attached to this file.
384
385 EaHandle - The new ea handle will be assigned to this address.
386
387 Return Value:
388
389 None
390
391 --*/
392
393 {
394 PBCB EaBcb;
395 BOOLEAN LockedEaFcb;
396
397 PEA_SET_HEADER EaSetHeader = NULL;
398 EA_RANGE EaSetRange;
399
400 PAGED_CODE();
401
402 DebugTrace(+1, Dbg, "FatCreateEa...\n", 0);
403
404 EaBcb = NULL;
405 LockedEaFcb = FALSE;
406
407 RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
408
409 //
410 // Use 'try' to facilitate cleanup.
411 //
412
413 _SEH2_TRY {
414
415 PDIRENT EaDirent;
416
417 ULONG PackedEasLength;
418 ULONG AllocationLength;
419 ULONG BytesPerCluster;
420
421 PFILE_FULL_EA_INFORMATION FullEa;
422
423 //
424 // We will allocate a buffer and copy the Ea list from the user's
425 // buffer to a FAT packed Ea list. Initial allocation is one
426 // cluster, our starting offset into the packed Ea list is 0.
427 //
428
429 PackedEasLength = 0;
430
431 BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
432
433 AllocationLength = (PackedEasLength
434 + SIZE_OF_EA_SET_HEADER
435 + BytesPerCluster - 1)
436 & ~(BytesPerCluster - 1);
437
438 //
439 // Allocate the memory and store the file name into it.
440 //
441
442 EaSetHeader = FsRtlAllocatePoolWithTag( PagedPool,
443 AllocationLength,
444 TAG_EA_SET_HEADER );
445
446 RtlZeroMemory( EaSetHeader, AllocationLength );
447
448 RtlCopyMemory( EaSetHeader->OwnerFileName,
449 FileName->Buffer,
450 FileName->Length );
451
452 AllocationLength -= SIZE_OF_EA_SET_HEADER;
453
454 //
455 // Loop through the user's Ea list. Catch any error for invalid
456 // name or non-existent Ea value.
457 //
458
459 for ( FullEa = (PFILE_FULL_EA_INFORMATION) Buffer;
460 FullEa < (PFILE_FULL_EA_INFORMATION) &Buffer[Length];
461 FullEa = (PFILE_FULL_EA_INFORMATION) (FullEa->NextEntryOffset == 0 ?
462 &Buffer[Length] :
463 (PUCHAR) FullEa + FullEa->NextEntryOffset)) {
464
465 OEM_STRING EaName;
466 ULONG EaOffset;
467
468 EaName.Length = FullEa->EaNameLength;
469 EaName.Buffer = &FullEa->EaName[0];
470
471 //
472 // Make sure the ea name is valid
473 //
474
475 if (!FatIsEaNameValid( IrpContext, EaName )) {
476
477 DebugTrace(0, Dbg,
478 "FatCreateEa: Invalid Ea Name -> %Z\n",
479 EaName);
480
481 IrpContext->OriginatingIrp->IoStatus.Information = (PUCHAR)FullEa - Buffer;
482 IrpContext->OriginatingIrp->IoStatus.Status = STATUS_INVALID_EA_NAME;
483 FatRaiseStatus( IrpContext, STATUS_INVALID_EA_NAME );
484 }
485
486 //
487 // Check that no invalid ea flags are set.
488 //
489
490 //
491 // TEMPCODE We are returning STATUS_INVALID_EA_NAME
492 // until a more appropriate error code exists.
493 //
494
495 if (FullEa->Flags != 0
496 && FullEa->Flags != FILE_NEED_EA) {
497
498 IrpContext->OriginatingIrp->IoStatus.Information = (PUCHAR)FullEa - Buffer;
499 IrpContext->OriginatingIrp->IoStatus.Status = STATUS_INVALID_EA_NAME;
500 FatRaiseStatus( IrpContext, STATUS_INVALID_EA_NAME );
501 }
502
503 //
504 // If this is a duplicate name then delete the current ea
505 // value.
506 //
507
508 if (FatLocateEaByName( IrpContext,
509 (PPACKED_EA) EaSetHeader->PackedEas,
510 PackedEasLength,
511 &EaName,
512 &EaOffset )) {
513
514 DebugTrace(0, Dbg, "FatCreateEa: Duplicate name found\n", 0);
515
516 FatDeletePackedEa( IrpContext,
517 EaSetHeader,
518 &PackedEasLength,
519 EaOffset );
520 }
521
522 //
523 // We ignore this value if the eavalue length is zero.
524 //
525
526 if (FullEa->EaValueLength == 0) {
527
528 DebugTrace(0, Dbg,
529 "FatCreateEa: Empty ea\n",
530 0);
531
532 continue;
533 }
534
535 FatAppendPackedEa( IrpContext,
536 &EaSetHeader,
537 &PackedEasLength,
538 &AllocationLength,
539 FullEa,
540 BytesPerCluster );
541 }
542
543 //
544 // If the resulting length isn't zero, then allocate a FAT cluster
545 // to store the data.
546 //
547
548 if (PackedEasLength != 0) {
549
550 PEA_SET_HEADER NewEaSetHeader;
551
552 //
553 // If the packed eas length (plus 4 bytes) is greater
554 // than the maximum allowed ea size, we return an error.
555 //
556
557 if (PackedEasLength + 4 > MAXIMUM_EA_SIZE) {
558
559 DebugTrace( 0, Dbg, "Ea length is greater than maximum\n", 0 );
560
561 FatRaiseStatus( IrpContext, STATUS_EA_TOO_LARGE );
562 }
563
564 //
565 // Get the Ea file.
566 //
567
568 FatGetEaFile( IrpContext,
569 Vcb,
570 &EaDirent,
571 &EaBcb,
572 TRUE,
573 TRUE );
574
575 LockedEaFcb = TRUE;
576
577 FatAddEaSet( IrpContext,
578 Vcb,
579 PackedEasLength + SIZE_OF_EA_SET_HEADER,
580 EaBcb,
581 EaDirent,
582 EaHandle,
583 &EaSetRange );
584
585 NewEaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
586
587 //
588 // Store the length of the new Ea's into the NewEaSetHeader.
589 // This is the PackedEasLength + 4.
590 //
591
592 PackedEasLength += 4;
593
594 CopyU4char( EaSetHeader->cbList, &PackedEasLength );
595
596 //
597 // Copy all but the first four bytes of EaSetHeader into
598 // the new ea. The signature and index fields have
599 // already been filled in.
600 //
601
602 RtlCopyMemory( &NewEaSetHeader->NeedEaCount,
603 &EaSetHeader->NeedEaCount,
604 PackedEasLength + SIZE_OF_EA_SET_HEADER - 8 );
605
606 FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange );
607 FatUnpinEaRange( IrpContext, &EaSetRange );
608
609 CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
610
611 //
612 // There was no data added to the Ea file. Return a handle
613 // of 0.
614 //
615
616 } else {
617
618 *EaHandle = 0;
619 }
620
621 } _SEH2_FINALLY {
622
623 DebugUnwind( FatCreateEa );
624
625 //
626 // Deallocate the EaSetHeader if present.
627 //
628
629 if (EaSetHeader) {
630
631 ExFreePool( EaSetHeader );
632 }
633
634 //
635 // Release the EaFcb if held.
636 //
637
638 if (LockedEaFcb) {
639
640 FatReleaseFcb( IrpContext, Vcb->EaFcb );
641 }
642
643 //
644 // Unpin the dirents for the EaFcb and EaSetFcb if necessary.
645 //
646
647 FatUnpinBcb( IrpContext, EaBcb );
648 FatUnpinEaRange( IrpContext, &EaSetRange );
649
650 DebugTrace(-1, Dbg, "FatCreateEa -> Exit\n", 0);
651 } _SEH2_END;
652
653 return;
654 }
655
_Requires_lock_held_(_Global_critical_region_)656 _Requires_lock_held_(_Global_critical_region_)
657 VOID
658 FatDeleteEa (
659 IN PIRP_CONTEXT IrpContext,
660 IN PVCB Vcb,
661 IN USHORT EaHandle,
662 IN POEM_STRING FileName
663 )
664
665 /*++
666
667 Routine Description:
668
669 This routine is called to remove an entire ea set. Most of the work
670 is done in the call to 'FatDeleteEaSet'. This routine opens the
671 Ea file and then calls the support routine.
672
673 NOTE: This routine may block, it should not be called unless the
674 thread is waitable.
675
676 Arguments:
677
678 Vcb - Vcb for the volume
679
680 EaHandle - The handle for the Ea's to remove. This handle will be
681 verified during this operation.
682
683 FileName - The name of the file whose Ea's are being removed. This
684 name is compared against the Ea owner's name in the Ea set.
685
686 Return Value:
687
688 None.
689
690 --*/
691
692 {
693 PBCB EaBcb;
694 BOOLEAN LockedEaFcb;
695
696 PAGED_CODE();
697
698 DebugTrace(+1, Dbg, "FatDeleteEa...\n", 0);
699
700 //
701 // Initialize local values.
702 //
703
704 EaBcb = NULL;
705 LockedEaFcb = FALSE;
706
707 //
708 // Use a try statement to facilitate cleanup.
709 //
710
711 _SEH2_TRY {
712
713 PDIRENT EaDirent;
714
715 //
716 // Get the Ea stream file. If the file doesn't exist on the disk
717 // then the disk has been corrupted.
718 //
719
720 FatGetEaFile( IrpContext,
721 Vcb,
722 &EaDirent,
723 &EaBcb,
724 FALSE,
725 TRUE );
726
727 LockedEaFcb = TRUE;
728
729 //
730 // If we didn't get the Ea file, then the disk is corrupt.
731 //
732
733 if ( EaBcb == NULL ) {
734
735
736 DebugTrace(0, Dbg,
737 "FatDeleteEa: No Ea file exists\n",
738 0);
739
740 FatRaiseStatus( IrpContext, STATUS_NO_EAS_ON_FILE );
741 }
742
743 //
744 // We now have everything we need to delete the ea set. Call the
745 // support routine to do this.
746 //
747
748 FatDeleteEaSet( IrpContext,
749 Vcb,
750 EaBcb,
751 EaDirent,
752 EaHandle,
753 FileName );
754
755 CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
756
757 } _SEH2_FINALLY {
758
759 DebugUnwind( FatDeleteEa );
760
761 //
762 // Release the EaFcb if held.
763 //
764
765 if (LockedEaFcb) {
766
767 FatReleaseFcb( IrpContext, Vcb->EaFcb );
768 }
769
770 //
771 // Unpin the dirent for the Ea file if pinned.
772 //
773
774 FatUnpinBcb( IrpContext, EaBcb );
775
776 DebugTrace(-1, Dbg, "FatDeleteEa -> Exit\n", 0);
777 } _SEH2_END;
778
779 return;
780 }
781
782
_Requires_lock_held_(_Global_critical_region_)783 _Requires_lock_held_(_Global_critical_region_)
784 VOID
785 FatGetEaFile (
786 IN PIRP_CONTEXT IrpContext,
787 IN OUT PVCB Vcb,
788 OUT PDIRENT *EaDirent,
789 OUT PBCB *EaBcb,
790 IN BOOLEAN CreateFile,
791 IN BOOLEAN ExclusiveFcb
792 )
793
794 /*++
795
796 Routine Description:
797
798 This routine is used to completely initialize the Vcb and
799 the Ea file for the Vcb.
800
801 If the Vcb doesn't have the Ea file object, then we first try to
802 lookup the Ea data file in the root directory and if that fails
803 we try to create the file. The 'CreateFile' flag is used to check
804 whether it is necessary to create the Ea file.
805
806 This routine will lock down the Fcb for exclusive or shared access before
807 performing any operations. If the operation does not complete due
808 to blocking, exclusive or shared access will be given up before returning.
809
810 If we are creating the Ea file and marking sections of it dirty,
811 we can't use the repin feature through the cache map. In that case
812 we use a local IrpContext and then unpin all of the Bcb's before
813 continuing.
814
815 Note: If this routine will be creating the Ea file, we are guaranteed
816 to be waitable.
817
818 Arguments:
819
820 Vcb - Vcb for the volume
821
822 EaDirent - Location to store the address of the pinned dirent for the
823 Ea file.
824
825 EaBcb - Location to store the address of the Bcb for the pinned dirent.
826
827 CreateFile - Boolean indicating whether we should create the Ea file
828 on the disk.
829
830 ExclusiveFcb - Indicates whether shared or exclusive access is desired
831 for the EaFcb.
832
833 Return Value:
834
835 None.
836
837 --*/
838
839 {
840 PFILE_OBJECT EaStreamFile = NULL;
841 EA_RANGE EaFileRange;
842
843 BOOLEAN UnwindLockedEaFcb = FALSE;
844 BOOLEAN UnwindLockedRootDcb = FALSE;
845 BOOLEAN UnwindAllocatedDiskSpace = FALSE;
846 BOOLEAN UnwindEaDirentCreated = FALSE;
847 BOOLEAN UnwindUpdatedSizes = FALSE;
848
849 PAGED_CODE();
850
851 DebugTrace(+1, Dbg, "FatGetEaFile ...\n", 0);
852
853 RtlZeroMemory( &EaFileRange, sizeof( EA_RANGE ));
854
855 //
856 // Use a try to facilitate cleanup
857 //
858
859 _SEH2_TRY {
860
861 OEM_STRING EaFileName;
862 LARGE_INTEGER SectionSize;
863
864 //
865 // Check if the Vcb already has the file object. If it doesn't, then
866 // we need to search the root directory for the Ea data file.
867 //
868
869 if (Vcb->VirtualEaFile == NULL) {
870
871 //
872 // Always lock the Ea file exclusively if we have to create the file.
873 //
874
875 if ( !FatAcquireExclusiveFcb( IrpContext, Vcb->EaFcb )) {
876
877 DebugTrace(0, Dbg, "FatGetEaFile: Can't grab exclusive\n", 0);
878 FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
879 }
880
881 UnwindLockedEaFcb = TRUE;
882
883 //
884 // Otherwise we acquire the Fcb as the caller requested.
885 //
886
887 } else {
888
889 if ((ExclusiveFcb && !FatAcquireExclusiveFcb( IrpContext, Vcb->EaFcb ))
890 || (!ExclusiveFcb && !FatAcquireSharedFcb( IrpContext, Vcb->EaFcb))) {
891
892 DebugTrace(0, Dbg, "FatGetEaFile: Can't grab EaFcb\n", 0);
893
894 FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
895 }
896
897 UnwindLockedEaFcb = TRUE;
898
899 //
900 // If the file now does not exist we need to release the Fcb and
901 // reacquire exclusive if we acquired shared.
902 //
903
904 if ((Vcb->VirtualEaFile == NULL) && !ExclusiveFcb) {
905
906 FatReleaseFcb( IrpContext, Vcb->EaFcb );
907 UnwindLockedEaFcb = FALSE;
908
909 if (!FatAcquireExclusiveFcb( IrpContext, Vcb->EaFcb )) {
910
911 DebugTrace(0, Dbg, "FatGetEaFile: Can't grab EaFcb\n", 0);
912
913 FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
914 }
915
916 UnwindLockedEaFcb = TRUE;
917 }
918 }
919
920 //
921 // If the file object is now there we only need to get the
922 // dirent for the Ea file.
923 //
924
925 if (Vcb->VirtualEaFile != NULL) {
926
927 FatVerifyFcb( IrpContext, Vcb->EaFcb );
928
929 FatGetDirentFromFcbOrDcb( IrpContext,
930 Vcb->EaFcb,
931 FALSE,
932 EaDirent,
933 EaBcb );
934
935 try_return( NOTHING );
936
937 } else {
938
939 VBO ByteOffset = 0;
940
941 //
942 // Always mark the ea fcb as good.
943 //
944
945 Vcb->EaFcb->FcbCondition = FcbGood;
946
947 //
948 // We try to lookup the dirent for the Ea Fcb.
949 //
950
951 EaFileName.Buffer = "EA DATA. SF";
952 EaFileName.Length = 11;
953 EaFileName.MaximumLength = 12;
954
955 //
956 // Now pick up the root directory to be synchronized with
957 // deletion/creation of entries. If we may create the file,
958 // get it exclusive right now.
959 //
960 // Again, note how we are relying on bottom-up lockorder. We
961 // already got the EaFcb.
962 //
963
964 if (CreateFile) {
965 ExAcquireResourceExclusiveLite( Vcb->RootDcb->Header.Resource, TRUE );
966 } else {
967 ExAcquireResourceSharedLite( Vcb->RootDcb->Header.Resource, TRUE );
968 }
969 UnwindLockedRootDcb = TRUE;
970
971 FatLocateSimpleOemDirent( IrpContext,
972 Vcb->EaFcb->ParentDcb,
973 &EaFileName,
974 EaDirent,
975 EaBcb,
976 &ByteOffset );
977
978 //
979 // If the file exists, we need to create the virtual file
980 // object for it.
981 //
982
983 if (*EaDirent != NULL) {
984
985 //
986 // Since we may be modifying the dirent, pin the data now.
987 //
988
989 FatPinMappedData( IrpContext,
990 Vcb->EaFcb->ParentDcb,
991 ByteOffset,
992 sizeof(DIRENT),
993 EaBcb );
994
995 //
996 // Update the Fcb with information on the file size
997 // and disk location. Also increment the open/unclean
998 // counts in the EaFcb and the open count in the
999 // Vcb.
1000 //
1001
1002 Vcb->EaFcb->FirstClusterOfFile = (*EaDirent)->FirstClusterOfFile;
1003 Vcb->EaFcb->DirentOffsetWithinDirectory = ByteOffset;
1004
1005 //
1006 // Find the allocation size. The purpose here is
1007 // really to completely fill in the Mcb for the
1008 // file.
1009 //
1010
1011 Vcb->EaFcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT;
1012
1013 FatLookupFileAllocationSize( IrpContext, Vcb->EaFcb );
1014
1015 //
1016 // Start by computing the section size for the cache
1017 // manager.
1018 //
1019
1020 SectionSize.QuadPart = (*EaDirent)->FileSize;
1021 Vcb->EaFcb->Header.AllocationSize = SectionSize;
1022 Vcb->EaFcb->Header.FileSize = SectionSize;
1023
1024 //
1025 // Create and initialize the file object for the
1026 // Ea virtual file.
1027 //
1028
1029 EaStreamFile = FatOpenEaFile( IrpContext, Vcb->EaFcb );
1030
1031 Vcb->VirtualEaFile = EaStreamFile;
1032
1033 //
1034 // Else there was no dirent. If we were instructed to
1035 // create the file object, we will try to create the dirent,
1036 // allocate disk space, initialize the Ea file header and
1037 // return this information to the user.
1038 //
1039
1040 } else if (CreateFile) {
1041
1042 ULONG BytesPerCluster;
1043 ULONG OffsetTableSize;
1044 ULONG AllocationSize;
1045 PEA_FILE_HEADER FileHeader;
1046 USHORT AllocatedClusters;
1047 PUSHORT CurrentIndex;
1048 ULONG Index;
1049 NTSTATUS Status = STATUS_SUCCESS;
1050
1051 DebugTrace(0, Dbg, "FatGetEaFile: Creating local IrpContext\n", 0);
1052
1053 BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
1054
1055 AllocationSize = (((ULONG) sizeof( EA_FILE_HEADER ) << 1) + BytesPerCluster - 1)
1056 & ~(BytesPerCluster - 1);
1057
1058 AllocatedClusters = (USHORT) (AllocationSize
1059 >> Vcb->AllocationSupport.LogOfBytesPerCluster);
1060
1061 OffsetTableSize = AllocationSize - sizeof( EA_FILE_HEADER );
1062
1063 //
1064 // Allocate disk space, the space allocated is 1024 bytes
1065 // rounded up to the nearest cluster size.
1066 //
1067
1068 FatAllocateDiskSpace( IrpContext,
1069 Vcb,
1070 0,
1071 &AllocationSize,
1072 FALSE,
1073 &Vcb->EaFcb->Mcb );
1074
1075 UnwindAllocatedDiskSpace = TRUE;
1076
1077 //
1078 // Allocate and initialize a dirent in the root directory
1079 // to describe this new file.
1080 //
1081
1082 Vcb->EaFcb->DirentOffsetWithinDirectory =
1083 FatCreateNewDirent( IrpContext,
1084 Vcb->EaFcb->ParentDcb,
1085 1,
1086 FALSE );
1087
1088 FatPrepareWriteDirectoryFile( IrpContext,
1089 Vcb->EaFcb->ParentDcb,
1090 Vcb->EaFcb->DirentOffsetWithinDirectory,
1091 sizeof(DIRENT),
1092 EaBcb,
1093 #ifndef __REACTOS__
1094 EaDirent,
1095 #else
1096 (PVOID *)EaDirent,
1097 #endif
1098 FALSE,
1099 TRUE,
1100 &Status );
1101
1102 NT_ASSERT( NT_SUCCESS( Status ));
1103
1104 UnwindEaDirentCreated = TRUE;
1105
1106 FatConstructDirent( IrpContext,
1107 *EaDirent,
1108 &EaFileName,
1109 FALSE,
1110 FALSE,
1111 NULL,
1112 FAT_DIRENT_ATTR_READ_ONLY
1113 | FAT_DIRENT_ATTR_HIDDEN
1114 | FAT_DIRENT_ATTR_SYSTEM
1115 | FAT_DIRENT_ATTR_ARCHIVE,
1116 TRUE,
1117 NULL );
1118
1119 (*EaDirent)->FileSize = AllocationSize;
1120
1121 //
1122 // Initialize the Fcb for this file and initialize the
1123 // cache map as well.
1124 //
1125
1126 //
1127 // Start by computing the section size for the cache
1128 // manager.
1129 //
1130
1131 SectionSize.QuadPart = (*EaDirent)->FileSize;
1132 Vcb->EaFcb->Header.AllocationSize = SectionSize;
1133 Vcb->EaFcb->Header.FileSize = SectionSize;
1134 UnwindUpdatedSizes = TRUE;
1135
1136 //
1137 // Create and initialize the file object for the
1138 // Ea virtual file.
1139 //
1140
1141 EaStreamFile = FatOpenEaFile( IrpContext, Vcb->EaFcb );
1142
1143 //
1144 // Update the Fcb with information on the file size
1145 // and disk location. Also increment the open/unclean
1146 // counts in the EaFcb and the open count in the
1147 // Vcb.
1148 //
1149
1150 {
1151 LBO FirstLboOfFile;
1152
1153 FatLookupMcbEntry( Vcb, &Vcb->EaFcb->Mcb,
1154 0,
1155 &FirstLboOfFile,
1156 NULL,
1157 NULL );
1158
1159 //
1160 // The discerning reader will note that this doesn't take
1161 // FAT32 into account, which is of course intentional.
1162 //
1163
1164 (*EaDirent)->FirstClusterOfFile =
1165 (USHORT) FatGetIndexFromLbo( Vcb, FirstLboOfFile );
1166 }
1167
1168 Vcb->EaFcb->FirstClusterOfFile = (*EaDirent)->FirstClusterOfFile;
1169
1170 //
1171 // Initialize the Ea file header and mark the Bcb as dirty.
1172 //
1173
1174 FatPinEaRange( IrpContext,
1175 EaStreamFile,
1176 Vcb->EaFcb,
1177 &EaFileRange,
1178 0,
1179 AllocationSize,
1180 STATUS_DATA_ERROR );
1181
1182 FileHeader = (PEA_FILE_HEADER) EaFileRange.Data;
1183
1184 RtlZeroMemory( FileHeader, AllocationSize );
1185 FileHeader->Signature = EA_FILE_SIGNATURE;
1186
1187 for (Index = MAX_EA_BASE_INDEX, CurrentIndex = FileHeader->EaBaseTable;
1188 Index;
1189 Index--, CurrentIndex++) {
1190
1191 *CurrentIndex = AllocatedClusters;
1192 }
1193
1194 //
1195 // Initialize the offset table with the offset set to
1196 // after the just allocated clusters.
1197 //
1198
1199 for (Index = OffsetTableSize >> 1,
1200 CurrentIndex = (PUSHORT) ((PUCHAR) FileHeader + sizeof( EA_FILE_HEADER ));
1201 Index;
1202 Index--, CurrentIndex++) {
1203
1204 *CurrentIndex = UNUSED_EA_HANDLE;
1205 }
1206
1207 //
1208 // Unpin the file header and offset table.
1209 //
1210
1211 FatMarkEaRangeDirty( IrpContext, EaStreamFile, &EaFileRange );
1212 FatUnpinEaRange( IrpContext, &EaFileRange );
1213
1214 CcFlushCache( EaStreamFile->SectionObjectPointer, NULL, 0, NULL );
1215
1216 //
1217 // Return the Ea file object to the user.
1218 //
1219
1220 Vcb->VirtualEaFile = EaStreamFile;
1221 }
1222 }
1223 try_exit: NOTHING;
1224 } _SEH2_FINALLY {
1225
1226 DebugUnwind( FatGetEaFile );
1227
1228 //
1229 // If this is abnormal termination and disk space has been
1230 // allocated. We deallocate it now.
1231 //
1232
1233 if (_SEH2_AbnormalTermination()) {
1234
1235 //
1236 // Deallocate the Ea file
1237 //
1238
1239 if (UnwindAllocatedDiskSpace) {
1240
1241 FatDeallocateDiskSpace( IrpContext,
1242 Vcb,
1243 &Vcb->EaFcb->Mcb,
1244 FALSE );
1245 }
1246
1247 //
1248 // Delete the dirent for the Ea file, if created.
1249 //
1250
1251 if (UnwindEaDirentCreated) {
1252
1253 if (UnwindUpdatedSizes) {
1254
1255 Vcb->EaFcb->Header.AllocationSize.QuadPart = 0;
1256 Vcb->EaFcb->Header.FileSize.QuadPart = 0;
1257 }
1258
1259 FatUnpinBcb( IrpContext, *EaBcb );
1260 FatDeleteDirent( IrpContext, Vcb->EaFcb, NULL, TRUE );
1261 }
1262
1263 //
1264 // Release the EA Fcb if held
1265 //
1266
1267 if (UnwindLockedEaFcb) {
1268
1269 FatReleaseFcb( IrpContext, Vcb->EaFcb );
1270 }
1271
1272 //
1273 // Dereference the Ea stream file if created.
1274 //
1275
1276 if (EaStreamFile != NULL) {
1277
1278 ObDereferenceObject( EaStreamFile );
1279 }
1280 }
1281
1282 //
1283 // Always release the root Dcb (our caller releases the EA Fcb if we
1284 // do not raise).
1285 //
1286
1287 if (UnwindLockedRootDcb) {
1288
1289 FatReleaseFcb( IrpContext, Vcb->RootDcb );
1290 }
1291
1292 //
1293 // If the Ea file header is locked down. We unpin it now.
1294 //
1295
1296 FatUnpinEaRange( IrpContext, &EaFileRange );
1297
1298 DebugTrace(-1, Dbg, "FatGetEaFile: Exit\n", 0);
1299 } _SEH2_END;
1300
1301 return;
1302 }
1303
1304
1305 VOID
FatReadEaSet(IN PIRP_CONTEXT IrpContext,IN PVCB Vcb,IN USHORT EaHandle,IN POEM_STRING FileName,IN BOOLEAN ReturnEntireSet,OUT PEA_RANGE EaSetRange)1306 FatReadEaSet (
1307 IN PIRP_CONTEXT IrpContext,
1308 IN PVCB Vcb,
1309 IN USHORT EaHandle,
1310 IN POEM_STRING FileName,
1311 IN BOOLEAN ReturnEntireSet,
1312 OUT PEA_RANGE EaSetRange
1313 )
1314
1315 /*++
1316
1317 Routine Description:
1318
1319 This routine pins the Ea set for the given ea handle within the
1320 Ea stream file. The EaHandle, after first comparing against valid
1321 index values, is used to compute the cluster offset for this
1322 this Ea set. The Ea set is then verified as belonging to this
1323 index and lying within the Ea data file.
1324
1325 The caller of this function will have verified that the Ea file
1326 exists and that the Vcb field points to an initialized cache file.
1327 The caller will already have gained exclusive access to the
1328 EaFcb.
1329
1330 Arguments:
1331
1332 Vcb - Supplies the Vcb for the volume.
1333
1334 EaHandle - Supplies the handle for the Ea's to read.
1335
1336 FileName - Name of the file whose Ea's are being read.
1337
1338 ReturnEntireSet - Indicates if the caller needs the entire set
1339 as opposed to just the header.
1340
1341 EaSetRange - Pointer to the EaRange structure which will describe the Ea
1342 on return.
1343
1344 Return Value:
1345
1346 None
1347
1348 --*/
1349
1350 {
1351 ULONG BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
1352
1353 ULONG EaOffsetVbo;
1354 EA_RANGE EaOffsetRange;
1355 USHORT EaOffsetCluster;
1356
1357 EA_RANGE EaHeaderRange;
1358 PEA_FILE_HEADER EaHeader;
1359
1360 ULONG EaSetVbo;
1361 PEA_SET_HEADER EaSet;
1362
1363 ULONG CbList;
1364
1365 PAGED_CODE();
1366
1367 UNREFERENCED_PARAMETER( FileName );
1368
1369 DebugTrace(+1, Dbg, "FatReadEaSet\n", 0);
1370 DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb);
1371
1372 //
1373 // Verify that the Ea index has a legal value. Raise status
1374 // STATUS_NONEXISTENT_EA_ENTRY if illegal.
1375 //
1376
1377 if (EaHandle < MIN_EA_HANDLE
1378 || EaHandle > MAX_EA_HANDLE) {
1379
1380 DebugTrace(-1, Dbg, "FatReadEaSet: Illegal handle value\n", 0);
1381 FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY );
1382 }
1383
1384 //
1385 // Verify that the virtual Ea file is large enough for us to read
1386 // the EaOffet table for this index.
1387 //
1388
1389 EaOffsetVbo = sizeof( EA_FILE_HEADER ) + (((ULONGLONG)EaHandle >> 7) << 8);
1390
1391 //
1392 // Zero the Ea range structures.
1393 //
1394
1395 RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE ));
1396 RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE ));
1397
1398 //
1399 // Use a try statement to clean up on exit.
1400 //
1401
1402 _SEH2_TRY {
1403
1404 //
1405 // Pin down the EA file header.
1406 //
1407
1408 FatPinEaRange( IrpContext,
1409 Vcb->VirtualEaFile,
1410 Vcb->EaFcb,
1411 &EaHeaderRange,
1412 0,
1413 sizeof( EA_FILE_HEADER ),
1414 STATUS_NONEXISTENT_EA_ENTRY );
1415
1416 EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data;
1417
1418 //
1419 // Pin down the Ea offset table for the particular index.
1420 //
1421
1422 FatPinEaRange( IrpContext,
1423 Vcb->VirtualEaFile,
1424 Vcb->EaFcb,
1425 &EaOffsetRange,
1426 EaOffsetVbo,
1427 sizeof( EA_OFF_TABLE ),
1428 STATUS_NONEXISTENT_EA_ENTRY );
1429
1430 //
1431 // Check if the specifific handle is currently being used.
1432 //
1433
1434 EaOffsetCluster = *((PUSHORT) EaOffsetRange.Data
1435 + (EaHandle & (MAX_EA_OFFSET_INDEX - 1)));
1436
1437 if (EaOffsetCluster == UNUSED_EA_HANDLE) {
1438
1439 DebugTrace(0, Dbg, "FatReadEaSet: Ea handle is unused\n", 0);
1440 FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY );
1441 }
1442
1443 //
1444 // Compute the file offset for the Ea data.
1445 //
1446
1447 EaSetVbo = (EaHeader->EaBaseTable[EaHandle >> 7] + EaOffsetCluster)
1448 << Vcb->AllocationSupport.LogOfBytesPerCluster;
1449
1450 //
1451 // Unpin the file header and offset table.
1452 //
1453
1454 FatUnpinEaRange( IrpContext, &EaHeaderRange );
1455 FatUnpinEaRange( IrpContext, &EaOffsetRange );
1456
1457 //
1458 // Pin the ea set.
1459 //
1460
1461 FatPinEaRange( IrpContext,
1462 Vcb->VirtualEaFile,
1463 Vcb->EaFcb,
1464 EaSetRange,
1465 EaSetVbo,
1466 BytesPerCluster,
1467 STATUS_DATA_ERROR );
1468
1469 //
1470 // Verify that the Ea set is valid and belongs to this index.
1471 // Raise STATUS_DATA_ERROR if there is a data conflict.
1472 //
1473
1474 EaSet = (PEA_SET_HEADER) EaSetRange->Data;
1475
1476 if (EaSet->Signature != EA_SET_SIGNATURE
1477 || EaSet->OwnEaHandle != EaHandle ) {
1478
1479 DebugTrace(0, Dbg, "FatReadEaSet: Ea set header is corrupt\n", 0);
1480 FatRaiseStatus( IrpContext, STATUS_DATA_ERROR );
1481 }
1482
1483 //
1484 // At this point we have pinned a single cluster of Ea data. If
1485 // this represents the entire Ea data for the Ea index, we are
1486 // done. Otherwise we need to check on the entire size of
1487 // of the Ea set header and whether it is contained in the allocated
1488 // size of the Ea virtual file. At that point we can unpin
1489 // the partial Ea set header and repin the entire header.
1490 //
1491
1492 CbList = GetcbList( EaSet );
1493
1494 if (ReturnEntireSet
1495 && CbList > BytesPerCluster ) {
1496
1497 //
1498 // Round up to the cluster size.
1499 //
1500
1501 CbList = (CbList + EA_CBLIST_OFFSET + BytesPerCluster - 1)
1502 & ~(BytesPerCluster - 1);
1503
1504 FatUnpinEaRange( IrpContext, EaSetRange );
1505
1506 RtlZeroMemory( EaSetRange, sizeof( EA_RANGE ));
1507
1508 FatPinEaRange( IrpContext,
1509 Vcb->VirtualEaFile,
1510 Vcb->EaFcb,
1511 EaSetRange,
1512 EaSetVbo,
1513 CbList,
1514 STATUS_DATA_ERROR );
1515 }
1516
1517 } _SEH2_FINALLY {
1518
1519 DebugUnwind( FatReadEaSet );
1520
1521 //
1522 // Unpin the Ea base and offset tables if locked down.
1523 //
1524
1525 FatUnpinEaRange( IrpContext, &EaHeaderRange );
1526 FatUnpinEaRange( IrpContext, &EaOffsetRange );
1527
1528 DebugTrace(-1, Dbg, "FatReadEaSet: Exit\n", 0);
1529 } _SEH2_END;
1530
1531 return;
1532 }
1533
1534
_Requires_lock_held_(_Global_critical_region_)1535 _Requires_lock_held_(_Global_critical_region_)
1536 VOID
1537 FatDeleteEaSet (
1538 IN PIRP_CONTEXT IrpContext,
1539 IN PVCB Vcb,
1540 IN PBCB EaBcb,
1541 OUT PDIRENT EaDirent,
1542 IN USHORT EaHandle,
1543 IN POEM_STRING FileName
1544 )
1545
1546 /*++
1547
1548 Routine Description:
1549
1550 This routines clips the Ea set for a particular index out of the
1551 Ea file for a volume. The index is verified as belonging to a valid
1552 handle. The clusters are removed and the Ea stream file along with
1553 the Ea base and offset files are updated.
1554
1555 The caller of this function will have verified that the Ea file
1556 exists and that the Vcb field points to an initialized cache file.
1557 The caller will already have gained exclusive access to the
1558 EaFcb.
1559
1560 Arguments:
1561
1562 Vcb - Supplies the Vcb for the volume.
1563
1564 VirtualEeFile - Pointer to the file object for the virtual Ea file.
1565
1566 EaFcb - Supplies the pointer to the Fcb for the Ea file.
1567
1568 EaBcb - Supplies a pointer to the Bcb for the Ea dirent.
1569
1570 EaDirent - Supplies a pointer to the dirent for the Ea file.
1571
1572 EaHandle - Supplies the handle for the Ea's to read.
1573
1574 FileName - Name of the file whose Ea's are being read.
1575
1576 Return Value:
1577
1578 None.
1579
1580 --*/
1581
1582 {
1583 ULONG BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
1584 ULONG CbList;
1585 LARGE_INTEGER FileOffset;
1586
1587 LARGE_MCB DataMcb;
1588 BOOLEAN UnwindInitializeDataMcb = FALSE;
1589 BOOLEAN UnwindSplitData = FALSE;
1590
1591 LARGE_MCB TailMcb;
1592 BOOLEAN UnwindInitializeTailMcb = FALSE;
1593 BOOLEAN UnwindSplitTail = FALSE;
1594 BOOLEAN UnwindMergeTail = FALSE;
1595
1596 BOOLEAN UnwindModifiedEaHeader = FALSE;
1597 BOOLEAN UnwindCacheValues = FALSE;
1598 ULONG UnwindPrevFileSize = 0;
1599
1600 ULONG EaOffsetVbo;
1601 USHORT EaOffsetIndex;
1602 EA_RANGE EaOffsetRange;
1603 USHORT EaOffsetCluster;
1604
1605 PFILE_OBJECT VirtualEaFile = Vcb->VirtualEaFile;
1606 PFCB EaFcb = Vcb->EaFcb;
1607
1608 EA_RANGE EaHeaderRange;
1609 PEA_FILE_HEADER EaHeader;
1610 USHORT EaHeaderBaseIndex;
1611
1612 ULONG EaSetVbo = 0;
1613 ULONG EaSetLength;
1614 EA_RANGE EaSetRange;
1615 PEA_SET_HEADER EaSet;
1616 USHORT EaSetClusterCount;
1617
1618 PAGED_CODE();
1619
1620 UNREFERENCED_PARAMETER( FileName );
1621
1622 //
1623 // Verify that the Ea index has a legal value. Raise status
1624 // STATUS_INVALID_HANDLE if illegal.
1625 //
1626
1627 if (EaHandle < MIN_EA_HANDLE
1628 || EaHandle > MAX_EA_HANDLE) {
1629
1630 DebugTrace(-1, Dbg, "FatDeleteEaSet: Illegal handle value\n", 0);
1631 FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY );
1632 }
1633
1634 //
1635 // Verify that the virtual Ea file is large enough for us to read
1636 // the EaOffet table for this index.
1637 //
1638
1639 EaOffsetVbo = sizeof( EA_FILE_HEADER ) + (((ULONGLONG)EaHandle >> 7) << 8);
1640
1641 //
1642 // Zero the Ea range structures.
1643 //
1644
1645 RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE ));
1646 RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE ));
1647 RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
1648
1649 //
1650 // Use a try to facilitate cleanup.
1651 //
1652
1653 _SEH2_TRY {
1654
1655 //
1656 // Pin down the EA file header.
1657 //
1658
1659 FatPinEaRange( IrpContext,
1660 VirtualEaFile,
1661 EaFcb,
1662 &EaHeaderRange,
1663 0,
1664 sizeof( EA_FILE_HEADER ),
1665 STATUS_NONEXISTENT_EA_ENTRY );
1666
1667 EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data;
1668
1669 //
1670 // Pin down the Ea offset table for the particular index.
1671 //
1672
1673 FatPinEaRange( IrpContext,
1674 VirtualEaFile,
1675 EaFcb,
1676 &EaOffsetRange,
1677 EaOffsetVbo,
1678 sizeof( EA_OFF_TABLE ),
1679 STATUS_NONEXISTENT_EA_ENTRY );
1680
1681 //
1682 // Check if the specifific handle is currently being used.
1683 //
1684
1685 EaOffsetIndex = EaHandle & (MAX_EA_OFFSET_INDEX - 1);
1686 EaOffsetCluster = *((PUSHORT) EaOffsetRange.Data + EaOffsetIndex);
1687
1688 if (EaOffsetCluster == UNUSED_EA_HANDLE) {
1689
1690 DebugTrace(0, Dbg, "FatReadEaSet: Ea handle is unused\n", 0);
1691 FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY );
1692 }
1693
1694 //
1695 // Compute the file offset for the Ea data.
1696 //
1697
1698 EaHeaderBaseIndex = EaHandle >> 7;
1699 EaSetVbo = (EaHeader->EaBaseTable[EaHeaderBaseIndex] + EaOffsetCluster)
1700 << Vcb->AllocationSupport.LogOfBytesPerCluster;
1701
1702 //
1703 // Unpin the file header and offset table.
1704 //
1705
1706 FatUnpinEaRange( IrpContext, &EaHeaderRange );
1707 FatUnpinEaRange( IrpContext, &EaOffsetRange );
1708
1709 //
1710 // Try to pin the requested Ea set.
1711 //
1712
1713 FatPinEaRange( IrpContext,
1714 VirtualEaFile,
1715 EaFcb,
1716 &EaSetRange,
1717 EaSetVbo,
1718 BytesPerCluster,
1719 STATUS_DATA_ERROR );
1720
1721 EaSet = (PEA_SET_HEADER) EaSetRange.Data;
1722
1723 if (EaSet->Signature != EA_SET_SIGNATURE
1724 || EaSet->OwnEaHandle != EaHandle ) {
1725
1726 DebugTrace(0, Dbg, "FatReadEaSet: Ea set header is corrupt\n", 0);
1727 FatRaiseStatus( IrpContext, STATUS_DATA_ERROR );
1728 }
1729
1730 //
1731 // At this point we have pinned a single cluster of Ea data. If
1732 // this represents the entire Ea data for the Ea index, we know
1733 // the number of clusters to remove. Otherwise we need to check
1734 // on the entire size of the Ea set header and whether it is
1735 // contained in the allocated size of the Ea virtual file. At
1736 // that point we unpin the partial Ea set header and remember the
1737 // starting cluster offset and number of clusters in both cluster
1738 // and Vbo formats.
1739 //
1740 // At that point the following variables have the described
1741 // values.
1742 //
1743 // EaSetVbo - Vbo to start splice at.
1744 // EaSetLength - Number of bytes to splice.
1745 // EaSetClusterCount - Number of clusters to splice.
1746 //
1747
1748 CbList = GetcbList( EaSet );
1749
1750 EaSetClusterCount = (USHORT) ((CbList + EA_CBLIST_OFFSET + BytesPerCluster - 1)
1751 >> Vcb->AllocationSupport.LogOfBytesPerCluster);
1752
1753 EaSetLength = EaSetClusterCount << Vcb->AllocationSupport.LogOfBytesPerCluster;
1754
1755 if (EaSetLength > BytesPerCluster) {
1756
1757 if (EaFcb->Header.FileSize.LowPart - EaSetVbo < EaSetLength) {
1758
1759 DebugTrace(0, Dbg, "FatDeleteEaSet: Full Ea set not contained in file\n", 0);
1760
1761 FatRaiseStatus( IrpContext, STATUS_DATA_ERROR );
1762 }
1763 }
1764
1765 FatUnpinEaRange( IrpContext, &EaSetRange );
1766
1767 //
1768 // Update the cache manager for this file. This is done by
1769 // truncating to the point where the data was spliced and
1770 // reinitializing with the modified size of the file.
1771 //
1772 // NOTE: Even if the all the EA's are removed the Ea file will
1773 // always exist and the header area will never shrink.
1774 //
1775
1776 FileOffset.LowPart = EaSetVbo;
1777 FileOffset.HighPart = 0;
1778
1779 //
1780 // Round the cache map down to a system page boundary.
1781 //
1782
1783 FileOffset.LowPart &= ~(PAGE_SIZE - 1);
1784
1785 //
1786 // Make sure all the data gets out to the disk.
1787 //
1788
1789 {
1790 IO_STATUS_BLOCK Iosb;
1791 ULONG PurgeCount = 5;
1792
1793 while (--PurgeCount) {
1794
1795 Iosb.Status = STATUS_SUCCESS;
1796
1797 CcFlushCache( VirtualEaFile->SectionObjectPointer,
1798 NULL,
1799 0,
1800 &Iosb );
1801
1802 NT_ASSERT( Iosb.Status == STATUS_SUCCESS );
1803
1804 //
1805 // We do not have to worry about a lazy writer firing in parallel
1806 // with our CcFlushCache since we have the EaFcb exclusive. Thus
1807 // we know all data is out.
1808 //
1809
1810 //
1811 // We throw the unwanted pages out of the cache and then
1812 // truncate the Ea File for the new size.
1813 //
1814
1815 if (CcPurgeCacheSection( VirtualEaFile->SectionObjectPointer,
1816 &FileOffset,
1817 0,
1818 FALSE )) {
1819
1820 break;
1821 }
1822 }
1823
1824 if (!PurgeCount) {
1825
1826 FatRaiseStatus( IrpContext, STATUS_UNABLE_TO_DELETE_SECTION );
1827 }
1828 }
1829
1830 FileOffset.LowPart = EaFcb->Header.FileSize.LowPart - EaSetLength;
1831
1832 //
1833 // Perform the splice operation on the FAT chain. This is done
1834 // by splitting the target clusters out and merging the remaining
1835 // clusters around them. We can ignore the return value from
1836 // the merge and splice functions because we are guaranteed
1837 // to be able to block.
1838 //
1839
1840 {
1841 FsRtlInitializeLargeMcb( &DataMcb, PagedPool );
1842
1843 UnwindInitializeDataMcb = TRUE;
1844
1845 FatSplitAllocation( IrpContext,
1846 Vcb,
1847 &EaFcb->Mcb,
1848 EaSetVbo,
1849 &DataMcb );
1850
1851 UnwindSplitData = TRUE;
1852
1853 if (EaSetLength + EaSetVbo != EaFcb->Header.FileSize.LowPart) {
1854
1855 FsRtlInitializeLargeMcb( &TailMcb, PagedPool );
1856
1857 UnwindInitializeTailMcb = TRUE;
1858
1859 FatSplitAllocation( IrpContext,
1860 Vcb,
1861 &DataMcb,
1862 EaSetLength,
1863 &TailMcb );
1864
1865 UnwindSplitTail = TRUE;
1866
1867 FatMergeAllocation( IrpContext,
1868 Vcb,
1869 &EaFcb->Mcb,
1870 &TailMcb );
1871
1872 UnwindMergeTail = TRUE;
1873 }
1874 }
1875
1876 //
1877 // Update the Fcb for the Ea file
1878 //
1879
1880 UnwindPrevFileSize = EaFcb->Header.FileSize.LowPart;
1881
1882 (VOID)ExAcquireResourceExclusiveLite( EaFcb->Header.PagingIoResource,
1883 TRUE );
1884
1885 EaFcb->Header.FileSize.LowPart = EaFcb->Header.FileSize.LowPart - EaSetLength;
1886 EaFcb->Header.AllocationSize = EaFcb->Header.FileSize;
1887
1888
1889 CcSetFileSizes( VirtualEaFile,
1890 (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize );
1891
1892 ExReleaseResourceLite( EaFcb->Header.PagingIoResource );
1893
1894 UnwindCacheValues = TRUE;
1895
1896 EaDirent->FileSize = EaFcb->Header.FileSize.LowPart;
1897
1898 FatSetDirtyBcb( IrpContext, EaBcb, Vcb, TRUE );
1899
1900 //
1901 // Update the Ea base and offset tables. For the Ea base table,
1902 // all subsequent index values must be decremented by the number
1903 // of clusters removed.
1904 //
1905 // For the entries in the relevant Ea offset table, all entries
1906 // after this index must also be decreased by the number of
1907 // clusters removed.
1908 //
1909
1910 //
1911 // Pin down the EA file header.
1912 //
1913
1914 RtlZeroMemory( &EaHeaderRange,
1915 sizeof( EA_RANGE ));
1916
1917 FatPinEaRange( IrpContext,
1918 VirtualEaFile,
1919 EaFcb,
1920 &EaHeaderRange,
1921 0,
1922 sizeof( EA_FILE_HEADER ),
1923 STATUS_NONEXISTENT_EA_ENTRY );
1924
1925 EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data;
1926
1927 //
1928 // Pin down the Ea offset table for the particular index.
1929 //
1930
1931 RtlZeroMemory( &EaOffsetRange,
1932 sizeof( EA_RANGE ));
1933
1934 FatPinEaRange( IrpContext,
1935 VirtualEaFile,
1936 EaFcb,
1937 &EaOffsetRange,
1938 EaOffsetVbo,
1939 sizeof( EA_OFF_TABLE ),
1940 STATUS_NONEXISTENT_EA_ENTRY );
1941
1942 {
1943 ULONG Count;
1944 PUSHORT NextEaIndex;
1945
1946 Count = MAX_EA_BASE_INDEX - EaHeaderBaseIndex - 1;
1947
1948 NextEaIndex = &EaHeader->EaBaseTable[EaHeaderBaseIndex + 1];
1949
1950 while (Count--) {
1951
1952 *(NextEaIndex++) -= EaSetClusterCount;
1953 }
1954
1955 FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaHeaderRange );
1956
1957 Count = MAX_EA_OFFSET_INDEX - EaOffsetIndex - 1;
1958 NextEaIndex = (PUSHORT) EaOffsetRange.Data + EaOffsetIndex;
1959
1960 *(NextEaIndex++) = UNUSED_EA_HANDLE;
1961
1962 while (Count--) {
1963
1964 if (*NextEaIndex != UNUSED_EA_HANDLE) {
1965
1966 *NextEaIndex -= EaSetClusterCount;
1967 }
1968
1969 NextEaIndex++;
1970 }
1971
1972 FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaOffsetRange );
1973 }
1974
1975 UnwindModifiedEaHeader = TRUE;
1976
1977 //
1978 // Deallocate the ea set removed
1979 //
1980
1981 FatDeallocateDiskSpace( IrpContext,
1982 Vcb,
1983 &DataMcb,
1984 FALSE );
1985
1986 } _SEH2_FINALLY {
1987
1988 DebugUnwind( FatDeleteEaSet );
1989
1990 //
1991 // Restore file if abnormal termination.
1992 //
1993 // If we have modified the ea file header we ignore this
1994 // error. Otherwise we walk through the state variables.
1995 //
1996
1997 if (_SEH2_AbnormalTermination()
1998 && !UnwindModifiedEaHeader) {
1999
2000 //
2001 // If we modified the Ea dirent or Fcb, recover the previous
2002 // values.
2003 //
2004
2005 if (UnwindPrevFileSize) {
2006
2007 EaFcb->Header.FileSize.LowPart = UnwindPrevFileSize;
2008 EaFcb->Header.AllocationSize.LowPart = UnwindPrevFileSize;
2009 EaDirent->FileSize = UnwindPrevFileSize;
2010
2011 if (UnwindCacheValues) {
2012
2013 CcSetFileSizes( VirtualEaFile,
2014 (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize );
2015 }
2016 }
2017
2018 //
2019 // If we merged the tail with the
2020 // ea file header. We split it out
2021 // again.
2022 //
2023
2024 if (UnwindMergeTail) {
2025
2026 FatSplitAllocation( IrpContext,
2027 Vcb,
2028 &EaFcb->Mcb,
2029 EaSetVbo,
2030 &TailMcb );
2031 }
2032
2033 //
2034 // If we split the tail off we merge the tail back
2035 // with the ea data to remove.
2036 //
2037
2038 if (UnwindSplitTail) {
2039
2040 FatMergeAllocation( IrpContext,
2041 Vcb,
2042 &DataMcb,
2043 &TailMcb );
2044 }
2045
2046 //
2047 // If the ea set has been split out, we merge that
2048 // cluster string back in the file. Otherwise we
2049 // simply uninitialize the local Mcb.
2050 //
2051
2052 if (UnwindSplitData) {
2053
2054 FatMergeAllocation( IrpContext,
2055 Vcb,
2056 &EaFcb->Mcb,
2057 &DataMcb );
2058 }
2059 }
2060
2061 //
2062 // Unpin any Bcb's still active.
2063 //
2064
2065 FatUnpinEaRange( IrpContext, &EaHeaderRange );
2066 FatUnpinEaRange( IrpContext, &EaOffsetRange );
2067 FatUnpinEaRange( IrpContext, &EaSetRange );
2068
2069 //
2070 // Uninitialize any initialized Mcbs
2071 //
2072
2073 if (UnwindInitializeDataMcb) {
2074
2075 FsRtlUninitializeLargeMcb( &DataMcb );
2076 }
2077
2078 if (UnwindInitializeTailMcb) {
2079
2080 FsRtlUninitializeLargeMcb( &TailMcb );
2081 }
2082
2083 DebugTrace(-1, Dbg, "FatDeleteEaSet -> Exit\n", 0);
2084 } _SEH2_END;
2085
2086 return;
2087 }
2088
2089
2090
_Requires_lock_held_(_Global_critical_region_)2091 _Requires_lock_held_(_Global_critical_region_)
2092 VOID
2093 FatAddEaSet (
2094 IN PIRP_CONTEXT IrpContext,
2095 IN PVCB Vcb,
2096 IN ULONG EaSetLength,
2097 IN PBCB EaBcb,
2098 OUT PDIRENT EaDirent,
2099 OUT PUSHORT EaHandle,
2100 OUT PEA_RANGE EaSetRange
2101 )
2102
2103 /*++
2104
2105 Routine Description:
2106
2107 This routine will add the necessary clusters to support a new
2108 Ea set of the given size. This is done by splicing a chain of
2109 clusters into the existing Ea file. An Ea index is assigned to
2110 this new chain and the Ea base and offset tables are updated to
2111 include this new handle. This routine also pins the added
2112 clusters and returns their address and a Bcb.
2113
2114 The caller of this function will have verified that the Ea file
2115 exists and that the Vcb field points to an initialized cache file.
2116 The caller will already have gained exclusive access to the
2117 EaFcb.
2118
2119 Arguments:
2120
2121 Vcb - Supplies the Vcb to fill in.
2122
2123 EaSetLength - The number of bytes needed to contain the Ea set. This
2124 routine will round this up the next cluster size.
2125
2126 EaBcb - Supplies a pointer to the Bcb for the Ea dirent.
2127
2128 EaDirent - Supplies a pointer to the dirent for the Ea file.
2129
2130 EaHandle - Supplies the address to store the ea index generated here.
2131
2132 EaSetRange - This is the structure that describes new range in the Ea file.
2133
2134 Return Value:
2135
2136 None.
2137
2138 --*/
2139
2140 {
2141 ULONG BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
2142
2143 EA_RANGE EaHeaderRange;
2144 USHORT EaHeaderIndex;
2145 PEA_FILE_HEADER EaHeader;
2146
2147 EA_RANGE EaOffsetRange;
2148 ULONG EaNewOffsetVbo = 0;
2149 USHORT EaOffsetIndex;
2150 ULONG EaOffsetTableSize;
2151 PUSHORT EaOffsetTable;
2152
2153 ULONG EaSetClusterOffset;
2154 ULONG EaSetVbo = 0;
2155 USHORT EaSetClusterCount;
2156 PEA_SET_HEADER EaSet;
2157
2158 PFILE_OBJECT VirtualEaFile = Vcb->VirtualEaFile;
2159 PFCB EaFcb = Vcb->EaFcb;
2160
2161 LARGE_MCB EaSetMcb;
2162 BOOLEAN UnwindInitializedEaSetMcb = FALSE;
2163 BOOLEAN UnwindAllocatedNewAllocation = FALSE;
2164 BOOLEAN UnwindMergedNewEaSet = FALSE;
2165
2166 LARGE_MCB EaOffsetMcb;
2167 BOOLEAN UnwindInitializedOffsetMcb = FALSE;
2168 BOOLEAN UnwindSplitNewAllocation = FALSE;
2169 BOOLEAN UnwindMergedNewOffset = FALSE;
2170
2171 LARGE_MCB EaTailMcb;
2172 BOOLEAN UnwindInitializedTailMcb = FALSE;
2173 BOOLEAN UnwindSplitTail = FALSE;
2174 BOOLEAN UnwindMergedTail = FALSE;
2175
2176 LARGE_MCB EaInitialEaMcb;
2177 BOOLEAN UnwindInitializedInitialEaMcb = FALSE;
2178 BOOLEAN UnwindSplitInitialEa = FALSE;
2179 BOOLEAN UnwindMergedInitialEa = FALSE;
2180
2181 USHORT NewEaIndex;
2182 PUSHORT NextEaOffset;
2183
2184 ULONG NewAllocation;
2185 LARGE_INTEGER FileOffset;
2186 ULONG Count;
2187
2188 ULONG UnwindPrevFileSize = 0;
2189 BOOLEAN UnwindCacheValues = FALSE;
2190
2191 BOOLEAN TailExists = FALSE;
2192 BOOLEAN AddedOffsetTableCluster = FALSE;
2193 BOOLEAN UnwindPurgeCacheMap = FALSE;
2194
2195 PAGED_CODE();
2196
2197 DebugTrace(+1, Dbg, "FatAddEaSet\n", 0);
2198 DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb);
2199 DebugTrace( 0, Dbg, " EaSetLength = %ul\n", EaSetLength );
2200
2201 //
2202 // Zero the Ea range structures.
2203 //
2204
2205 RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE ));
2206 RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE ));
2207
2208 //
2209 // Use a try statement to facilitate cleanup.
2210 //
2211
2212 _SEH2_TRY {
2213
2214 //
2215 // Pin down the file header.
2216 //
2217
2218 FatPinEaRange( IrpContext,
2219 VirtualEaFile,
2220 EaFcb,
2221 &EaHeaderRange,
2222 0,
2223 sizeof( EA_FILE_HEADER ),
2224 STATUS_DATA_ERROR );
2225
2226 EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data;
2227
2228 //
2229 // Compute the size of the offset table.
2230 //
2231
2232 EaNewOffsetVbo = EaHeader->EaBaseTable[0] << Vcb->AllocationSupport.LogOfBytesPerCluster;
2233 EaOffsetTableSize = EaNewOffsetVbo - sizeof( EA_FILE_HEADER );
2234
2235 //
2236 // Pin down the entire offset table.
2237 //
2238
2239 FatPinEaRange( IrpContext,
2240 VirtualEaFile,
2241 EaFcb,
2242 &EaOffsetRange,
2243 sizeof( EA_FILE_HEADER ),
2244 EaOffsetTableSize,
2245 STATUS_DATA_ERROR );
2246
2247 //
2248 // We now look for a valid handle out of the existing offset table.
2249 // We start at the last entry and walk backwards. We stop at the
2250 // first unused handle which is preceded by a used handle (or handle
2251 // 1).
2252 //
2253 // As we walk backwards, we need to remember the file offset of the
2254 // cluster which will follow the clusters we add. We initially
2255 // remember the end of the file. If the end of the offset table
2256 // consists of a string of used handles, we remember the offset of
2257 // the handle prior to the transition from used to unused handles.
2258 //
2259
2260 EaSetClusterOffset = EaFcb->Header.FileSize.LowPart
2261 >> Vcb->AllocationSupport.LogOfBytesPerCluster;
2262
2263 NewEaIndex = (USHORT) ((EaOffsetTableSize >> 1) - 1);
2264
2265 NextEaOffset = (PUSHORT) EaOffsetRange.Data + NewEaIndex;
2266
2267 //
2268 // Walk through the used handles at the end of the offset table.
2269 //
2270
2271 if (*NextEaOffset != UNUSED_EA_HANDLE) {
2272
2273 while (NewEaIndex != 0) {
2274
2275 if (*(NextEaOffset - 1) == UNUSED_EA_HANDLE) {
2276
2277 //
2278 // If the handle is 1, we take no action. Otherwise
2279 // we save the cluster offset of the current handle
2280 // knowing we will use a previous handle and insert
2281 // a chain of clusters.
2282 //
2283
2284 if (NewEaIndex != 1) {
2285
2286 EaSetClusterOffset = *NextEaOffset
2287 + EaHeader->EaBaseTable[NewEaIndex >> 7];
2288
2289 TailExists = TRUE;
2290 }
2291
2292 NewEaIndex--;
2293 NextEaOffset--;
2294
2295 break;
2296 }
2297
2298 NewEaIndex--;
2299 NextEaOffset--;
2300 }
2301 }
2302
2303 //
2304 // Walk through looking for the first unused handle in a string
2305 // of unused handles.
2306 //
2307
2308 while (NewEaIndex) {
2309
2310 if (*(NextEaOffset - 1) != UNUSED_EA_HANDLE) {
2311
2312 break;
2313 }
2314
2315 NextEaOffset--;
2316 NewEaIndex--;
2317 }
2318
2319 //
2320 // If the handle is zero, we do a special test to see if handle 1
2321 // is available. Otherwise we will use the first handle of a new
2322 // cluster. A non-zero handle now indicates that a handle was found
2323 // in an existing offset table cluster.
2324 //
2325
2326 if (NewEaIndex == 0) {
2327
2328 if (*(NextEaOffset + 1) == UNUSED_EA_HANDLE) {
2329
2330 NewEaIndex = 1;
2331
2332 } else {
2333
2334 NewEaIndex = (USHORT) EaOffsetTableSize >> 1;
2335 AddedOffsetTableCluster = TRUE;
2336 }
2337 }
2338
2339 //
2340 // If the Ea index is outside the legal range then raise an
2341 // exception.
2342 //
2343
2344 if (NewEaIndex > MAX_EA_HANDLE) {
2345
2346 DebugTrace(-1, Dbg,
2347 "FatAddEaSet: Illegal handle value for new handle\n", 0);
2348
2349 FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
2350 }
2351
2352 //
2353 // Compute the base and offset indexes.
2354 //
2355
2356 EaHeaderIndex = NewEaIndex >> 7;
2357 EaOffsetIndex = NewEaIndex & (MAX_EA_OFFSET_INDEX - 1);
2358
2359 //
2360 // Compute the byte offset of the new ea data in the file.
2361 //
2362
2363 EaSetVbo = EaSetClusterOffset << Vcb->AllocationSupport.LogOfBytesPerCluster;
2364
2365 //
2366 // Allocate all the required disk space together to insure this
2367 // operation is atomic. We don't want to allocate one block
2368 // of disk space and then fail on a second allocation.
2369 //
2370
2371 EaSetLength = (EaSetLength + BytesPerCluster - 1)
2372 & ~(BytesPerCluster - 1);
2373
2374 NewAllocation = EaSetLength
2375 + (AddedOffsetTableCluster ? BytesPerCluster : 0);
2376
2377 //
2378 // Verify that adding these clusters will not grow the Ea file
2379 // beyond its legal value. The maximum number of clusters is
2380 // 2^16 since the Ea sets are referenced by a 16 bit cluster
2381 // offset value.
2382 //
2383
2384 if ((ULONG) ((0x0000FFFF << Vcb->AllocationSupport.LogOfBytesPerCluster)
2385 - EaFcb->Header.FileSize.LowPart)
2386 < NewAllocation) {
2387
2388 DebugTrace(-1, Dbg,
2389 "FatAddEaSet: New Ea file size is too large\n", 0);
2390
2391 FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
2392 }
2393
2394 FsRtlInitializeLargeMcb( &EaSetMcb, PagedPool );
2395
2396 UnwindInitializedEaSetMcb = TRUE;
2397
2398 FatAllocateDiskSpace( IrpContext,
2399 Vcb,
2400 0,
2401 &NewAllocation,
2402 FALSE,
2403 &EaSetMcb );
2404
2405 UnwindAllocatedNewAllocation = TRUE;
2406
2407 EaSetClusterCount = (USHORT) (EaSetLength >> Vcb->AllocationSupport.LogOfBytesPerCluster);
2408
2409 if (AddedOffsetTableCluster) {
2410
2411 FsRtlInitializeLargeMcb( &EaOffsetMcb, PagedPool );
2412
2413 UnwindInitializedOffsetMcb = TRUE;
2414
2415 FatSplitAllocation( IrpContext,
2416 Vcb,
2417 &EaSetMcb,
2418 EaSetLength,
2419 &EaOffsetMcb );
2420
2421 UnwindSplitNewAllocation = TRUE;
2422 }
2423
2424 FatUnpinEaRange( IrpContext, &EaHeaderRange );
2425 FatUnpinEaRange( IrpContext, &EaOffsetRange );
2426
2427 if (AddedOffsetTableCluster) {
2428
2429 FileOffset.LowPart = EaNewOffsetVbo;
2430
2431 } else {
2432
2433 FileOffset.LowPart = EaSetVbo;
2434 }
2435
2436 FileOffset.HighPart = 0;
2437
2438 //
2439 // Round the cache map down to a system page boundary.
2440 //
2441
2442 FileOffset.LowPart &= ~(PAGE_SIZE - 1);
2443
2444 {
2445 IO_STATUS_BLOCK Iosb;
2446 ULONG PurgeCount = 5;
2447
2448 while (--PurgeCount) {
2449
2450 Iosb.Status = STATUS_SUCCESS;
2451
2452 CcFlushCache( VirtualEaFile->SectionObjectPointer,
2453 NULL,
2454 0,
2455 &Iosb );
2456
2457 NT_ASSERT( Iosb.Status == STATUS_SUCCESS );
2458
2459 //
2460 // We do not have to worry about a lazy writer firing in parallel
2461 // with our CcFlushCache since we have the EaFcb exclusive. Thus
2462 // we know all data is out.
2463 //
2464
2465 //
2466 // We throw the unwanted pages out of the cache and then
2467 // truncate the Ea File for the new size.
2468 //
2469 //
2470
2471 if (CcPurgeCacheSection( VirtualEaFile->SectionObjectPointer,
2472 &FileOffset,
2473 0,
2474 FALSE )) {
2475
2476 break;
2477 }
2478 }
2479
2480 if (!PurgeCount) {
2481
2482 FatRaiseStatus( IrpContext, STATUS_UNABLE_TO_DELETE_SECTION );
2483 }
2484 }
2485
2486 UnwindPurgeCacheMap = TRUE;
2487
2488 FileOffset.LowPart = EaFcb->Header.FileSize.LowPart + NewAllocation;
2489
2490 //
2491 // If there is a tail to the file, then we initialize an Mcb
2492 // for the file section and split the tail from the file.
2493 //
2494
2495 if (TailExists) {
2496
2497 FsRtlInitializeLargeMcb( &EaTailMcb, PagedPool );
2498
2499 UnwindInitializedTailMcb = TRUE;
2500
2501 FatSplitAllocation( IrpContext,
2502 Vcb,
2503 &EaFcb->Mcb,
2504 EaSetVbo,
2505 &EaTailMcb );
2506
2507 UnwindSplitTail = TRUE;
2508 }
2509
2510 //
2511 // If there is an initial section of ea data, we initialize an
2512 // Mcb for that section.
2513 //
2514
2515 if (AddedOffsetTableCluster
2516 && EaSetVbo != EaNewOffsetVbo) {
2517
2518 FsRtlInitializeLargeMcb( &EaInitialEaMcb, PagedPool );
2519
2520 UnwindInitializedInitialEaMcb = TRUE;
2521
2522 FatSplitAllocation( IrpContext,
2523 Vcb,
2524 &EaFcb->Mcb,
2525 EaNewOffsetVbo,
2526 &EaInitialEaMcb );
2527
2528 UnwindSplitInitialEa = TRUE;
2529 }
2530
2531 //
2532 // We have now split the new file allocation into the new
2533 // ea set and possibly a new offset table.
2534 //
2535 // We have also split the existing file data into a file
2536 // header, an initial section of ea data and the tail of the
2537 // file. These last 2 may not exist.
2538 //
2539 // Each section is described by an Mcb.
2540 //
2541
2542 //
2543 // Merge the new offset information if it exists.
2544 //
2545
2546 if (AddedOffsetTableCluster) {
2547
2548 FatMergeAllocation( IrpContext,
2549 Vcb,
2550 &EaFcb->Mcb,
2551 &EaOffsetMcb );
2552
2553 FsRtlUninitializeLargeMcb( &EaOffsetMcb );
2554 FsRtlInitializeLargeMcb( &EaOffsetMcb, PagedPool );
2555
2556 UnwindMergedNewOffset = TRUE;
2557 }
2558
2559 //
2560 // Merge the existing initial ea data if it exists.
2561 //
2562
2563 if (UnwindInitializedInitialEaMcb) {
2564
2565 FatMergeAllocation( IrpContext,
2566 Vcb,
2567 &EaFcb->Mcb,
2568 &EaInitialEaMcb );
2569
2570 FsRtlUninitializeLargeMcb( &EaInitialEaMcb );
2571 FsRtlInitializeLargeMcb( &EaInitialEaMcb, PagedPool );
2572
2573 UnwindMergedInitialEa = TRUE;
2574 }
2575
2576 //
2577 // We modify the offset of the new ea set by one cluster if
2578 // we added one to the offset table.
2579 //
2580
2581 if (AddedOffsetTableCluster) {
2582
2583 EaSetClusterOffset += 1;
2584 EaSetVbo += BytesPerCluster;
2585 }
2586
2587 //
2588 // Merge the new ea set.
2589 //
2590
2591 FatMergeAllocation( IrpContext,
2592 Vcb,
2593 &EaFcb->Mcb,
2594 &EaSetMcb );
2595
2596 FsRtlUninitializeLargeMcb( &EaSetMcb );
2597 FsRtlInitializeLargeMcb( &EaSetMcb, PagedPool );
2598
2599 UnwindMergedNewEaSet = TRUE;
2600
2601 //
2602 // Merge the tail if it exists.
2603 //
2604
2605 if (UnwindInitializedTailMcb) {
2606
2607 FatMergeAllocation( IrpContext,
2608 Vcb,
2609 &EaFcb->Mcb,
2610 &EaTailMcb );
2611
2612 FsRtlUninitializeLargeMcb( &EaTailMcb );
2613 FsRtlInitializeLargeMcb( &EaTailMcb, PagedPool );
2614
2615 UnwindMergedTail = TRUE;
2616 }
2617
2618 //
2619 // If we added a new cluster for the offset table, we need to
2620 // lock the entire cluster down and initialize all the handles to
2621 // the unused state except the first one.
2622 //
2623
2624 //
2625 // Update the Fcb information.
2626 //
2627
2628 UnwindPrevFileSize = EaFcb->Header.FileSize.LowPart;
2629
2630 EaFcb->Header.FileSize.LowPart += NewAllocation;
2631 EaFcb->Header.AllocationSize = EaFcb->Header.FileSize;
2632 EaDirent->FileSize = EaFcb->Header.FileSize.LowPart;
2633
2634 FatSetDirtyBcb( IrpContext, EaBcb, Vcb, TRUE );
2635
2636 //
2637 // Let Mm and Cc know the new file sizes.
2638 //
2639
2640 CcSetFileSizes( VirtualEaFile,
2641 (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize );
2642
2643 UnwindCacheValues = TRUE;
2644
2645 //
2646 // Pin down the file header.
2647 //
2648
2649 RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE ));
2650
2651 FatPinEaRange( IrpContext,
2652 VirtualEaFile,
2653 EaFcb,
2654 &EaHeaderRange,
2655 0,
2656 sizeof( EA_FILE_HEADER ),
2657 STATUS_DATA_ERROR );
2658
2659 EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data;
2660
2661 //
2662 // Pin down the entire offset table.
2663 //
2664
2665
2666 RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE ));
2667
2668 FatPinEaRange( IrpContext,
2669 VirtualEaFile,
2670 EaFcb,
2671 &EaOffsetRange,
2672 sizeof( EA_FILE_HEADER ) + (((ULONGLONG)NewEaIndex >> 7) << 8),
2673 sizeof( EA_OFF_TABLE ),
2674 STATUS_DATA_ERROR );
2675
2676 EaOffsetTable = (PUSHORT) EaOffsetRange.Data;
2677
2678 //
2679 // Pin the Ea set header for the added clusters and initialize
2680 // the fields of interest. These are the signature field, the
2681 // owning handle field, the need Ea field and the cbList field.
2682 // Also mark the data as dirty.
2683 //
2684
2685 //
2686 // Pin the ea set.
2687 //
2688
2689 FatPinEaRange( IrpContext,
2690 VirtualEaFile,
2691 EaFcb,
2692 EaSetRange,
2693 EaSetVbo,
2694 EaSetLength,
2695 STATUS_DATA_ERROR );
2696
2697 EaSet = (PEA_SET_HEADER) EaSetRange->Data;
2698
2699 EaSet->Signature = EA_SET_SIGNATURE;
2700 EaSet->OwnEaHandle = NewEaIndex;
2701
2702 FatMarkEaRangeDirty( IrpContext, VirtualEaFile, EaSetRange );
2703
2704 //
2705 // Update the Ea base and offset tables. For the Ea base table,
2706 // all subsequent index values must be incremented by the number
2707 // of clusters added.
2708 //
2709 // For the entries in the relevant Ea offset table, all entries
2710 // after this index must also be increased by the number of
2711 // clusters added.
2712 //
2713 // If we added another cluster to the offset table, then we increment
2714 // all the base table values by 1.
2715 //
2716
2717 Count = MAX_EA_BASE_INDEX - EaHeaderIndex - 1;
2718
2719 NextEaOffset = &EaHeader->EaBaseTable[EaHeaderIndex + 1];
2720
2721 while (Count--) {
2722
2723 *(NextEaOffset++) += EaSetClusterCount;
2724 }
2725
2726 if (AddedOffsetTableCluster) {
2727
2728 Count = MAX_EA_BASE_INDEX;
2729
2730 NextEaOffset = &EaHeader->EaBaseTable[0];
2731
2732 while (Count--) {
2733
2734 *(NextEaOffset++) += 1;
2735 }
2736 }
2737
2738 FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaHeaderRange );
2739
2740 //
2741 // If we added an offset table cluster, we need to initialize
2742 // the handles to unused.
2743 //
2744
2745 if (AddedOffsetTableCluster) {
2746
2747 Count = (BytesPerCluster >> 1) - 1;
2748 NextEaOffset = EaOffsetTable;
2749
2750 *NextEaOffset++ = 0;
2751
2752 while (Count--) {
2753
2754 *NextEaOffset++ = UNUSED_EA_HANDLE;
2755 }
2756 }
2757
2758 //
2759 // We need to compute the offset of the added Ea set clusters
2760 // from their base.
2761 //
2762
2763 NextEaOffset = EaOffsetTable + EaOffsetIndex;
2764
2765 *NextEaOffset++ = (USHORT) (EaSetClusterOffset
2766 - EaHeader->EaBaseTable[EaHeaderIndex]);
2767
2768 Count = MAX_EA_OFFSET_INDEX - EaOffsetIndex - 1;
2769
2770 while (Count--) {
2771
2772 if (*NextEaOffset != UNUSED_EA_HANDLE) {
2773
2774 *NextEaOffset += EaSetClusterCount;
2775 }
2776
2777 NextEaOffset++;
2778 }
2779
2780 FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaOffsetRange );
2781
2782 //
2783 // Update the callers parameters.
2784 //
2785
2786 *EaHandle = NewEaIndex;
2787
2788 DebugTrace(0, Dbg, "FatAddEaSet: Return values\n", 0);
2789
2790 DebugTrace(0, Dbg, "FatAddEaSet: New Handle -> %x\n",
2791 *EaHandle);
2792
2793 } _SEH2_FINALLY {
2794
2795 DebugUnwind( FatAddEaSet );
2796
2797 //
2798 // Handle cleanup for abnormal termination only if we allocated
2799 // disk space for the new ea set.
2800 //
2801
2802 if (_SEH2_AbnormalTermination() && UnwindAllocatedNewAllocation) {
2803
2804 //
2805 // If we modified the Ea dirent or Fcb, recover the previous
2806 // values. Even though we are decreasing FileSize here, we
2807 // don't need to synchronize to synchronize with paging Io
2808 // because there was no dirty data generated in the new allocation.
2809 //
2810
2811 if (UnwindPrevFileSize) {
2812
2813 EaFcb->Header.FileSize.LowPart = UnwindPrevFileSize;
2814 EaFcb->Header.AllocationSize.LowPart = UnwindPrevFileSize;
2815 EaDirent->FileSize = UnwindPrevFileSize;
2816
2817 if (UnwindCacheValues) {
2818
2819 CcSetFileSizes( VirtualEaFile,
2820 (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize );
2821 }
2822 }
2823
2824 //
2825 // If we merged the tail then split it off.
2826 //
2827
2828 if (UnwindMergedTail) {
2829
2830 VBO NewTailPosition;
2831
2832 NewTailPosition = EaSetVbo + EaSetLength;
2833
2834 FatSplitAllocation( IrpContext,
2835 Vcb,
2836 &EaFcb->Mcb,
2837 NewTailPosition,
2838 &EaTailMcb );
2839 }
2840
2841 //
2842 // If we merged the new ea data then split it out.
2843 //
2844
2845 if (UnwindMergedNewEaSet) {
2846
2847 FatSplitAllocation( IrpContext,
2848 Vcb,
2849 &EaFcb->Mcb,
2850 EaSetVbo,
2851 &EaSetMcb );
2852 }
2853
2854 //
2855 // If we merged the initial ea data then split it out.
2856 //
2857
2858 if (UnwindMergedInitialEa) {
2859
2860 FatSplitAllocation( IrpContext,
2861 Vcb,
2862 &EaFcb->Mcb,
2863 EaNewOffsetVbo + BytesPerCluster,
2864 &EaInitialEaMcb );
2865 }
2866
2867 //
2868 // If we added a new offset cluster, then split it out.
2869 //
2870
2871 if (UnwindMergedNewOffset) {
2872
2873 FatSplitAllocation( IrpContext,
2874 Vcb,
2875 &EaFcb->Mcb,
2876 EaNewOffsetVbo,
2877 &EaOffsetMcb );
2878 }
2879
2880 //
2881 // If there is an initial ea section prior to the new section, merge
2882 // it with the rest of the file.
2883 //
2884
2885 if (UnwindSplitInitialEa) {
2886
2887 FatMergeAllocation( IrpContext, Vcb, &EaFcb->Mcb, &EaInitialEaMcb );
2888 }
2889
2890 //
2891 // If there is a file tail split off, merge it with the
2892 // rest of the file.
2893 //
2894
2895 if (UnwindSplitTail) {
2896
2897 FatMergeAllocation( IrpContext, Vcb, &EaFcb->Mcb, &EaTailMcb );
2898 }
2899
2900 //
2901 // If we modified the cache initialization for the ea file,
2902 // then throw away the ea file object.
2903 //
2904
2905 if (UnwindPurgeCacheMap) {
2906
2907 Vcb->VirtualEaFile = NULL;
2908 ObDereferenceObject( VirtualEaFile );
2909 }
2910
2911 //
2912 // If we split the allocation, then deallocate the block for
2913 // the new offset information.
2914 //
2915
2916 if (UnwindSplitNewAllocation) {
2917
2918 FatDeallocateDiskSpace( IrpContext, Vcb, &EaOffsetMcb, FALSE );
2919 }
2920
2921 //
2922 // Deallocate the disk space.
2923 //
2924
2925 FatDeallocateDiskSpace( IrpContext, Vcb, &EaSetMcb, FALSE );
2926 }
2927
2928 //
2929 // Unpin the Ea ranges.
2930 //
2931
2932 FatUnpinEaRange( IrpContext, &EaHeaderRange );
2933 FatUnpinEaRange( IrpContext, &EaOffsetRange );
2934
2935 //
2936 // Uninitialize any local Mcbs
2937 //
2938
2939 if (UnwindInitializedEaSetMcb) {
2940
2941 FsRtlUninitializeLargeMcb( &EaSetMcb );
2942 }
2943
2944 if (UnwindInitializedOffsetMcb) {
2945
2946 FsRtlUninitializeLargeMcb( &EaOffsetMcb );
2947 }
2948
2949 if (UnwindInitializedTailMcb) {
2950
2951 FsRtlUninitializeLargeMcb( &EaTailMcb );
2952 }
2953
2954 if (UnwindInitializedInitialEaMcb) {
2955
2956 FsRtlUninitializeLargeMcb( &EaInitialEaMcb );
2957 }
2958
2959 DebugTrace(-1, Dbg, "FatAddEaSet -> Exit\n", 0);
2960 } _SEH2_END;
2961
2962 return;
2963 }
2964
2965
2966 VOID
FatAppendPackedEa(IN PIRP_CONTEXT IrpContext,IN OUT PEA_SET_HEADER * EaSetHeader,IN OUT PULONG PackedEasLength,IN OUT PULONG AllocationLength,IN PFILE_FULL_EA_INFORMATION FullEa,IN ULONG BytesPerCluster)2967 FatAppendPackedEa (
2968 IN PIRP_CONTEXT IrpContext,
2969 IN OUT PEA_SET_HEADER *EaSetHeader,
2970 IN OUT PULONG PackedEasLength,
2971 IN OUT PULONG AllocationLength,
2972 IN PFILE_FULL_EA_INFORMATION FullEa,
2973 IN ULONG BytesPerCluster
2974 )
2975
2976 /*++
2977
2978 Routine Description:
2979
2980 This routine appends a new packed ea onto an existing packed ea list,
2981 it also will allocate/dealloate pool as necessary to hold the ea list.
2982
2983 Arguments:
2984
2985 EaSetHeader - Supplies the address to store the pointer to pool memory
2986 which contains the Ea list for a file.
2987
2988 PackedEasLength - Supplies the length of the actual Ea data. The
2989 new Ea data will be appended at this point.
2990
2991 AllocationLength - Supplies the allocated length available for Ea
2992 data.
2993
2994 FullEa - Supplies a pointer to the new full ea that is to be appended
2995 (in packed form) to the packed ea list.
2996
2997 BytesPerCluster - Number of bytes per cluster on this volume.
2998
2999 NOTE: The EaSetHeader refers to the entire block of Ea data for a
3000 file. This includes the Ea's and their values as well as the
3001 header information. The PackedEasLength and AllocationLength
3002 parameters refer to the name/value pairs only.
3003
3004 Return Value:
3005
3006 None.
3007
3008 --*/
3009
3010 {
3011 ULONG PackedEaSize;
3012 PPACKED_EA ThisPackedEa;
3013 OEM_STRING EaName;
3014
3015 PAGED_CODE();
3016
3017 DebugTrace(+1, Dbg, "FatAppendPackedEa...\n", 0);
3018
3019 //
3020 // As a quick check see if the computed packed ea size plus the
3021 // current packed ea list size will overflow the buffer. Full Ea and
3022 // packed Ea only differ by 4 in their size
3023 //
3024
3025 PackedEaSize = SizeOfFullEa( FullEa ) - 4;
3026
3027 if ( PackedEaSize + *PackedEasLength > *AllocationLength ) {
3028
3029 //
3030 // We will overflow our current work buffer so allocate a larger
3031 // one and copy over the current buffer
3032 //
3033
3034 PVOID Temp;
3035 ULONG NewAllocationSize;
3036 ULONG OldAllocationSize;
3037
3038 DebugTrace(0, Dbg, "Allocate a new ea list buffer\n", 0);
3039
3040 //
3041 // Compute a new size and allocate space. Always increase the
3042 // allocation in cluster increments.
3043 //
3044
3045 NewAllocationSize = (SIZE_OF_EA_SET_HEADER
3046 + PackedEaSize
3047 + *PackedEasLength
3048 + BytesPerCluster - 1)
3049 & ~(BytesPerCluster - 1);
3050
3051 Temp = FsRtlAllocatePoolWithTag( PagedPool,
3052 NewAllocationSize,
3053 TAG_EA_SET_HEADER );
3054
3055 //
3056 // Move over the existing ea list, and deallocate the old one
3057 //
3058
3059 RtlCopyMemory( Temp,
3060 *EaSetHeader,
3061 OldAllocationSize = *AllocationLength
3062 + SIZE_OF_EA_SET_HEADER );
3063
3064 ExFreePool( *EaSetHeader );
3065
3066 //
3067 // Set up so we will use the new packed ea list
3068 //
3069
3070 *EaSetHeader = Temp;
3071
3072 //
3073 // Zero out the added memory.
3074 //
3075
3076 RtlZeroMemory( &(*EaSetHeader)->PackedEas[*AllocationLength],
3077 NewAllocationSize - OldAllocationSize );
3078
3079 *AllocationLength = NewAllocationSize - SIZE_OF_EA_SET_HEADER;
3080 }
3081
3082 //
3083 // Determine if we need to increment our need ea changes count
3084 //
3085
3086 if ( FlagOn(FullEa->Flags, FILE_NEED_EA )) {
3087
3088 //
3089 // The NeedEaCount field is long aligned so we will write
3090 // directly to it.
3091 //
3092
3093 (*EaSetHeader)->NeedEaCount++;
3094 }
3095
3096 //
3097 // Now copy over the ea, full ea's and packed ea are identical except
3098 // that full ea also have a next ea offset that we skip over
3099 //
3100 // Before:
3101 // UsedSize Allocated
3102 // | |
3103 // V V
3104 // +xxxxxxxx+-----------------------------+
3105 //
3106 // After:
3107 // UsedSize Allocated
3108 // | |
3109 // V V
3110 // +xxxxxxxx+yyyyyyyyyyyyyyyy+------------+
3111 //
3112
3113 ThisPackedEa = (PPACKED_EA) (RtlOffsetToPointer( (*EaSetHeader)->PackedEas,
3114 *PackedEasLength ));
3115
3116 RtlCopyMemory( ThisPackedEa,
3117 (PUCHAR) FullEa + 4,
3118 PackedEaSize );
3119
3120 //
3121 // Now convert the name to uppercase.
3122 //
3123
3124 EaName.MaximumLength = EaName.Length = FullEa->EaNameLength;
3125 EaName.Buffer = ThisPackedEa->EaName;
3126
3127 FatUpcaseEaName( IrpContext, &EaName, &EaName );
3128
3129 //
3130 // Increment the used size in the packed ea list structure
3131 //
3132
3133 *PackedEasLength += PackedEaSize;
3134
3135 //
3136 // And return to our caller
3137 //
3138
3139 DebugTrace(-1, Dbg, "FatAppendPackedEa -> VOID\n", 0);
3140
3141 UNREFERENCED_PARAMETER( IrpContext );
3142
3143 return;
3144 }
3145
3146
3147 VOID
FatDeletePackedEa(IN PIRP_CONTEXT IrpContext,IN OUT PEA_SET_HEADER EaSetHeader,IN OUT PULONG PackedEasLength,IN ULONG Offset)3148 FatDeletePackedEa (
3149 IN PIRP_CONTEXT IrpContext,
3150 IN OUT PEA_SET_HEADER EaSetHeader,
3151 IN OUT PULONG PackedEasLength,
3152 IN ULONG Offset
3153 )
3154
3155 /*++
3156
3157 Routine Description:
3158
3159 This routine deletes an individual packed ea from the supplied
3160 packed ea list.
3161
3162 Arguments:
3163
3164 EaSetHeader - Supplies the address to store the pointer to pool memory
3165 which contains the Ea list for a file.
3166
3167 PackedEasLength - Supplies the length of the actual Ea data. The
3168 new Ea data will be appended at this point.
3169
3170 Offset - Supplies the offset to the individual ea in the list to delete
3171
3172 NOTE: The EaSetHeader refers to the entire block of Ea data for a
3173 file. This includes the Ea's and their values as well as the
3174 header information. The PackedEasLength parameter refer to the
3175 name/value pairs only.
3176
3177 Return Value:
3178
3179 None.
3180
3181 --*/
3182
3183 {
3184 PPACKED_EA PackedEa;
3185 ULONG PackedEaSize;
3186
3187 PAGED_CODE();
3188
3189 DebugTrace(+1, Dbg, "FatDeletePackedEa, Offset = %08lx\n", Offset);
3190
3191 //
3192 // Get a reference to the packed ea and figure out its size
3193 //
3194
3195 PackedEa = (PPACKED_EA) (&EaSetHeader->PackedEas[Offset]);
3196
3197 SizeOfPackedEa( PackedEa, &PackedEaSize );
3198
3199 //
3200 // Determine if we need to decrement our need ea changes count
3201 //
3202
3203 if (FlagOn(PackedEa->Flags, EA_NEED_EA_FLAG)) {
3204
3205 EaSetHeader->NeedEaCount--;
3206 }
3207
3208 //
3209 // Shrink the ea list over the deleted ea. The amount to copy is the
3210 // total size of the ea list minus the offset to the end of the ea
3211 // we're deleting.
3212 //
3213 // Before:
3214 // Offset Offset+PackedEaSize UsedSize Allocated
3215 // | | | |
3216 // V V V V
3217 // +xxxxxxxx+yyyyyyyyyyyyyyyy+zzzzzzzzzzzzzzzzzz+------------+
3218 //
3219 // After
3220 // Offset UsedSize Allocated
3221 // | | |
3222 // V V V
3223 // +xxxxxxxx+zzzzzzzzzzzzzzzzzz+-----------------------------+
3224 //
3225
3226 RtlCopyMemory( PackedEa,
3227 (PUCHAR) PackedEa + PackedEaSize,
3228 *PackedEasLength - (Offset + PackedEaSize) );
3229
3230 //
3231 // And zero out the remaing part of the ea list, to make things
3232 // nice and more robust
3233 //
3234
3235 RtlZeroMemory( &EaSetHeader->PackedEas[*PackedEasLength - PackedEaSize],
3236 PackedEaSize );
3237
3238 //
3239 // Decrement the used size by the amount we just removed
3240 //
3241
3242 *PackedEasLength -= PackedEaSize;
3243
3244 //
3245 // And return to our caller
3246 //
3247
3248 DebugTrace(-1, Dbg, "FatDeletePackedEa -> VOID\n", 0);
3249
3250 UNREFERENCED_PARAMETER( IrpContext );
3251
3252 return;
3253 }
3254
3255
3256 ULONG
FatLocateNextEa(IN PIRP_CONTEXT IrpContext,IN PPACKED_EA FirstPackedEa,IN ULONG PackedEasLength,IN ULONG PreviousOffset)3257 FatLocateNextEa (
3258 IN PIRP_CONTEXT IrpContext,
3259 IN PPACKED_EA FirstPackedEa,
3260 IN ULONG PackedEasLength,
3261 IN ULONG PreviousOffset
3262 )
3263
3264 /*++
3265
3266 Routine Description:
3267
3268 This routine locates the offset for the next individual packed ea
3269 inside of a packed ea list, given the offset to a previous Ea.
3270 Instead of returing boolean to indicate if we've found the next one
3271 we let the return offset be so large that it overuns the used size
3272 of the packed ea list, and that way it's an easy construct to use
3273 in a for loop.
3274
3275 Arguments:
3276
3277 FirstPackedEa - Supplies a pointer to the packed ea list structure
3278
3279 PackedEasLength - Supplies the length of the packed ea list
3280
3281 PreviousOffset - Supplies the offset to a individual packed ea in the
3282 list
3283
3284 Return Value:
3285
3286 ULONG - The offset to the next ea in the list or 0xffffffff of one
3287 does not exist.
3288
3289 --*/
3290
3291 {
3292 PPACKED_EA PackedEa;
3293 ULONG PackedEaSize;
3294 ULONG Offset;
3295
3296 PAGED_CODE();
3297
3298 DebugTrace(+1, Dbg, "FatLocateNextEa, PreviousOffset = %08lx\n",
3299 PreviousOffset);
3300
3301 //
3302 // Make sure the previous offset is within the used size range
3303 //
3304
3305 if ( PreviousOffset >= PackedEasLength ) {
3306
3307 DebugTrace(-1, Dbg, "FatLocateNextEa -> 0xffffffff\n", 0);
3308 return 0xffffffff;
3309 }
3310
3311 //
3312 // Get a reference to the previous packed ea, and compute its size
3313 //
3314
3315 PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + PreviousOffset );
3316 SizeOfPackedEa( PackedEa, &PackedEaSize );
3317
3318 //
3319 // Compute to the next ea
3320 //
3321
3322 Offset = PreviousOffset + PackedEaSize;
3323
3324 //
3325 // Now, if the new offset is beyond the ea size then we know
3326 // that there isn't one so, we return an offset of 0xffffffff.
3327 // otherwise we'll leave the new offset alone.
3328 //
3329
3330 if ( Offset >= PackedEasLength ) {
3331
3332 Offset = 0xffffffff;
3333 }
3334
3335 DebugTrace(-1, Dbg, "FatLocateNextEa -> %08lx\n", Offset);
3336
3337 UNREFERENCED_PARAMETER( IrpContext );
3338
3339 return Offset;
3340 }
3341
3342
3343 BOOLEAN
FatLocateEaByName(IN PIRP_CONTEXT IrpContext,IN PPACKED_EA FirstPackedEa,IN ULONG PackedEasLength,IN POEM_STRING EaName,OUT PULONG Offset)3344 FatLocateEaByName (
3345 IN PIRP_CONTEXT IrpContext,
3346 IN PPACKED_EA FirstPackedEa,
3347 IN ULONG PackedEasLength,
3348 IN POEM_STRING EaName,
3349 OUT PULONG Offset
3350 )
3351
3352 /*++
3353
3354 Routine Description:
3355
3356 This routine locates the offset for the next individual packed ea
3357 inside of a packed ea list, given the name of the ea to locate
3358
3359 Arguments:
3360
3361 FirstPackedEa - Supplies a pointer to the packed ea list structure
3362
3363 PackedEasLength - Supplies the length of the packed ea list
3364
3365 EaName - Supplies the name of the ea search for
3366
3367 Offset - Receives the offset to the located individual ea in the list
3368 if one exists.
3369
3370 Return Value:
3371
3372 BOOLEAN - TRUE if the named packed ea exists in the list and FALSE
3373 otherwise.
3374
3375 --*/
3376
3377 {
3378 PPACKED_EA PackedEa;
3379 OEM_STRING Name;
3380
3381 PAGED_CODE();
3382
3383 DebugTrace(+1, Dbg, "FatLocateEaByName, EaName = %Z\n", EaName);
3384
3385 //
3386 // For each packed ea in the list check its name against the
3387 // ea name we're searching for
3388 //
3389
3390 for ( *Offset = 0;
3391 *Offset < PackedEasLength;
3392 *Offset = FatLocateNextEa( IrpContext,
3393 FirstPackedEa,
3394 PackedEasLength,
3395 *Offset )) {
3396
3397 //
3398 // Reference the packed ea and get a string to its name
3399 //
3400
3401 PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + *Offset);
3402
3403 Name.Buffer = &PackedEa->EaName[0];
3404 Name.Length = PackedEa->EaNameLength;
3405 Name.MaximumLength = PackedEa->EaNameLength;
3406
3407 //
3408 // Compare the two strings, if they are equal then we've
3409 // found the caller's ea
3410 //
3411
3412 if ( RtlCompareString( EaName, &Name, TRUE ) == 0 ) {
3413
3414 DebugTrace(-1, Dbg, "FatLocateEaByName -> TRUE, *Offset = %08lx\n", *Offset);
3415 return TRUE;
3416 }
3417 }
3418
3419 //
3420 // We've exhausted the ea list without finding a match so return false
3421 //
3422
3423 DebugTrace(-1, Dbg, "FatLocateEaByName -> FALSE\n", 0);
3424 return FALSE;
3425 }
3426
3427
3428 BOOLEAN
FatIsEaNameValid(IN PIRP_CONTEXT IrpContext,IN OEM_STRING Name)3429 FatIsEaNameValid (
3430 IN PIRP_CONTEXT IrpContext,
3431 IN OEM_STRING Name
3432 )
3433
3434 /*++
3435
3436 Routine Description:
3437
3438 This routine simple returns whether the specified file names conforms
3439 to the file system specific rules for legal Ea names.
3440
3441 For Ea names, the following rules apply:
3442
3443 A. An Ea name may not contain any of the following characters:
3444
3445 0x0000 - 0x001F \ / : * ? " < > | , + = [ ] ;
3446
3447 Arguments:
3448
3449 Name - Supllies the name to check.
3450
3451 Return Value:
3452
3453 BOOLEAN - TRUE if the name is legal, FALSE otherwise.
3454
3455 --*/
3456
3457 {
3458 ULONG Index;
3459
3460 UCHAR Char;
3461
3462 PAGED_CODE();
3463
3464 UNREFERENCED_PARAMETER( IrpContext );
3465
3466 //
3467 // Empty names are not valid.
3468 //
3469
3470 if ( Name.Length == 0 ) { return FALSE; }
3471
3472 //
3473 // At this point we should only have a single name, which can't have
3474 // more than 254 characters
3475 //
3476
3477 if ( Name.Length > 254 ) { return FALSE; }
3478
3479 for ( Index = 0; Index < (ULONG)Name.Length; Index += 1 ) {
3480
3481 Char = Name.Buffer[ Index ];
3482
3483 //
3484 // Skip over and Dbcs chacters
3485 //
3486
3487 if ( FsRtlIsLeadDbcsCharacter( Char ) ) {
3488
3489 NT_ASSERT( Index != (ULONG)(Name.Length - 1) );
3490
3491 Index += 1;
3492
3493 continue;
3494 }
3495
3496 //
3497 // Make sure this character is legal, and if a wild card, that
3498 // wild cards are permissible.
3499 //
3500
3501 if ( !FsRtlIsAnsiCharacterLegalFat(Char, FALSE) ) {
3502
3503 return FALSE;
3504 }
3505 }
3506
3507 return TRUE;
3508 }
3509
3510
3511 VOID
FatPinEaRange(IN PIRP_CONTEXT IrpContext,IN PFILE_OBJECT VirtualEaFile,IN PFCB EaFcb,IN OUT PEA_RANGE EaRange,IN ULONG StartingVbo,IN ULONG Length,IN NTSTATUS ErrorStatus)3512 FatPinEaRange (
3513 IN PIRP_CONTEXT IrpContext,
3514 IN PFILE_OBJECT VirtualEaFile,
3515 IN PFCB EaFcb,
3516 IN OUT PEA_RANGE EaRange,
3517 IN ULONG StartingVbo,
3518 IN ULONG Length,
3519 IN NTSTATUS ErrorStatus
3520 )
3521
3522 /*++
3523
3524 Routine Description:
3525
3526 This routine is called to pin a range within the Ea file. It will follow all the
3527 rules required by the cache manager so that we don't have overlapping pin operations.
3528 If the range being pinned spans a section then the desired data will be copied into
3529 an auxilary buffer. FatMarkEaRangeDirty will know whether to copy the data back
3530 into the cache or whether to simply mark the pinned data dirty.
3531
3532 Arguments:
3533
3534 VirtualEaFile - This is the stream file for the Ea file.
3535
3536 EaFcb - This is the Fcb for the Ea file.
3537
3538 EaRange - This is the Ea range structure for this request.
3539
3540 StartingVbo - This is the starting offset in the Ea file to read from.
3541
3542 Length - This is the length of the read.
3543
3544 ErrorStatus - This is the error status to use if we are reading outside
3545 of the file.
3546
3547 Return Value:
3548
3549 None.
3550
3551 --*/
3552
3553 {
3554 LARGE_INTEGER LargeVbo;
3555 ULONG ByteCount;
3556 PBCB *NextBcb;
3557 PVOID Buffer;
3558 PCHAR DestinationBuffer = NULL;
3559 BOOLEAN FirstPage = TRUE;
3560
3561 PAGED_CODE();
3562
3563 //
3564 // Verify that the entire read is contained within the Ea file.
3565 //
3566
3567 if (Length == 0
3568 || StartingVbo >= EaFcb->Header.AllocationSize.LowPart
3569 || (EaFcb->Header.AllocationSize.LowPart - StartingVbo) < Length) {
3570
3571 FatRaiseStatus( IrpContext, ErrorStatus );
3572 }
3573
3574 //
3575 // If the read will span a section, the system addresses may not be contiguous.
3576 // Allocate a separate buffer in this case.
3577 //
3578
3579 if (((StartingVbo & (EA_SECTION_SIZE - 1)) + Length) > EA_SECTION_SIZE) {
3580
3581 EaRange->Data = FsRtlAllocatePoolWithTag( PagedPool,
3582 Length,
3583 TAG_EA_DATA );
3584 EaRange->AuxilaryBuffer = TRUE;
3585
3586 DestinationBuffer = EaRange->Data;
3587
3588 } else {
3589
3590 //
3591 // PREfix correctly notes that if we don't decide here to have an aux buffer
3592 // and the flag is up in the EaRange, we'll party on random memory since
3593 // DestinationBuffer won't be set; however, this will never happen due to
3594 // initialization of ea ranges and the cleanup in UnpinEaRange.
3595 //
3596
3597 NT_ASSERT( EaRange->AuxilaryBuffer == FALSE );
3598 }
3599
3600
3601 //
3602 // If the read will require more pages than our structure will hold then
3603 // allocate an auxilary buffer. We have to figure the number of pages
3604 // being requested so we have to include the page offset of the first page of
3605 // the request.
3606 //
3607
3608 EaRange->BcbChainLength = (USHORT) (((StartingVbo & (PAGE_SIZE - 1)) + Length + PAGE_SIZE - 1) / PAGE_SIZE);
3609
3610 if (EaRange->BcbChainLength > EA_BCB_ARRAY_SIZE) {
3611
3612 EaRange->BcbChain = FsRtlAllocatePoolWithTag( PagedPool,
3613 sizeof( PBCB ) * EaRange->BcbChainLength,
3614 TAG_BCB );
3615
3616 RtlZeroMemory( EaRange->BcbChain, sizeof( PBCB ) * EaRange->BcbChainLength );
3617
3618 } else {
3619
3620 EaRange->BcbChain = (PBCB *) &EaRange->BcbArray;
3621 }
3622
3623 //
3624 // Store the byte range data in the Ea Range structure.
3625 //
3626
3627 EaRange->StartingVbo = StartingVbo;
3628 EaRange->Length = Length;
3629
3630 //
3631 // Compute the initial pin length.
3632 //
3633
3634 ByteCount = PAGE_SIZE - (StartingVbo & (PAGE_SIZE - 1));
3635
3636 //
3637 // For each page in the range; pin the page and update the Bcb count, copy to
3638 // the auxiliary buffer.
3639 //
3640
3641 NextBcb = EaRange->BcbChain;
3642
3643 while (Length != 0) {
3644
3645 //
3646 // Pin the page and remember the data start.
3647 //
3648
3649 LargeVbo.QuadPart = StartingVbo;
3650
3651 if (ByteCount > Length) {
3652
3653 ByteCount = Length;
3654 }
3655
3656 if (!CcPinRead( VirtualEaFile,
3657 &LargeVbo,
3658 ByteCount,
3659 BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT),
3660 NextBcb,
3661 &Buffer )) {
3662
3663 //
3664 // Could not read the data without waiting (cache miss).
3665 //
3666
3667 FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
3668 }
3669
3670 //
3671 // Increment the Bcb pointer and copy to the auxilary buffer if necessary.
3672 //
3673
3674 NextBcb += 1;
3675
3676 if (EaRange->AuxilaryBuffer == TRUE) {
3677
3678 RtlCopyMemory( DestinationBuffer,
3679 Buffer,
3680 ByteCount );
3681
3682 DestinationBuffer = (PCHAR) Add2Ptr( DestinationBuffer, ByteCount );
3683 }
3684
3685 StartingVbo += ByteCount;
3686 Length -= ByteCount;
3687
3688 //
3689 // If this is the first page then update the Ea Range structure.
3690 //
3691
3692 if (FirstPage) {
3693
3694 FirstPage = FALSE;
3695 ByteCount = PAGE_SIZE;
3696
3697 if (EaRange->AuxilaryBuffer == FALSE) {
3698
3699 EaRange->Data = Buffer;
3700 }
3701 }
3702 }
3703
3704 return;
3705 }
3706
3707
3708 VOID
FatMarkEaRangeDirty(IN PIRP_CONTEXT IrpContext,IN PFILE_OBJECT EaFileObject,IN OUT PEA_RANGE EaRange)3709 FatMarkEaRangeDirty (
3710 IN PIRP_CONTEXT IrpContext,
3711 IN PFILE_OBJECT EaFileObject,
3712 IN OUT PEA_RANGE EaRange
3713 )
3714
3715 /*++
3716
3717 Routine Description:
3718
3719 This routine is called to mark a range of the Ea file as dirty. If the modified
3720 data is sitting in an auxilary buffer then we will copy it back into the cache.
3721 In any case we will go through the list of Bcb's and mark them dirty.
3722
3723 Arguments:
3724
3725 EaFileObject - This is the file object for the Ea file.
3726
3727 EaRange - This is the Ea range structure for this request.
3728
3729 Return Value:
3730
3731 None.
3732
3733 --*/
3734
3735 {
3736 PBCB *NextBcb;
3737 ULONG BcbCount;
3738
3739 PAGED_CODE();
3740
3741 UNREFERENCED_PARAMETER( IrpContext );
3742
3743 //
3744 // If there is an auxilary buffer we need to copy the data back into the cache.
3745 //
3746
3747 if (EaRange->AuxilaryBuffer == TRUE) {
3748
3749 LARGE_INTEGER LargeVbo;
3750
3751 LargeVbo.QuadPart = EaRange->StartingVbo;
3752
3753 CcCopyWrite( EaFileObject,
3754 &LargeVbo,
3755 EaRange->Length,
3756 TRUE,
3757 EaRange->Data );
3758 }
3759
3760 //
3761 // Now walk through the Bcb chain and mark everything dirty.
3762 //
3763
3764 BcbCount = EaRange->BcbChainLength;
3765 NextBcb = EaRange->BcbChain;
3766
3767 while (BcbCount--) {
3768
3769 if (*NextBcb != NULL) {
3770
3771 CcSetDirtyPinnedData( *NextBcb, NULL );
3772 }
3773
3774 NextBcb += 1;
3775 }
3776
3777 return;
3778 }
3779
3780
3781 VOID
FatUnpinEaRange(IN PIRP_CONTEXT IrpContext,IN OUT PEA_RANGE EaRange)3782 FatUnpinEaRange (
3783 IN PIRP_CONTEXT IrpContext,
3784 IN OUT PEA_RANGE EaRange
3785 )
3786
3787 /*++
3788
3789 Routine Description:
3790
3791 This routine is called to unpin a range in the Ea file. Any structures allocated
3792 will be deallocated here.
3793
3794 Arguments:
3795
3796 EaRange - This is the Ea range structure for this request.
3797
3798 Return Value:
3799
3800 None.
3801
3802 --*/
3803
3804 {
3805 PBCB *NextBcb;
3806 ULONG BcbCount;
3807
3808 PAGED_CODE();
3809
3810 UNREFERENCED_PARAMETER( IrpContext );
3811
3812 //
3813 // If we allocated a auxilary buffer, deallocate it here.
3814 //
3815
3816 if (EaRange->AuxilaryBuffer == TRUE) {
3817
3818 ExFreePool( EaRange->Data );
3819 EaRange->AuxilaryBuffer = FALSE;
3820 }
3821
3822 //
3823 // Walk through the Bcb chain and unpin the data.
3824 //
3825
3826 if (EaRange->BcbChain != NULL) {
3827
3828 BcbCount = EaRange->BcbChainLength;
3829 NextBcb = EaRange->BcbChain;
3830
3831 while (BcbCount--) {
3832
3833 if (*NextBcb != NULL) {
3834
3835 CcUnpinData( *NextBcb );
3836 *NextBcb = NULL;
3837 }
3838
3839 NextBcb += 1;
3840 }
3841
3842 //
3843 // If we allocated a Bcb chain, deallocate it here.
3844 //
3845
3846 if (EaRange->BcbChain != &EaRange->BcbArray[0]) {
3847
3848 ExFreePool( EaRange->BcbChain );
3849 }
3850
3851 EaRange->BcbChain = NULL;
3852 }
3853
3854 return;
3855 }
3856
3857