xref: /reactos/drivers/filesystems/fastfat/ea.c (revision 09dde2cf)
1 /*++
2 
3 Copyright (c) 1989-2000 Microsoft Corporation
4 
5 Module Name:
6 
7     Ea.c
8 
9 Abstract:
10 
11     This module implements the EA routines for Fat called by
12     the dispatch driver.
13 
14 
15 --*/
16 
17 #include "fatprocs.h"
18 
19 //
20 //  The local debug trace level
21 //
22 
23 #define Dbg                              (DEBUG_TRACE_EA)
24 
25 //
26 //  Local procedure prototypes
27 //
28 
29 IO_STATUS_BLOCK
30 FatQueryEaUserEaList (
31     IN PIRP_CONTEXT IrpContext,
32     OUT PCCB Ccb,
33     IN PPACKED_EA FirstPackedEa,
34     IN ULONG PackedEasLength,
35     OUT PUCHAR UserBuffer,
36     IN ULONG  UserBufferLength,
37     IN PUCHAR UserEaList,
38     IN ULONG  UserEaListLength,
39     IN BOOLEAN ReturnSingleEntry
40     );
41 
42 IO_STATUS_BLOCK
43 FatQueryEaIndexSpecified (
44     IN PIRP_CONTEXT IrpContext,
45     OUT PCCB Ccb,
46     IN PPACKED_EA FirstPackedEa,
47     IN ULONG PackedEasLength,
48     OUT PUCHAR UserBuffer,
49     IN ULONG  UserBufferLength,
50     IN ULONG  UserEaIndex,
51     IN BOOLEAN ReturnSingleEntry
52     );
53 
54 IO_STATUS_BLOCK
55 FatQueryEaSimpleScan (
56     IN PIRP_CONTEXT IrpContext,
57     OUT PCCB Ccb,
58     IN PPACKED_EA FirstPackedEa,
59     IN ULONG PackedEasLength,
60     OUT PUCHAR UserBuffer,
61     IN ULONG  UserBufferLength,
62     IN BOOLEAN ReturnSingleEntry,
63     ULONG StartOffset
64     );
65 
66 BOOLEAN
67 FatIsDuplicateEaName (
68     IN PIRP_CONTEXT IrpContext,
69     IN PFILE_GET_EA_INFORMATION GetEa,
70     IN PUCHAR UserBuffer
71     );
72 
73 #ifdef ALLOC_PRAGMA
74 #pragma alloc_text(PAGE, FatCommonQueryEa)
75 #pragma alloc_text(PAGE, FatCommonSetEa)
76 #pragma alloc_text(PAGE, FatFsdQueryEa)
77 #pragma alloc_text(PAGE, FatFsdSetEa)
78 #if 0
79 #pragma alloc_text(PAGE, FatIsDuplicateEaName)
80 #pragma alloc_text(PAGE, FatQueryEaIndexSpecified)
81 #pragma alloc_text(PAGE, FatQueryEaSimpleScan)
82 #pragma alloc_text(PAGE, FatQueryEaUserEaList)
83 #endif
84 #endif
85 
86 
87 _Function_class_(IRP_MJ_QUERY_EA)
88 _Function_class_(DRIVER_DISPATCH)
89 NTSTATUS
90 NTAPI
91 FatFsdQueryEa (
92     _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
93     _Inout_ PIRP Irp
94     )
95 
96 /*++
97 
98 Routine Description:
99 
100     This routine implements the Fsd part of the NtQueryEa API
101     call.
102 
103 Arguments:
104 
105     VolumeDeviceObject - Supplies the volume device object where the file
106         being queried exists.
107 
108     Irp - Supplies the Irp being processed.
109 
110 Return Value:
111 
112     NTSTATUS - The FSD status for the Irp.
113 
114 --*/
115 
116 {
117     NTSTATUS Status;
118     PIRP_CONTEXT IrpContext = NULL;
119 
120     BOOLEAN TopLevel;
121 
122     PAGED_CODE();
123 
124     DebugTrace(+1, Dbg, "FatFsdQueryEa\n", 0);
125 
126     //
127     //  Call the common query routine, with blocking allowed if synchronous
128     //
129 
130     FsRtlEnterFileSystem();
131 
132     TopLevel = FatIsIrpTopLevel( Irp );
133 
134     _SEH2_TRY {
135 
136         IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
137 
138         Status = FatCommonQueryEa( IrpContext, Irp );
139 
140     } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
141 
142         //
143         //  We had some trouble trying to perform the requested
144         //  operation, so we'll abort the I/O request with
145         //  the error status that we get back from the
146         //  execption code
147         //
148 
149         Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
150     } _SEH2_END;
151 
152     if (TopLevel) { IoSetTopLevelIrp( NULL ); }
153 
154     FsRtlExitFileSystem();
155 
156     //
157     //  And return to our caller
158     //
159 
160     DebugTrace(-1, Dbg, "FatFsdQueryEa -> %08lx\n", Status);
161 
162     UNREFERENCED_PARAMETER( VolumeDeviceObject );
163 
164     return Status;
165 }
166 
167 
168 _Function_class_(IRP_MJ_SET_EA)
169 _Function_class_(DRIVER_DISPATCH)
170 NTSTATUS
171 NTAPI
172 FatFsdSetEa (
173     _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
174     _Inout_ PIRP Irp
175     )
176 
177 /*++
178 
179 Routine Description:
180 
181     This routine implements the FSD part of the NtSetEa API
182     call.
183 
184 Arguments:
185 
186     VolumeDeviceObject - Supplies the volume device object where the file
187         being set exists.
188 
189     Irp - Supplies the Irp being processed.
190 
191 Return Value:
192 
193     NTSTATUS - The FSD status for the Irp.
194 
195 --*/
196 
197 {
198     NTSTATUS Status;
199     PIRP_CONTEXT IrpContext = NULL;
200 
201     BOOLEAN TopLevel;
202 
203     PAGED_CODE();
204 
205     DebugTrace(+1, Dbg, "FatFsdSetEa\n", 0);
206 
207     //
208     //  Call the common set routine, with blocking allowed if synchronous
209     //
210 
211     FsRtlEnterFileSystem();
212 
213     TopLevel = FatIsIrpTopLevel( Irp );
214 
215     _SEH2_TRY {
216 
217         IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
218 
219         Status = FatCommonSetEa( IrpContext, Irp );
220 
221     } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
222 
223         //
224         //  We had some trouble trying to perform the requested
225         //  operation, so we'll abort the I/O request with
226         //  the error status that we get back from the
227         //  execption code
228         //
229 
230         Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
231     } _SEH2_END;
232 
233     if (TopLevel) { IoSetTopLevelIrp( NULL ); }
234 
235     FsRtlExitFileSystem();
236 
237     //
238     //  And return to our caller
239     //
240 
241     DebugTrace(-1, Dbg, "FatFsdSetEa -> %08lx\n", Status);
242 
243     UNREFERENCED_PARAMETER( VolumeDeviceObject );
244 
245     return Status;
246 }
247 
248 
249 NTSTATUS
250 FatCommonQueryEa (
251     IN PIRP_CONTEXT IrpContext,
252     IN PIRP Irp
253     )
254 
255 /*++
256 
257 Routine Description:
258 
259     This is the common routine for querying File ea called by both
260     the fsd and fsp threads.
261 
262 Arguments:
263 
264     Irp - Supplies the Irp being processed
265 
266 Return Value:
267 
268     NTSTATUS - The return status for the operation
269 
270 --*/
271 
272 {
273 #if 0
274     PIO_STACK_LOCATION IrpSp;
275 
276     NTSTATUS Status;
277 
278     PUCHAR  Buffer;
279     ULONG   UserBufferLength;
280 
281     PUCHAR  UserEaList;
282     ULONG   UserEaListLength;
283     ULONG   UserEaIndex;
284     BOOLEAN RestartScan;
285     BOOLEAN ReturnSingleEntry;
286     BOOLEAN IndexSpecified;
287 
288     PVCB Vcb;
289     PCCB Ccb;
290 
291     PFCB Fcb;
292     PDIRENT Dirent;
293     PBCB Bcb;
294 
295     PDIRENT EaDirent;
296     PBCB EaBcb;
297     BOOLEAN LockedEaFcb;
298 
299     PEA_SET_HEADER EaSetHeader;
300     EA_RANGE EaSetRange;
301 
302     USHORT ExtendedAttributes;
303 #endif
304 
305     PAGED_CODE();
306 
307     FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST);
308     return STATUS_INVALID_DEVICE_REQUEST;
309 
310 #if 0
311     //
312     //  Get the current Irp stack location
313     //
314 
315     IrpSp = IoGetCurrentIrpStackLocation( Irp );
316 
317     DebugTrace(+1, Dbg, "FatCommonQueryEa...\n", 0);
318     DebugTrace( 0, Dbg, " Wait                = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT));
319     DebugTrace( 0, Dbg, " Irp                 = %p\n", Irp );
320     DebugTrace( 0, Dbg, " ->SystemBuffer      = %p\n", Irp->AssociatedIrp.SystemBuffer );
321     DebugTrace( 0, Dbg, " ->Length            = %08lx\n", IrpSp->Parameters.QueryEa.Length );
322     DebugTrace( 0, Dbg, " ->EaList            = %08lx\n", IrpSp->Parameters.QueryEa.EaList );
323     DebugTrace( 0, Dbg, " ->EaListLength      = %08lx\n", IrpSp->Parameters.QueryEa.EaListLength );
324     DebugTrace( 0, Dbg, " ->EaIndex           = %08lx\n", IrpSp->Parameters.QueryEa.EaIndex );
325     DebugTrace( 0, Dbg, " ->RestartScan       = %08lx\n", FlagOn(IrpSp->Flags, SL_RESTART_SCAN));
326     DebugTrace( 0, Dbg, " ->ReturnSingleEntry = %08lx\n", FlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY));
327     DebugTrace( 0, Dbg, " ->IndexSpecified    = %08lx\n", FlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED));
328 
329     Irp->IoStatus.Status = STATUS_SUCCESS;
330     Irp->IoStatus.Information = 0;
331 
332     //
333     //  Check that the file object is associated with either a user file
334     //  or directory open.  We don't allow Ea operations on the root
335     //  directory.
336     //
337 
338     {
339         TYPE_OF_OPEN OpenType;
340 
341         if (((OpenType = FatDecodeFileObject( IrpSp->FileObject,
342                                              &Vcb,
343                                              &Fcb,
344                                              &Ccb )) != UserFileOpen
345              && OpenType != UserDirectoryOpen) ||
346 
347             (NodeType( Fcb )) == FAT_NTC_ROOT_DCB) {
348 
349             FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
350 
351             DebugTrace(-1, Dbg,
352                        "FatCommonQueryEa -> %08lx\n",
353                        STATUS_INVALID_PARAMETER);
354 
355             return STATUS_INVALID_PARAMETER;
356         }
357     }
358 
359     //
360     //  Fat32 does not support ea's.
361     //
362 
363     if (FatIsFat32(Vcb)) {
364 
365         FatCompleteRequest( IrpContext, Irp, STATUS_EAS_NOT_SUPPORTED );
366         DebugTrace(-1, Dbg,
367                    "FatCommonQueryEa -> %08lx\n",
368                    STATUS_EAS_NOT_SUPPORTED);
369         return STATUS_EAS_NOT_SUPPORTED;
370     }
371 
372     //
373     //  Acquire shared access to the Fcb and enqueue the Irp if we didn't
374     //  get access.
375     //
376 
377     if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
378 
379         DebugTrace(0, Dbg, "FatCommonQueryEa:  Thread can't wait\n", 0);
380 
381         Status = FatFsdPostRequest( IrpContext, Irp );
382 
383         DebugTrace(-1, Dbg, "FatCommonQueryEa -> %08lx\n", Status );
384 
385         return Status;
386     }
387 
388     FatAcquireSharedFcb( IrpContext, Fcb );
389 
390     //
391     //  Reference our input parameters to make things easier
392     //
393 
394     UserBufferLength  = IrpSp->Parameters.QueryEa.Length;
395     UserEaList        = IrpSp->Parameters.QueryEa.EaList;
396     UserEaListLength  = IrpSp->Parameters.QueryEa.EaListLength;
397     UserEaIndex       = IrpSp->Parameters.QueryEa.EaIndex;
398     RestartScan       = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN);
399     ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY);
400     IndexSpecified    = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED);
401 
402     //
403     //  Initialize our local values.
404     //
405 
406     LockedEaFcb = FALSE;
407     Bcb = NULL;
408     EaBcb = NULL;
409 
410     Status = STATUS_SUCCESS;
411 
412     RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
413 
414     try {
415 
416         PPACKED_EA FirstPackedEa;
417         ULONG PackedEasLength;
418 
419         Buffer = FatMapUserBuffer( IrpContext, Irp );
420 
421         //
422         //  We verify that the Fcb is still valid.
423         //
424 
425         FatVerifyFcb( IrpContext, Fcb );
426 
427         //
428         //  We need to get the dirent for the Fcb to recover the Ea handle.
429         //
430 
431         FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb );
432 
433         //
434         //  Verify that the Ea file is in a consistant state.  If the
435         //  Ea modification count in the Fcb doesn't match that in
436         //  the CCB, then the Ea file has been changed from under
437         //  us.  If we are not starting the search from the beginning
438         //  of the Ea set, we return an error.
439         //
440 
441         if (UserEaList == NULL
442             && Ccb->OffsetOfNextEaToReturn != 0
443             && !IndexSpecified
444             && !RestartScan
445             && Fcb->EaModificationCount != Ccb->EaModificationCount) {
446 
447             DebugTrace(0, Dbg,
448                       "FatCommonQueryEa:  Ea file in unknown state\n", 0);
449 
450             Status = STATUS_EA_CORRUPT_ERROR;
451 
452             try_return( Status );
453         }
454 
455         //
456         //  Show that the Ea's for this file are consistant for this
457         //  file handle.
458         //
459 
460         Ccb->EaModificationCount = Fcb->EaModificationCount;
461 
462         //
463         //  If the handle value is 0, then the file has no Eas.  We dummy up
464         //  an ea list to use below.
465         //
466 
467         ExtendedAttributes = Dirent->ExtendedAttributes;
468 
469         FatUnpinBcb( IrpContext, Bcb );
470 
471         if (ExtendedAttributes == 0) {
472 
473             DebugTrace(0, Dbg,
474                       "FatCommonQueryEa:  Zero handle, no Ea's for this file\n", 0);
475 
476             FirstPackedEa = (PPACKED_EA) NULL;
477 
478             PackedEasLength = 0;
479 
480         } else {
481 
482             //
483             //  We need to get the Ea file for this volume.  If the
484             //  operation doesn't complete due to blocking, then queue the
485             //  Irp to the Fsp.
486             //
487 
488             FatGetEaFile( IrpContext,
489                           Vcb,
490                           &EaDirent,
491                           &EaBcb,
492                           FALSE,
493                           FALSE );
494 
495             LockedEaFcb = TRUE;
496 
497             //
498             //  If the above operation completed and the Ea file did not exist,
499             //  the disk has been corrupted.  There is an existing Ea handle
500             //  without any Ea data.
501             //
502 
503             if (Vcb->VirtualEaFile == NULL) {
504 
505                 DebugTrace(0, Dbg,
506                           "FatCommonQueryEa:  No Ea file found when expected\n", 0);
507 
508                 Status = STATUS_NO_EAS_ON_FILE;
509 
510                 try_return( Status );
511             }
512 
513             //
514             //  We need to try to get the Ea set for the desired file.  If
515             //  blocking is necessary then we'll post the request to the Fsp.
516             //
517 
518             FatReadEaSet( IrpContext,
519                           Vcb,
520                           ExtendedAttributes,
521                           &Fcb->ShortName.Name.Oem,
522                           TRUE,
523                           &EaSetRange );
524 
525             EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
526 
527             //
528             //  Find the start and length of the Eas.
529             //
530 
531             FirstPackedEa = (PPACKED_EA) EaSetHeader->PackedEas;
532 
533             PackedEasLength = GetcbList( EaSetHeader ) - 4;
534         }
535 
536         //
537         //  Protect our access to the user buffer since IO dosn't do this
538         //  for us in this path unless we had specified that our driver
539         //  requires buffering for these large requests.  We don't, so ...
540         //
541 
542         try {
543 
544             //
545             //  Let's clear the output buffer.
546             //
547 
548             RtlZeroMemory( Buffer, UserBufferLength );
549 
550             //
551             //  We now satisfy the user's request depending on whether he
552             //  specified an Ea name list, an Ea index or restarting the
553             //  search.
554             //
555 
556             //
557             //  The user has supplied a list of Ea names.
558             //
559 
560             if (UserEaList != NULL) {
561 
562                 Irp->IoStatus = FatQueryEaUserEaList( IrpContext,
563                                                       Ccb,
564                                                       FirstPackedEa,
565                                                       PackedEasLength,
566                                                       Buffer,
567                                                       UserBufferLength,
568                                                       UserEaList,
569                                                       UserEaListLength,
570                                                       ReturnSingleEntry );
571 
572             //
573             //  The user supplied an index into the Ea list.
574             //
575 
576             } else if (IndexSpecified) {
577 
578                 Irp->IoStatus = FatQueryEaIndexSpecified( IrpContext,
579                                                           Ccb,
580                                                           FirstPackedEa,
581                                                           PackedEasLength,
582                                                           Buffer,
583                                                           UserBufferLength,
584                                                           UserEaIndex,
585                                                           ReturnSingleEntry );
586 
587             //
588             //  Else perform a simple scan, taking into account the restart
589             //  flag and the position of the next Ea stored in the Ccb.
590             //
591 
592             } else {
593 
594                 Irp->IoStatus = FatQueryEaSimpleScan( IrpContext,
595                                                       Ccb,
596                                                       FirstPackedEa,
597                                                       PackedEasLength,
598                                                       Buffer,
599                                                       UserBufferLength,
600                                                       ReturnSingleEntry,
601                                                       RestartScan
602                                                       ? 0
603                                                       : Ccb->OffsetOfNextEaToReturn );
604             }
605 
606         }  except (!FsRtlIsNtstatusExpected(GetExceptionCode()) ?
607                    EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
608 
609                //
610                //  We must have had a problem filling in the user's buffer, so fail.
611                //
612 
613                Irp->IoStatus.Status = GetExceptionCode();
614                Irp->IoStatus.Information = 0;
615         }
616 
617         Status = Irp->IoStatus.Status;
618 
619     try_exit: NOTHING;
620     } finally {
621 
622         DebugUnwind( FatCommonQueryEa );
623 
624         //
625         //  Release the Fcb for the file object, and the Ea Fcb if
626         //  successfully locked.
627         //
628 
629         FatReleaseFcb( IrpContext, Fcb );
630 
631         if (LockedEaFcb) {
632 
633             FatReleaseFcb( IrpContext, Vcb->EaFcb );
634         }
635 
636         //
637         //  Unpin the dirents for the Fcb, EaFcb and EaSetFcb if necessary.
638         //
639 
640         FatUnpinBcb( IrpContext, Bcb );
641         FatUnpinBcb( IrpContext, EaBcb );
642 
643         FatUnpinEaRange( IrpContext, &EaSetRange );
644 
645         if (!AbnormalTermination()) {
646 
647             FatCompleteRequest( IrpContext, Irp, Status );
648         }
649 
650         DebugTrace(-1, Dbg, "FatCommonQueryEa -> %08lx\n", Status);
651     }
652 
653     return Status;
654 #endif
655 }
656 
657 
658 NTSTATUS
659 FatCommonSetEa (
660     IN PIRP_CONTEXT IrpContext,
661     IN PIRP Irp
662     )
663 
664 /*++
665 
666 Routine Description:
667 
668     This routine implements the common Set Ea File Api called by the
669     the Fsd and Fsp threads
670 
671 Arguments:
672 
673     Irp - Supplies the Irp to process
674 
675 Return Value:
676 
677     NTSTATUS - The appropriate status for the Irp
678 
679 --*/
680 
681 {
682 #if 0
683     PIO_STACK_LOCATION IrpSp;
684 
685     NTSTATUS Status;
686 
687     USHORT ExtendedAttributes;
688 
689     PUCHAR Buffer;
690     ULONG UserBufferLength;
691 
692     PVCB Vcb;
693     PCCB Ccb;
694 
695     PFCB Fcb;
696     PDIRENT Dirent;
697     PBCB Bcb = NULL;
698 
699     PDIRENT EaDirent = NULL;
700     PBCB EaBcb = NULL;
701 
702     PEA_SET_HEADER EaSetHeader = NULL;
703 
704     PEA_SET_HEADER PrevEaSetHeader;
705     PEA_SET_HEADER NewEaSetHeader;
706     EA_RANGE EaSetRange;
707 
708     BOOLEAN AcquiredVcb = FALSE;
709     BOOLEAN AcquiredFcb = FALSE;
710     BOOLEAN AcquiredParentDcb = FALSE;
711     BOOLEAN AcquiredRootDcb = FALSE;
712     BOOLEAN AcquiredEaFcb = FALSE;
713 #endif
714 
715     PAGED_CODE();
716 
717     FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST);
718     return STATUS_INVALID_DEVICE_REQUEST;
719 
720 #if 0
721 
722     //
723     //  The following booleans are used in the unwind process.
724     //
725 
726     //
727     //  Get the current Irp stack location
728     //
729 
730     IrpSp = IoGetCurrentIrpStackLocation( Irp );
731 
732     DebugTrace(+1, Dbg, "FatCommonSetEa...\n", 0);
733     DebugTrace( 0, Dbg, " Wait                = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT));
734     DebugTrace( 0, Dbg, " Irp                 = %p\n", Irp );
735     DebugTrace( 0, Dbg, " ->SystemBuffer      = %p\n", Irp->AssociatedIrp.SystemBuffer );
736     DebugTrace( 0, Dbg, " ->Length            = %08lx\n", IrpSp->Parameters.SetEa.Length );
737 
738     Irp->IoStatus.Status = STATUS_SUCCESS;
739     Irp->IoStatus.Information = 0;
740 
741     //
742     //  Check that the file object is associated with either a user file
743     //  or directory open.
744     //
745 
746     {
747         TYPE_OF_OPEN OpenType;
748 
749         if (((OpenType = FatDecodeFileObject( IrpSp->FileObject,
750                                              &Vcb,
751                                              &Fcb,
752                                              &Ccb )) != UserFileOpen
753              && OpenType != UserDirectoryOpen) ||
754 
755             (NodeType( Fcb )) == FAT_NTC_ROOT_DCB) {
756 
757             FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
758 
759             DebugTrace(-1, Dbg,
760                        "FatCommonSetEa -> %08lx\n",
761                        STATUS_INVALID_PARAMETER);
762 
763             return STATUS_INVALID_PARAMETER;
764         }
765     }
766 
767     //
768     // Fat32 does not support ea's.
769     //
770 
771     if (FatIsFat32(Vcb)) {
772 
773         FatCompleteRequest( IrpContext, Irp, STATUS_EAS_NOT_SUPPORTED );
774         DebugTrace(-1, Dbg,
775                    "FatCommonSetEa -> %08lx\n",
776                    STATUS_EAS_NOT_SUPPORTED);
777         return STATUS_EAS_NOT_SUPPORTED;
778     }
779 
780     //
781     //  Reference our input parameters to make things easier
782     //
783 
784     UserBufferLength  = IrpSp->Parameters.SetEa.Length;
785 
786     //
787     //  Since we ask for no outside help (direct or buffered IO), it
788     //  is our responsibility to insulate ourselves from the
789     //  deviousness of the user above.  Now, buffer and validate the
790     //  contents.
791     //
792 
793     Buffer = FatBufferUserBuffer( IrpContext, Irp, UserBufferLength );
794 
795     //
796     //  Check the validity of the buffer with the new eas.  We really
797     //  need to do this always since we don't know, if it was already
798     //  buffered, that we buffered and checked it or some overlying
799     //  filter buffered without checking.
800     //
801 
802     Status = IoCheckEaBufferValidity( (PFILE_FULL_EA_INFORMATION) Buffer,
803                                       UserBufferLength,
804                                       (PULONG)&Irp->IoStatus.Information );
805 
806     if (!NT_SUCCESS( Status )) {
807 
808         FatCompleteRequest( IrpContext, Irp, Status );
809         DebugTrace(-1, Dbg,
810                    "FatCommonSetEa -> %08lx\n",
811                    Status);
812         return Status;
813     }
814 
815     //
816     //  Acquire exclusive access to the Fcb.  If this is a write-through operation
817     //  we will need to pick up the other possible streams that can be modified in
818     //  this operation so that the locking order is preserved - the root directory
819     //  (dirent addition if EA database doesn't already exist) and the parent
820     //  directory (addition of the EA handle to the object's dirent).
821     //
822     //  We are primarily synchronizing with directory enumeration here.
823     //
824     //  If we cannot wait need to send things off to the fsp.
825     //
826 
827     if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
828 
829         DebugTrace(0, Dbg, "FatCommonSetEa:  Set Ea must be waitable\n", 0);
830 
831         Status = FatFsdPostRequest( IrpContext, Irp );
832 
833         DebugTrace(-1, Dbg, "FatCommonSetEa -> %08lx\n", Status );
834 
835         return Status;
836     }
837 
838     //
839     //  Set this handle as having modified the file
840     //
841 
842     IrpSp->FileObject->Flags |= FO_FILE_MODIFIED;
843 
844     RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
845 
846     try {
847 
848         ULONG PackedEasLength;
849         BOOLEAN PreviousEas;
850         ULONG AllocationLength;
851         ULONG BytesPerCluster;
852         USHORT EaHandle;
853 
854         PFILE_FULL_EA_INFORMATION FullEa;
855 
856         //
857         //  Now go pick up everything
858         //
859 
860         FatAcquireSharedVcb( IrpContext, Fcb->Vcb );
861         AcquiredVcb = TRUE;
862         FatAcquireExclusiveFcb( IrpContext, Fcb );
863         AcquiredFcb = TRUE;
864 
865         if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH)) {
866 
867             if (Fcb->ParentDcb) {
868 
869                 FatAcquireExclusiveFcb( IrpContext, Fcb->ParentDcb );
870                 AcquiredParentDcb = TRUE;
871             }
872 
873             FatAcquireExclusiveFcb( IrpContext, Fcb->Vcb->RootDcb );
874             AcquiredRootDcb = TRUE;
875         }
876 
877         //
878         //  We verify that the Fcb is still valid.
879         //
880 
881         FatVerifyFcb( IrpContext, Fcb );
882 
883         //
884         //  We need to get the dirent for the Fcb to recover the Ea handle.
885         //
886 
887         FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb );
888 
889         DebugTrace(0, Dbg, "FatCommonSetEa:  Dirent Address -> %p\n",
890                    Dirent );
891         DebugTrace(0, Dbg, "FatCommonSetEa:  Dirent Bcb -> %p\n",
892                    Bcb);
893 
894         //
895         //  If the handle value is 0, then the file has no Eas.  In that
896         //  case we allocate memory to hold the Eas to be added.  If there
897         //  are existing Eas for the file, then we must read from the
898         //  file and copy the Eas.
899         //
900 
901         ExtendedAttributes = Dirent->ExtendedAttributes;
902 
903         FatUnpinBcb( IrpContext, Bcb );
904 
905         if (ExtendedAttributes == 0) {
906 
907             PreviousEas = FALSE;
908 
909             DebugTrace(0, Dbg,
910                       "FatCommonSetEa:  File has no current Eas\n", 0 );
911 
912         } else {
913 
914             PreviousEas = TRUE;
915 
916             DebugTrace(0, Dbg, "FatCommonSetEa:  File has previous Eas\n", 0 );
917 
918             FatGetEaFile( IrpContext,
919                           Vcb,
920                           &EaDirent,
921                           &EaBcb,
922                           FALSE,
923                           TRUE );
924 
925             AcquiredEaFcb = TRUE;
926 
927             //
928             //  If we didn't get the file then there is an error on
929             //  the disk.
930             //
931 
932             if (Vcb->VirtualEaFile == NULL) {
933 
934                 Status = STATUS_NO_EAS_ON_FILE;
935                 try_return( Status );
936             }
937         }
938 
939         DebugTrace(0, Dbg, "FatCommonSetEa:  EaBcb -> %p\n", EaBcb);
940 
941         DebugTrace(0, Dbg, "FatCommonSetEa:  EaDirent -> %p\n", EaDirent);
942 
943         //
944         //  If the file has existing ea's, we need to read them to
945         //  determine the size of the buffer allocation.
946         //
947 
948         if (PreviousEas) {
949 
950             //
951             //  We need to try to get the Ea set for the desired file.
952             //
953 
954             FatReadEaSet( IrpContext,
955                           Vcb,
956                           ExtendedAttributes,
957                           &Fcb->ShortName.Name.Oem,
958                           TRUE,
959                           &EaSetRange );
960 
961             PrevEaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
962 
963             //
964             //  We now must allocate pool memory for our copy of the
965             //  EaSetHeader and then copy the Ea data into it.  At that
966             //  time we can unpin the EaSet.
967             //
968 
969             PackedEasLength = GetcbList( PrevEaSetHeader ) - 4;
970 
971         //
972         //  Else we will create a dummy EaSetHeader.
973         //
974 
975         } else {
976 
977             PackedEasLength = 0;
978         }
979 
980         BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
981 
982         AllocationLength = (PackedEasLength
983                             + SIZE_OF_EA_SET_HEADER
984                             + BytesPerCluster - 1)
985                            & ~(BytesPerCluster - 1);
986 
987         EaSetHeader = FsRtlAllocatePoolWithTag( PagedPool,
988                                                 AllocationLength,
989                                                 TAG_EA_SET_HEADER );
990 
991         //
992         //  Copy the existing Eas over to pool memory.
993         //
994 
995         if (PreviousEas) {
996 
997             RtlCopyMemory( EaSetHeader, PrevEaSetHeader, AllocationLength );
998 
999             FatUnpinEaRange( IrpContext, &EaSetRange );
1000 
1001         } else {
1002 
1003             RtlZeroMemory( EaSetHeader, AllocationLength );
1004 
1005             RtlCopyMemory( EaSetHeader->OwnerFileName,
1006                            Fcb->ShortName.Name.Oem.Buffer,
1007                            Fcb->ShortName.Name.Oem.Length );
1008         }
1009 
1010 
1011         AllocationLength -= SIZE_OF_EA_SET_HEADER;
1012 
1013         DebugTrace(0, Dbg, "FatCommonSetEa:  Initial Ea set -> %p\n",
1014                    EaSetHeader);
1015 
1016         //
1017         //  At this point we have either read in the current eas for the file
1018         //  or we have initialized a new empty buffer for the eas.  Now for
1019         //  each full ea in the input user buffer we do the specified operation
1020         //  on the ea
1021         //
1022 
1023         for (FullEa = (PFILE_FULL_EA_INFORMATION) Buffer;
1024              FullEa < (PFILE_FULL_EA_INFORMATION) &Buffer[UserBufferLength];
1025              FullEa = (PFILE_FULL_EA_INFORMATION) (FullEa->NextEntryOffset == 0 ?
1026                                   &Buffer[UserBufferLength] :
1027                                   (PUCHAR) FullEa + FullEa->NextEntryOffset)) {
1028 
1029             OEM_STRING EaName;
1030             ULONG Offset;
1031 
1032             EaName.MaximumLength = EaName.Length = FullEa->EaNameLength;
1033             EaName.Buffer = &FullEa->EaName[0];
1034 
1035             DebugTrace(0, Dbg, "FatCommonSetEa:  Next Ea name -> %Z\n",
1036                        &EaName);
1037 
1038             //
1039             //  Make sure the ea name is valid
1040             //
1041 
1042             if (!FatIsEaNameValid( IrpContext,EaName )) {
1043 
1044                 Irp->IoStatus.Information = (PUCHAR)FullEa - Buffer;
1045                 Status = STATUS_INVALID_EA_NAME;
1046                 try_return( Status );
1047             }
1048 
1049             //
1050             //  Check that no invalid ea flags are set.
1051             //
1052 
1053             //
1054             //  TEMPCODE  We are returning STATUS_INVALID_EA_NAME
1055             //  until a more appropriate error code exists.
1056             //
1057 
1058             if (FullEa->Flags != 0
1059                 && FullEa->Flags != FILE_NEED_EA) {
1060 
1061                 Irp->IoStatus.Information = (PUCHAR)FullEa - (PUCHAR)Buffer;
1062                 try_return( Status = STATUS_INVALID_EA_NAME );
1063             }
1064 
1065             //
1066             //  See if we can locate the ea name in the ea set
1067             //
1068 
1069             if (FatLocateEaByName( IrpContext,
1070                                    (PPACKED_EA) EaSetHeader->PackedEas,
1071                                    PackedEasLength,
1072                                    &EaName,
1073                                    &Offset )) {
1074 
1075                 DebugTrace(0, Dbg, "FatCommonSetEa:  Found Ea name\n", 0);
1076 
1077                 //
1078                 //  We found the ea name so now delete the current entry,
1079                 //  and if the new ea value length is not zero then we
1080                 //  replace if with the new ea
1081                 //
1082 
1083                 FatDeletePackedEa( IrpContext,
1084                                    EaSetHeader,
1085                                    &PackedEasLength,
1086                                    Offset );
1087             }
1088 
1089             if (FullEa->EaValueLength != 0) {
1090 
1091                 FatAppendPackedEa( IrpContext,
1092                                    &EaSetHeader,
1093                                    &PackedEasLength,
1094                                    &AllocationLength,
1095                                    FullEa,
1096                                    BytesPerCluster );
1097             }
1098         }
1099 
1100         //
1101         //  If there are any ea's not removed, we
1102         //  call 'AddEaSet' to insert them into the Fat chain.
1103         //
1104 
1105         if (PackedEasLength != 0) {
1106 
1107             LARGE_INTEGER EaOffset;
1108 
1109             EaOffset.HighPart = 0;
1110 
1111             //
1112             //  If the packed eas length (plus 4 bytes) is greater
1113             //  than the maximum allowed ea size, we return an error.
1114             //
1115 
1116             if (PackedEasLength + 4 > MAXIMUM_EA_SIZE) {
1117 
1118                 DebugTrace( 0, Dbg, "Ea length is greater than maximum\n", 0 );
1119 
1120                 try_return( Status = STATUS_EA_TOO_LARGE );
1121             }
1122 
1123             //
1124             //  We need to now read the ea file if we haven't already.
1125             //
1126 
1127             if (EaDirent == NULL) {
1128 
1129                 FatGetEaFile( IrpContext,
1130                               Vcb,
1131                               &EaDirent,
1132                               &EaBcb,
1133                               TRUE,
1134                               TRUE );
1135 
1136                 AcquiredEaFcb = TRUE;
1137             }
1138 
1139             FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb );
1140 
1141             RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
1142 
1143             FatAddEaSet( IrpContext,
1144                          Vcb,
1145                          PackedEasLength + SIZE_OF_EA_SET_HEADER,
1146                          EaBcb,
1147                          EaDirent,
1148                          &EaHandle,
1149                          &EaSetRange );
1150 
1151             NewEaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
1152 
1153             DebugTrace(0, Dbg, "FatCommonSetEa:  Adding an ea set\n", 0);
1154 
1155             //
1156             //  Store the length of the new Ea's into the EaSetHeader.
1157             //  This is the PackedEasLength + 4.
1158             //
1159 
1160             PackedEasLength += 4;
1161 
1162             CopyU4char( EaSetHeader->cbList, &PackedEasLength );
1163 
1164             //
1165             //  Copy all but the first four bytes of EaSetHeader into
1166             //  NewEaSetHeader.  The signature and index fields have
1167             //  already been filled in.
1168             //
1169 
1170             RtlCopyMemory( &NewEaSetHeader->NeedEaCount,
1171                            &EaSetHeader->NeedEaCount,
1172                            PackedEasLength + SIZE_OF_EA_SET_HEADER - 8 );
1173 
1174             FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange );
1175             FatUnpinEaRange( IrpContext, &EaSetRange );
1176 
1177             CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
1178 
1179         } else {
1180 
1181             FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb );
1182 
1183             EaHandle = 0;
1184         }
1185 
1186         //
1187         //  Now we do a wholesale replacement of the ea for the file
1188         //
1189 
1190         if (PreviousEas) {
1191 
1192             FatDeleteEaSet( IrpContext,
1193                             Vcb,
1194                             EaBcb,
1195                             EaDirent,
1196                             ExtendedAttributes,
1197                             &Fcb->ShortName.Name.Oem );
1198 
1199             CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
1200         }
1201 
1202         if (PackedEasLength != 0 ) {
1203 
1204             Fcb->EaModificationCount++;
1205         }
1206 
1207         //
1208         //  Mark the dirent with the new ea's
1209         //
1210 
1211         Dirent->ExtendedAttributes = EaHandle;
1212 
1213         FatSetDirtyBcb( IrpContext, Bcb, Vcb, TRUE );
1214 
1215         //
1216         //  We call the notify package to report that the ea's were
1217         //  modified.
1218         //
1219 
1220         FatNotifyReportChange( IrpContext,
1221                                Vcb,
1222                                Fcb,
1223                                FILE_NOTIFY_CHANGE_EA,
1224                                FILE_ACTION_MODIFIED );
1225 
1226         Irp->IoStatus.Information = 0;
1227         Status = STATUS_SUCCESS;
1228 
1229     try_exit: NOTHING;
1230 
1231         //
1232         //  Unpin the dirents for the Fcb and EaFcb if necessary.
1233         //
1234 
1235         FatUnpinBcb( IrpContext, Bcb );
1236         FatUnpinBcb( IrpContext, EaBcb );
1237 
1238         FatUnpinRepinnedBcbs( IrpContext );
1239 
1240     } finally {
1241 
1242         DebugUnwind( FatCommonSetEa );
1243 
1244         //
1245         //  If this is an abnormal termination, we need to clean up
1246         //  any locked resources.
1247         //
1248 
1249         if (AbnormalTermination()) {
1250 
1251             //
1252             //  Unpin the dirents for the Fcb, EaFcb and EaSetFcb if necessary.
1253             //
1254 
1255             FatUnpinBcb( IrpContext, Bcb );
1256             FatUnpinBcb( IrpContext, EaBcb );
1257 
1258             FatUnpinEaRange( IrpContext, &EaSetRange );
1259         }
1260 
1261         //
1262         //  Release the Fcbs/Vcb acquired.
1263         //
1264 
1265         if (AcquiredEaFcb) {
1266             FatReleaseFcb( IrpContext, Vcb->EaFcb );
1267         }
1268 
1269         if (AcquiredFcb) {
1270             FatReleaseFcb( IrpContext, Fcb );
1271         }
1272 
1273         if (AcquiredParentDcb) {
1274             FatReleaseFcb( IrpContext, Fcb->ParentDcb );
1275         }
1276 
1277         if (AcquiredRootDcb) {
1278             FatReleaseFcb( IrpContext, Fcb->Vcb->RootDcb );
1279         }
1280 
1281         if (AcquiredVcb) {
1282             FatReleaseVcb( IrpContext, Fcb->Vcb );
1283         }
1284 
1285         //
1286         //  Deallocate our Ea buffer.
1287         //
1288 
1289         if (EaSetHeader != NULL) {
1290 
1291             ExFreePool( EaSetHeader );
1292         }
1293 
1294         //
1295         //  Complete the irp.
1296         //
1297 
1298         if (!AbnormalTermination()) {
1299 
1300             FatCompleteRequest( IrpContext, Irp, Status );
1301         }
1302 
1303         DebugTrace(-1, Dbg, "FatCommonSetEa -> %08lx\n", Status);
1304     }
1305 
1306     //
1307     //  And return to our caller
1308     //
1309 
1310     return Status;
1311 #endif
1312 }
1313 
1314 
1315 #if 0
1316 
1317 //
1318 //  Local Support Routine
1319 //
1320 
1321 IO_STATUS_BLOCK
1322 FatQueryEaUserEaList (
1323     IN PIRP_CONTEXT IrpContext,
1324     OUT PCCB Ccb,
1325     IN PPACKED_EA FirstPackedEa,
1326     IN ULONG PackedEasLength,
1327     OUT PUCHAR UserBuffer,
1328     IN ULONG  UserBufferLength,
1329     IN PUCHAR UserEaList,
1330     IN ULONG  UserEaListLength,
1331     IN BOOLEAN ReturnSingleEntry
1332     )
1333 
1334 /*++
1335 
1336 Routine Description:
1337 
1338     This routine is the work routine for querying EAs given an ea index
1339 
1340 Arguments:
1341 
1342     Ccb - Supplies the Ccb for the query
1343 
1344     FirstPackedEa - Supplies the first ea for the file being queried
1345 
1346     PackedEasLength - Supplies the length of the ea data
1347 
1348     UserBuffer - Supplies the buffer to receive the full eas
1349 
1350     UserBufferLength - Supplies the length, in bytes, of the user buffer
1351 
1352     UserEaList - Supplies the user specified ea name list
1353 
1354     UserEaListLength - Supplies the length, in bytes, of the user ea list
1355 
1356     ReturnSingleEntry - Indicates if we are to return a single entry or not
1357 
1358 Return Value:
1359 
1360     IO_STATUS_BLOCK - Receives the completion status for the operation
1361 
1362 --*/
1363 
1364 {
1365     IO_STATUS_BLOCK Iosb;
1366 
1367     ULONG Offset;
1368     ULONG RemainingUserBufferLength;
1369 
1370     PPACKED_EA PackedEa;
1371     ULONG PackedEaSize;
1372 
1373     PFILE_FULL_EA_INFORMATION LastFullEa = NULL;
1374     ULONG LastFullEaSize;
1375     PFILE_FULL_EA_INFORMATION NextFullEa;
1376 
1377     PFILE_GET_EA_INFORMATION GetEa;
1378 
1379     BOOLEAN Overflow;
1380 
1381     DebugTrace(+1, Dbg, "FatQueryEaUserEaList...\n", 0);
1382 
1383     LastFullEa = NULL;
1384     NextFullEa = (PFILE_FULL_EA_INFORMATION) UserBuffer;
1385     RemainingUserBufferLength = UserBufferLength;
1386 
1387     Overflow = FALSE;
1388 
1389     for (GetEa = (PFILE_GET_EA_INFORMATION) &UserEaList[0];
1390          GetEa < (PFILE_GET_EA_INFORMATION) ((PUCHAR) UserEaList
1391                                              + UserEaListLength);
1392          GetEa = (GetEa->NextEntryOffset == 0
1393                   ? (PFILE_GET_EA_INFORMATION) MAXUINT_PTR
1394                   : (PFILE_GET_EA_INFORMATION) ((PUCHAR) GetEa
1395                                                 + GetEa->NextEntryOffset))) {
1396 
1397         OEM_STRING Str;
1398         OEM_STRING OutputEaName;
1399 
1400         DebugTrace(0, Dbg, "Top of loop, GetEa = %p\n", GetEa);
1401         DebugTrace(0, Dbg, "LastFullEa = %p\n", LastFullEa);
1402         DebugTrace(0, Dbg, "NextFullEa = %p\n", NextFullEa);
1403         DebugTrace(0, Dbg, "RemainingUserBufferLength = %08lx\n", RemainingUserBufferLength);
1404 
1405         //
1406         //  Make a string reference to the GetEa and see if we can
1407         //  locate the ea by name
1408         //
1409 
1410         Str.MaximumLength = Str.Length = GetEa->EaNameLength;
1411         Str.Buffer = &GetEa->EaName[0];
1412 
1413         //
1414         //  Check for a valid name.
1415         //
1416 
1417         if (!FatIsEaNameValid( IrpContext, Str )) {
1418 
1419             DebugTrace(-1, Dbg,
1420                        "FatQueryEaUserEaList:  Invalid Ea Name -> %Z\n",
1421                        &Str);
1422 
1423             Iosb.Information = (PUCHAR)GetEa - UserEaList;
1424             Iosb.Status = STATUS_INVALID_EA_NAME;
1425             return Iosb;
1426         }
1427 
1428         //
1429         //  If this is a duplicate name, we skip to the next.
1430         //
1431 
1432         if (FatIsDuplicateEaName( IrpContext, GetEa, UserEaList )) {
1433 
1434             DebugTrace(0, Dbg, "FatQueryEaUserEaList:  Duplicate name\n", 0);
1435             continue;
1436         }
1437 
1438         if (!FatLocateEaByName( IrpContext,
1439                                 FirstPackedEa,
1440                                 PackedEasLength,
1441                                 &Str,
1442                                 &Offset )) {
1443 
1444             Offset = 0xffffffff;
1445 
1446             DebugTrace(0, Dbg, "Need to dummy up an ea\n", 0);
1447 
1448             //
1449             //  We were not able to locate the name therefore we must
1450             //  dummy up a entry for the query.  The needed Ea size is
1451             //  the size of the name + 4 (next entry offset) + 1 (flags)
1452             //  + 1 (name length) + 2 (value length) + the name length +
1453             //  1 (null byte).
1454             //
1455 
1456             if ((ULONG)(4+1+1+2+GetEa->EaNameLength+1)
1457                 > RemainingUserBufferLength) {
1458 
1459                 Overflow = TRUE;
1460                 break;
1461             }
1462 
1463             //
1464             //  Everything is going to work fine, so copy over the name,
1465             //  set the name length and zero out the rest of the ea.
1466             //
1467 
1468             NextFullEa->NextEntryOffset = 0;
1469             NextFullEa->Flags = 0;
1470             NextFullEa->EaNameLength = GetEa->EaNameLength;
1471             NextFullEa->EaValueLength = 0;
1472             RtlCopyMemory( &NextFullEa->EaName[0],
1473                            &GetEa->EaName[0],
1474                            GetEa->EaNameLength );
1475 
1476             //
1477             //  Upcase the name in the buffer.
1478             //
1479 
1480             OutputEaName.MaximumLength = OutputEaName.Length = Str.Length;
1481             OutputEaName.Buffer = NextFullEa->EaName;
1482 
1483             FatUpcaseEaName( IrpContext, &OutputEaName, &OutputEaName );
1484 
1485             NextFullEa->EaName[GetEa->EaNameLength] = 0;
1486 
1487         } else {
1488 
1489             DebugTrace(0, Dbg, "Located the ea, Offset = %08lx\n", Offset);
1490 
1491             //
1492             //  We were able to locate the packed ea
1493             //  Reference the packed ea
1494             //
1495 
1496             PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + Offset);
1497             SizeOfPackedEa( PackedEa, &PackedEaSize );
1498 
1499             DebugTrace(0, Dbg, "PackedEaSize = %08lx\n", PackedEaSize);
1500 
1501             //
1502             //  We know that the packed ea is 4 bytes smaller than its
1503             //  equivalent full ea so we need to check the remaining
1504             //  user buffer length against the computed full ea size.
1505             //
1506 
1507             if (PackedEaSize + 4 > RemainingUserBufferLength) {
1508 
1509                 Overflow = TRUE;
1510                 break;
1511             }
1512 
1513             //
1514             //  Everything is going to work fine, so copy over the packed
1515             //  ea to the full ea and zero out the next entry offset field.
1516             //
1517 
1518             RtlCopyMemory( &NextFullEa->Flags,
1519                            &PackedEa->Flags,
1520                            PackedEaSize );
1521 
1522             NextFullEa->NextEntryOffset = 0;
1523         }
1524 
1525         //
1526         //  At this point we've copied a new full ea into the next full ea
1527         //  location.  So now go back and set the set full eas entry offset
1528         //  field to be the difference between out two pointers.
1529         //
1530 
1531         if (LastFullEa != NULL) {
1532 
1533             LastFullEa->NextEntryOffset = (ULONG)((PUCHAR) NextFullEa
1534                                           - (PUCHAR) LastFullEa);
1535         }
1536 
1537         //
1538         //  Set the last full ea to the next full ea, compute
1539         //  where the next full should be, and decrement the remaining user
1540         //  buffer length appropriately
1541         //
1542 
1543         LastFullEa = NextFullEa;
1544         LastFullEaSize = LongAlign( SizeOfFullEa( LastFullEa ));
1545         RemainingUserBufferLength -= LastFullEaSize;
1546         NextFullEa = (PFILE_FULL_EA_INFORMATION) ((PUCHAR) NextFullEa
1547                                                   + LastFullEaSize);
1548 
1549         //
1550         //  Remember the offset of the next ea in case we're asked to
1551         //  resume the iteration
1552         //
1553 
1554         Ccb->OffsetOfNextEaToReturn = FatLocateNextEa( IrpContext,
1555                                                        FirstPackedEa,
1556                                                        PackedEasLength,
1557                                                        Offset );
1558 
1559         //
1560         //  If we were to return a single entry then break out of our loop
1561         //  now
1562         //
1563 
1564         if (ReturnSingleEntry) {
1565 
1566             break;
1567         }
1568     }
1569 
1570     //
1571     //  Now we've iterated all that can and we've exited the preceding loop
1572     //  with either all, some or no information stored in the return buffer.
1573     //  We can decide if we got everything to fit by checking the local
1574     //  Overflow variable
1575     //
1576 
1577     if (Overflow) {
1578 
1579         Iosb.Information = 0;
1580         Iosb.Status = STATUS_BUFFER_OVERFLOW;
1581 
1582     } else {
1583 
1584         //
1585         //  Otherwise we've been successful in returing at least one
1586         //  ea so we'll compute the number of bytes used to store the
1587         //  full ea information.  The number of bytes used is the difference
1588         //  between the LastFullEa and the start of the buffer, and the
1589         //  non-aligned size of the last full ea.
1590         //
1591 
1592         Iosb.Information = ((PUCHAR) LastFullEa - UserBuffer)
1593                             + SizeOfFullEa(LastFullEa);
1594 
1595         Iosb.Status = STATUS_SUCCESS;
1596     }
1597 
1598     DebugTrace(-1, Dbg, "FatQueryEaUserEaList -> Iosb.Status = %08lx\n",
1599                Iosb.Status);
1600 
1601     return Iosb;
1602 }
1603 
1604 
1605 //
1606 //  Local Support Routine
1607 //
1608 
1609 IO_STATUS_BLOCK
1610 FatQueryEaIndexSpecified (
1611     IN PIRP_CONTEXT IrpContext,
1612     OUT PCCB Ccb,
1613     IN PPACKED_EA FirstPackedEa,
1614     IN ULONG PackedEasLength,
1615     OUT PUCHAR UserBuffer,
1616     IN ULONG  UserBufferLength,
1617     IN ULONG  UserEaIndex,
1618     IN BOOLEAN ReturnSingleEntry
1619     )
1620 
1621 /*++
1622 
1623 Routine Description:
1624 
1625     This routine is the work routine for querying EAs given an ea index
1626 
1627 Arguments:
1628 
1629     Ccb - Supplies the Ccb for the query
1630 
1631     FirstPackedEa - Supplies the first ea for the file being queried
1632 
1633     PackedEasLength - Supplies the length of the ea data
1634 
1635     UserBuffer - Supplies the buffer to receive the full eas
1636 
1637     UserBufferLength - Supplies the length, in bytes, of the user buffer
1638 
1639     UserEaIndex - Supplies the index of the first ea to return.
1640 
1641     RestartScan - Indicates if the first item to return is at the
1642                   beginning of the packed ea list or if we should resume our
1643                   previous iteration
1644 
1645 Return Value:
1646 
1647     IO_STATUS_BLOCK - Receives the completion status for the operation
1648 
1649 --*/
1650 
1651 {
1652     IO_STATUS_BLOCK Iosb;
1653 
1654     ULONG i;
1655     ULONG Offset;
1656 
1657     DebugTrace(+1, Dbg, "FatQueryEaIndexSpecified...\n", 0);
1658 
1659     //
1660     //  Zero out the information field of the iosb
1661     //
1662 
1663     Iosb.Information = 0;
1664 
1665     //
1666     //  If the index value is zero or there are no Eas on the file, then
1667     //  the specified index can't be returned.
1668     //
1669 
1670     if (UserEaIndex == 0
1671         || PackedEasLength == 0) {
1672 
1673         DebugTrace( -1, Dbg, "FatQueryEaIndexSpecified: Non-existant entry\n", 0 );
1674 
1675         Iosb.Status = STATUS_NONEXISTENT_EA_ENTRY;
1676 
1677         return Iosb;
1678     }
1679 
1680     //
1681     //  Iterate the eas until we find the index we're after.
1682     //
1683 
1684     for (i = 1, Offset = 0;
1685          (i < UserEaIndex) && (Offset < PackedEasLength);
1686          i += 1, Offset = FatLocateNextEa( IrpContext,
1687                                            FirstPackedEa,
1688                                            PackedEasLength, Offset )) {
1689 
1690         NOTHING;
1691     }
1692 
1693     //
1694     //  Make sure the offset we're given to the ea is a real offset otherwise
1695     //  the ea doesn't exist
1696     //
1697 
1698     if (Offset >= PackedEasLength) {
1699 
1700         //
1701         //  If we just passed the last Ea, we will return STATUS_NO_MORE_EAS.
1702         //  This is for the caller who may be enumerating the Eas.
1703         //
1704 
1705         if (i == UserEaIndex) {
1706 
1707             Iosb.Status = STATUS_NO_MORE_EAS;
1708 
1709         //
1710         //  Otherwise we report that this is a bad ea index.
1711         //
1712 
1713         } else {
1714 
1715             Iosb.Status = STATUS_NONEXISTENT_EA_ENTRY;
1716         }
1717 
1718         DebugTrace(-1, Dbg, "FatQueryEaIndexSpecified -> %08lx\n", Iosb.Status);
1719         return Iosb;
1720     }
1721 
1722     //
1723     //  We now have the offset of the first Ea to return to the user.
1724     //  We simply call our EaSimpleScan routine to do the actual work.
1725     //
1726 
1727     Iosb = FatQueryEaSimpleScan( IrpContext,
1728                                  Ccb,
1729                                  FirstPackedEa,
1730                                  PackedEasLength,
1731                                  UserBuffer,
1732                                  UserBufferLength,
1733                                  ReturnSingleEntry,
1734                                  Offset );
1735 
1736     DebugTrace(-1, Dbg, "FatQueryEaIndexSpecified -> %08lx\n", Iosb.Status);
1737 
1738     return Iosb;
1739 
1740 }
1741 
1742 
1743 //
1744 //  Local Support Routine
1745 //
1746 
1747 IO_STATUS_BLOCK
1748 FatQueryEaSimpleScan (
1749     IN PIRP_CONTEXT IrpContext,
1750     OUT PCCB Ccb,
1751     IN PPACKED_EA FirstPackedEa,
1752     IN ULONG PackedEasLength,
1753     OUT PUCHAR UserBuffer,
1754     IN ULONG  UserBufferLength,
1755     IN BOOLEAN ReturnSingleEntry,
1756     ULONG StartOffset
1757     )
1758 
1759 /*++
1760 
1761 Routine Description:
1762 
1763     This routine is the work routine for querying EAs from the beginning of
1764     the ea list.
1765 
1766 Arguments:
1767 
1768     Ccb - Supplies the Ccb for the query
1769 
1770     FirstPackedEa - Supplies the first ea for the file being queried
1771 
1772     PackedEasLength - Supplies the length of the ea data
1773 
1774     UserBuffer - Supplies the buffer to receive the full eas
1775 
1776     UserBufferLength - Supplies the length, in bytes, of the user buffer
1777 
1778     ReturnSingleEntry - Indicates if we are to return a single entry or not
1779 
1780     StartOffset - Indicates the offset within the Ea data to return the
1781                   first block of data.
1782 
1783 Return Value:
1784 
1785     IO_STATUS_BLOCK - Receives the completion status for the operation
1786 
1787 --*/
1788 
1789 {
1790     IO_STATUS_BLOCK Iosb;
1791 
1792     ULONG RemainingUserBufferLength;
1793 
1794     PPACKED_EA PackedEa;
1795     ULONG PackedEaSize;
1796 
1797     PFILE_FULL_EA_INFORMATION LastFullEa;
1798     ULONG LastFullEaSize;
1799     PFILE_FULL_EA_INFORMATION NextFullEa;
1800     BOOLEAN BufferOverflow = FALSE;
1801 
1802 
1803     DebugTrace(+1, Dbg, "FatQueryEaSimpleScan...\n", 0);
1804 
1805     //
1806     //  Zero out the information field in the Iosb
1807     //
1808 
1809     Iosb.Information = 0;
1810 
1811     LastFullEa = NULL;
1812     NextFullEa = (PFILE_FULL_EA_INFORMATION) UserBuffer;
1813     RemainingUserBufferLength = UserBufferLength;
1814 
1815     while (StartOffset < PackedEasLength) {
1816 
1817         DebugTrace(0, Dbg, "Top of loop, Offset = %08lx\n", StartOffset);
1818         DebugTrace(0, Dbg, "LastFullEa = %p\n", LastFullEa);
1819         DebugTrace(0, Dbg, "NextFullEa = %p\n", NextFullEa);
1820         DebugTrace(0, Dbg, "RemainingUserBufferLength = %08lx\n", RemainingUserBufferLength);
1821 
1822         //
1823         //  Reference the packed ea of interest.
1824         //
1825 
1826         PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + StartOffset);
1827 
1828         SizeOfPackedEa( PackedEa, &PackedEaSize );
1829 
1830         DebugTrace(0, Dbg, "PackedEaSize = %08lx\n", PackedEaSize);
1831 
1832         //
1833         //  We know that the packed ea is 4 bytes smaller than its
1834         //  equivalent full ea so we need to check the remaining
1835         //  user buffer length against the computed full ea size.
1836         //
1837 
1838         if (PackedEaSize + 4 > RemainingUserBufferLength) {
1839 
1840             BufferOverflow = TRUE;
1841             break;
1842         }
1843 
1844         //
1845         //  Everything is going to work fine, so copy over the packed
1846         //  ea to the full ea and zero out the next entry offset field.
1847         //  Then go back and set the last full eas entry offset field
1848         //  to be the difference between the two pointers.
1849         //
1850 
1851         RtlCopyMemory( &NextFullEa->Flags, &PackedEa->Flags, PackedEaSize );
1852         NextFullEa->NextEntryOffset = 0;
1853 
1854         if (LastFullEa != NULL) {
1855 
1856             LastFullEa->NextEntryOffset = (ULONG)((PUCHAR) NextFullEa
1857                                           - (PUCHAR) LastFullEa);
1858         }
1859 
1860         //
1861         //  Set the last full ea to the next full ea, compute
1862         //  where the next full should be, and decrement the remaining user
1863         //  buffer length appropriately
1864         //
1865 
1866         LastFullEa = NextFullEa;
1867         LastFullEaSize = LongAlign( SizeOfFullEa( LastFullEa ));
1868         RemainingUserBufferLength -= LastFullEaSize;
1869         NextFullEa = (PFILE_FULL_EA_INFORMATION) ((PUCHAR) NextFullEa
1870                                                   + LastFullEaSize);
1871 
1872         //
1873         //  Remember the offset of the next ea in case we're asked to
1874         //  resume the teration
1875         //
1876 
1877         StartOffset = FatLocateNextEa( IrpContext,
1878                                        FirstPackedEa,
1879                                        PackedEasLength,
1880                                        StartOffset );
1881 
1882         Ccb->OffsetOfNextEaToReturn = StartOffset;
1883 
1884         //
1885         //  If we were to return a single entry then break out of our loop
1886         //  now
1887         //
1888 
1889         if (ReturnSingleEntry) {
1890 
1891             break;
1892         }
1893     }
1894 
1895     //
1896     //  Now we've iterated all that can and we've exited the preceding loop
1897     //  with either some or no information stored in the return buffer.
1898     //  We can decide which it is by checking if the last full ea is null
1899     //
1900 
1901     if (LastFullEa == NULL) {
1902 
1903         Iosb.Information = 0;
1904 
1905         //
1906         //  We were not able to return a single ea entry, now we need to find
1907         //  out if it is because we didn't have an entry to return or the
1908         //  buffer is too small.  If the Offset variable is less than
1909         //  PackedEaList->UsedSize then the user buffer is too small
1910         //
1911 
1912         if (PackedEasLength == 0) {
1913 
1914             Iosb.Status = STATUS_NO_EAS_ON_FILE;
1915 
1916         } else if (StartOffset >= PackedEasLength) {
1917 
1918             Iosb.Status = STATUS_NO_MORE_EAS;
1919 
1920         } else {
1921 
1922             Iosb.Status = STATUS_BUFFER_TOO_SMALL;
1923         }
1924 
1925     } else {
1926 
1927         //
1928         //  Otherwise we've been successful in returing at least one
1929         //  ea so we'll compute the number of bytes used to store the
1930         //  full ea information.  The number of bytes used is the difference
1931         //  between the LastFullEa and the start of the buffer, and the
1932         //  non-aligned size of the last full ea.
1933         //
1934 
1935         Iosb.Information = ((PUCHAR) LastFullEa - UserBuffer)
1936                             + SizeOfFullEa( LastFullEa );
1937 
1938         //
1939         //  If there are more to return, report the buffer was too small.
1940         //  Otherwise return STATUS_SUCCESS.
1941         //
1942 
1943         if (BufferOverflow) {
1944 
1945             Iosb.Status = STATUS_BUFFER_OVERFLOW;
1946 
1947         } else {
1948 
1949             Iosb.Status = STATUS_SUCCESS;
1950         }
1951     }
1952 
1953     DebugTrace(-1, Dbg, "FatQueryEaSimpleScan -> Iosb.Status = %08lx\n",
1954                Iosb.Status);
1955 
1956     return Iosb;
1957 
1958 }
1959 
1960 
1961 //
1962 //  Local Support Routine
1963 //
1964 
1965 BOOLEAN
1966 FatIsDuplicateEaName (
1967     IN PIRP_CONTEXT IrpContext,
1968     IN PFILE_GET_EA_INFORMATION GetEa,
1969     IN PUCHAR UserBuffer
1970     )
1971 
1972 /*++
1973 
1974 Routine Description:
1975 
1976     This routine walks through a list of ea names to find a duplicate name.
1977     'GetEa' is an actual position in the list.  We are only interested in
1978     previous matching ea names, as the ea information for that ea name
1979     would have been returned with the previous instance.
1980 
1981 Arguments:
1982 
1983     GetEa - Supplies the Ea name structure for the ea name to match.
1984 
1985     UserBuffer - Supplies a pointer to the user buffer with the list
1986                  of ea names to search for.
1987 
1988 Return Value:
1989 
1990     BOOLEAN - TRUE if a previous match is found, FALSE otherwise.
1991 
1992 --*/
1993 
1994 {
1995     PFILE_GET_EA_INFORMATION ThisGetEa;
1996 
1997     BOOLEAN DuplicateFound;
1998     OEM_STRING EaString;
1999 
2000     DebugTrace(+1, Dbg, "FatIsDuplicateEaName...\n", 0);
2001 
2002     EaString.MaximumLength = EaString.Length = GetEa->EaNameLength;
2003     EaString.Buffer = &GetEa->EaName[0];
2004 
2005     FatUpcaseEaName( IrpContext, &EaString, &EaString );
2006 
2007     DuplicateFound = FALSE;
2008 
2009     for (ThisGetEa = (PFILE_GET_EA_INFORMATION) &UserBuffer[0];
2010          ThisGetEa < GetEa
2011          && ThisGetEa->NextEntryOffset != 0;
2012          ThisGetEa = (PFILE_GET_EA_INFORMATION) ((PUCHAR) ThisGetEa
2013                                                  + ThisGetEa->NextEntryOffset)) {
2014 
2015         OEM_STRING Str;
2016 
2017         DebugTrace(0, Dbg, "Top of loop, ThisGetEa = %p\n", ThisGetEa);
2018 
2019         //
2020         //  Make a string reference to the GetEa and see if we can
2021         //  locate the ea by name
2022         //
2023 
2024         Str.MaximumLength = Str.Length = ThisGetEa->EaNameLength;
2025         Str.Buffer = &ThisGetEa->EaName[0];
2026 
2027         DebugTrace(0, Dbg, "FatIsDuplicateEaName:  Next Name -> %Z\n", &Str);
2028 
2029         if ( FatAreNamesEqual(IrpContext, Str, EaString) ) {
2030 
2031             DebugTrace(0, Dbg, "FatIsDuplicateEaName:  Duplicate found\n", 0);
2032             DuplicateFound = TRUE;
2033             break;
2034         }
2035     }
2036 
2037     DebugTrace(-1, Dbg, "FatIsDuplicateEaName:  Exit -> %04x\n", DuplicateFound);
2038 
2039     return DuplicateFound;
2040 }
2041 #endif
2042 
2043 
2044