Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2017 lines
54 KiB

  1. /*++
  2. Copyright (c) 1989-2000 Microsoft Corporation
  3. Module Name:
  4. Ea.c
  5. Abstract:
  6. This module implements the EA routines for Fat called by
  7. the dispatch driver.
  8. // @@BEGIN_DDKSPLIT
  9. Author:
  10. Gary Kimura [GaryKi] 12-Apr-1990
  11. Revision History:
  12. // @@END_DDKSPLIT
  13. --*/
  14. #include "FatProcs.h"
  15. //
  16. // The local debug trace level
  17. //
  18. #define Dbg (DEBUG_TRACE_EA)
  19. //
  20. // Local procedure prototypes
  21. //
  22. IO_STATUS_BLOCK
  23. FatQueryEaUserEaList (
  24. IN PIRP_CONTEXT IrpContext,
  25. OUT PCCB Ccb,
  26. IN PPACKED_EA FirstPackedEa,
  27. IN ULONG PackedEasLength,
  28. OUT PUCHAR UserBuffer,
  29. IN ULONG UserBufferLength,
  30. IN PUCHAR UserEaList,
  31. IN ULONG UserEaListLength,
  32. IN BOOLEAN ReturnSingleEntry
  33. );
  34. IO_STATUS_BLOCK
  35. FatQueryEaIndexSpecified (
  36. IN PIRP_CONTEXT IrpContext,
  37. OUT PCCB Ccb,
  38. IN PPACKED_EA FirstPackedEa,
  39. IN ULONG PackedEasLength,
  40. OUT PUCHAR UserBuffer,
  41. IN ULONG UserBufferLength,
  42. IN ULONG UserEaIndex,
  43. IN BOOLEAN ReturnSingleEntry
  44. );
  45. IO_STATUS_BLOCK
  46. FatQueryEaSimpleScan (
  47. IN PIRP_CONTEXT IrpContext,
  48. OUT PCCB Ccb,
  49. IN PPACKED_EA FirstPackedEa,
  50. IN ULONG PackedEasLength,
  51. OUT PUCHAR UserBuffer,
  52. IN ULONG UserBufferLength,
  53. IN BOOLEAN ReturnSingleEntry,
  54. ULONG StartOffset
  55. );
  56. BOOLEAN
  57. FatIsDuplicateEaName (
  58. IN PIRP_CONTEXT IrpContext,
  59. IN PFILE_GET_EA_INFORMATION GetEa,
  60. IN PUCHAR UserBuffer
  61. );
  62. #ifdef ALLOC_PRAGMA
  63. #pragma alloc_text(PAGE, FatCommonQueryEa)
  64. #pragma alloc_text(PAGE, FatCommonSetEa)
  65. #pragma alloc_text(PAGE, FatFsdQueryEa)
  66. #pragma alloc_text(PAGE, FatFsdSetEa)
  67. #pragma alloc_text(PAGE, FatIsDuplicateEaName)
  68. #pragma alloc_text(PAGE, FatQueryEaIndexSpecified)
  69. #pragma alloc_text(PAGE, FatQueryEaSimpleScan)
  70. #pragma alloc_text(PAGE, FatQueryEaUserEaList)
  71. #endif
  72. NTSTATUS
  73. FatFsdQueryEa (
  74. IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
  75. IN PIRP Irp
  76. )
  77. /*++
  78. Routine Description:
  79. This routine implements the Fsd part of the NtQueryEa API
  80. call.
  81. Arguments:
  82. VolumeDeviceObject - Supplies the volume device object where the file
  83. being queried exists.
  84. Irp - Supplies the Irp being processed.
  85. Return Value:
  86. NTSTATUS - The FSD status for the Irp.
  87. --*/
  88. {
  89. NTSTATUS Status;
  90. PIRP_CONTEXT IrpContext = NULL;
  91. BOOLEAN TopLevel;
  92. DebugTrace(+1, Dbg, "FatFsdQueryEa\n", 0);
  93. //
  94. // Call the common query routine, with blocking allowed if synchronous
  95. //
  96. FsRtlEnterFileSystem();
  97. TopLevel = FatIsIrpTopLevel( Irp );
  98. try {
  99. IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
  100. Status = FatCommonQueryEa( IrpContext, Irp );
  101. } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
  102. //
  103. // We had some trouble trying to perform the requested
  104. // operation, so we'll abort the I/O request with
  105. // the error status that we get back from the
  106. // execption code
  107. //
  108. Status = FatProcessException( IrpContext, Irp, GetExceptionCode() );
  109. }
  110. if (TopLevel) { IoSetTopLevelIrp( NULL ); }
  111. FsRtlExitFileSystem();
  112. //
  113. // And return to our caller
  114. //
  115. DebugTrace(-1, Dbg, "FatFsdQueryEa -> %08lx\n", Status);
  116. UNREFERENCED_PARAMETER( VolumeDeviceObject );
  117. return Status;
  118. }
  119. NTSTATUS
  120. FatFsdSetEa (
  121. IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
  122. IN PIRP Irp
  123. )
  124. /*++
  125. Routine Description:
  126. This routine implements the FSD part of the NtSetEa API
  127. call.
  128. Arguments:
  129. VolumeDeviceObject - Supplies the volume device object where the file
  130. being set exists.
  131. Irp - Supplies the Irp being processed.
  132. Return Value:
  133. NTSTATUS - The FSD status for the Irp.
  134. --*/
  135. {
  136. NTSTATUS Status;
  137. PIRP_CONTEXT IrpContext = NULL;
  138. BOOLEAN TopLevel;
  139. DebugTrace(+1, Dbg, "FatFsdSetEa\n", 0);
  140. //
  141. // Call the common set routine, with blocking allowed if synchronous
  142. //
  143. FsRtlEnterFileSystem();
  144. TopLevel = FatIsIrpTopLevel( Irp );
  145. try {
  146. IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
  147. Status = FatCommonSetEa( IrpContext, Irp );
  148. } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
  149. //
  150. // We had some trouble trying to perform the requested
  151. // operation, so we'll abort the I/O request with
  152. // the error status that we get back from the
  153. // execption code
  154. //
  155. Status = FatProcessException( IrpContext, Irp, GetExceptionCode() );
  156. }
  157. if (TopLevel) { IoSetTopLevelIrp( NULL ); }
  158. FsRtlExitFileSystem();
  159. //
  160. // And return to our caller
  161. //
  162. DebugTrace(-1, Dbg, "FatFsdSetEa -> %08lx\n", Status);
  163. UNREFERENCED_PARAMETER( VolumeDeviceObject );
  164. return Status;
  165. }
  166. NTSTATUS
  167. FatCommonQueryEa (
  168. IN PIRP_CONTEXT IrpContext,
  169. IN PIRP Irp
  170. )
  171. /*++
  172. Routine Description:
  173. This is the common routine for querying File ea called by both
  174. the fsd and fsp threads.
  175. Arguments:
  176. Irp - Supplies the Irp being processed
  177. Return Value:
  178. NTSTATUS - The return status for the operation
  179. --*/
  180. {
  181. PIO_STACK_LOCATION IrpSp;
  182. NTSTATUS Status;
  183. PUCHAR Buffer;
  184. ULONG UserBufferLength;
  185. PUCHAR UserEaList;
  186. ULONG UserEaListLength;
  187. ULONG UserEaIndex;
  188. BOOLEAN RestartScan;
  189. BOOLEAN ReturnSingleEntry;
  190. BOOLEAN IndexSpecified;
  191. PVCB Vcb;
  192. PCCB Ccb;
  193. PFCB Fcb;
  194. PDIRENT Dirent;
  195. PBCB Bcb;
  196. PDIRENT EaDirent;
  197. PBCB EaBcb;
  198. BOOLEAN LockedEaFcb;
  199. PEA_SET_HEADER EaSetHeader;
  200. EA_RANGE EaSetRange;
  201. USHORT ExtendedAttributes;
  202. //
  203. // Get the current Irp stack location
  204. //
  205. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  206. DebugTrace(+1, Dbg, "FatCommonQueryEa...\n", 0);
  207. DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT));
  208. DebugTrace( 0, Dbg, " Irp = %08lx\n", Irp );
  209. DebugTrace( 0, Dbg, " ->SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer );
  210. DebugTrace( 0, Dbg, " ->Length = %08lx\n", IrpSp->Parameters.QueryEa.Length );
  211. DebugTrace( 0, Dbg, " ->EaList = %08lx\n", IrpSp->Parameters.QueryEa.EaList );
  212. DebugTrace( 0, Dbg, " ->EaListLength = %08lx\n", IrpSp->Parameters.QueryEa.EaListLength );
  213. DebugTrace( 0, Dbg, " ->EaIndex = %08lx\n", IrpSp->Parameters.QueryEa.EaIndex );
  214. DebugTrace( 0, Dbg, " ->RestartScan = %08lx\n", FlagOn(IrpSp->Flags, SL_RESTART_SCAN));
  215. DebugTrace( 0, Dbg, " ->ReturnSingleEntry = %08lx\n", FlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY));
  216. DebugTrace( 0, Dbg, " ->IndexSpecified = %08lx\n", FlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED));
  217. Irp->IoStatus.Status = STATUS_SUCCESS;
  218. Irp->IoStatus.Information = 0;
  219. //
  220. // Check that the file object is associated with either a user file
  221. // or directory open. We don't allow Ea operations on the root
  222. // directory.
  223. //
  224. {
  225. TYPE_OF_OPEN OpenType;
  226. if (((OpenType = FatDecodeFileObject( IrpSp->FileObject,
  227. &Vcb,
  228. &Fcb,
  229. &Ccb )) != UserFileOpen
  230. && OpenType != UserDirectoryOpen) ||
  231. (NodeType( Fcb )) == FAT_NTC_ROOT_DCB) {
  232. FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  233. DebugTrace(-1, Dbg,
  234. "FatCommonQueryEa -> %08lx\n",
  235. STATUS_INVALID_PARAMETER);
  236. return STATUS_INVALID_PARAMETER;
  237. }
  238. }
  239. //
  240. // Fat32 does not support ea's.
  241. //
  242. if (FatIsFat32(Vcb)) {
  243. FatCompleteRequest( IrpContext, Irp, STATUS_EAS_NOT_SUPPORTED );
  244. DebugTrace(-1, Dbg,
  245. "FatCommonQueryEa -> %08lx\n",
  246. STATUS_EAS_NOT_SUPPORTED);
  247. return STATUS_EAS_NOT_SUPPORTED;
  248. }
  249. //
  250. // Acquire shared access to the Fcb and enqueue the Irp if we didn't
  251. // get access.
  252. //
  253. if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
  254. DebugTrace(0, Dbg, "FatCommonQueryEa: Thread can't wait\n", 0);
  255. Status = FatFsdPostRequest( IrpContext, Irp );
  256. DebugTrace(-1, Dbg, "FatCommonQueryEa -> %08lx\n", Status );
  257. return Status;
  258. }
  259. FatAcquireSharedFcb( IrpContext, Fcb );
  260. //
  261. // Reference our input parameters to make things easier
  262. //
  263. UserBufferLength = IrpSp->Parameters.QueryEa.Length;
  264. UserEaList = IrpSp->Parameters.QueryEa.EaList;
  265. UserEaListLength = IrpSp->Parameters.QueryEa.EaListLength;
  266. UserEaIndex = IrpSp->Parameters.QueryEa.EaIndex;
  267. RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN);
  268. ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY);
  269. IndexSpecified = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED);
  270. //
  271. // Initialize our local values.
  272. //
  273. LockedEaFcb = FALSE;
  274. Bcb = NULL;
  275. EaBcb = NULL;
  276. Status = STATUS_SUCCESS;
  277. RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
  278. try {
  279. PPACKED_EA FirstPackedEa;
  280. ULONG PackedEasLength;
  281. Buffer = FatMapUserBuffer( IrpContext, Irp );
  282. //
  283. // We verify that the Fcb is still valid.
  284. //
  285. FatVerifyFcb( IrpContext, Fcb );
  286. //
  287. // We need to get the dirent for the Fcb to recover the Ea handle.
  288. //
  289. FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb );
  290. //
  291. // Verify that the Ea file is in a consistant state. If the
  292. // Ea modification count in the Fcb doesn't match that in
  293. // the CCB, then the Ea file has been changed from under
  294. // us. If we are not starting the search from the beginning
  295. // of the Ea set, we return an error.
  296. //
  297. if (UserEaList == NULL
  298. && Ccb->OffsetOfNextEaToReturn != 0
  299. && !IndexSpecified
  300. && !RestartScan
  301. && Fcb->EaModificationCount != Ccb->EaModificationCount) {
  302. DebugTrace(0, Dbg,
  303. "FatCommonQueryEa: Ea file in unknown state\n", 0);
  304. Status = STATUS_EA_CORRUPT_ERROR;
  305. try_return( Status );
  306. }
  307. //
  308. // Show that the Ea's for this file are consistant for this
  309. // file handle.
  310. //
  311. Ccb->EaModificationCount = Fcb->EaModificationCount;
  312. //
  313. // If the handle value is 0, then the file has no Eas. We dummy up
  314. // an ea list to use below.
  315. //
  316. ExtendedAttributes = Dirent->ExtendedAttributes;
  317. FatUnpinBcb( IrpContext, Bcb );
  318. if (ExtendedAttributes == 0) {
  319. DebugTrace(0, Dbg,
  320. "FatCommonQueryEa: Zero handle, no Ea's for this file\n", 0);
  321. FirstPackedEa = (PPACKED_EA) NULL;
  322. PackedEasLength = 0;
  323. } else {
  324. //
  325. // We need to get the Ea file for this volume. If the
  326. // operation doesn't complete due to blocking, then queue the
  327. // Irp to the Fsp.
  328. //
  329. FatGetEaFile( IrpContext,
  330. Vcb,
  331. &EaDirent,
  332. &EaBcb,
  333. FALSE,
  334. FALSE );
  335. LockedEaFcb = TRUE;
  336. //
  337. // If the above operation completed and the Ea file did not exist,
  338. // the disk has been corrupted. There is an existing Ea handle
  339. // without any Ea data.
  340. //
  341. if (Vcb->VirtualEaFile == NULL) {
  342. DebugTrace(0, Dbg,
  343. "FatCommonQueryEa: No Ea file found when expected\n", 0);
  344. Status = STATUS_NO_EAS_ON_FILE;
  345. try_return( Status );
  346. }
  347. //
  348. // We need to try to get the Ea set for the desired file. If
  349. // blocking is necessary then we'll post the request to the Fsp.
  350. //
  351. FatReadEaSet( IrpContext,
  352. Vcb,
  353. ExtendedAttributes,
  354. &Fcb->ShortName.Name.Oem,
  355. TRUE,
  356. &EaSetRange );
  357. EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
  358. //
  359. // Find the start and length of the Eas.
  360. //
  361. FirstPackedEa = (PPACKED_EA) EaSetHeader->PackedEas;
  362. PackedEasLength = GetcbList( EaSetHeader ) - 4;
  363. }
  364. //
  365. // Protect our access to the user buffer since IO dosn't do this
  366. // for us in this path unless we had specified that our driver
  367. // requires buffering for these large requests. We don't, so ...
  368. //
  369. try {
  370. //
  371. // Let's clear the output buffer.
  372. //
  373. RtlZeroMemory( Buffer, UserBufferLength );
  374. //
  375. // We now satisfy the user's request depending on whether he
  376. // specified an Ea name list, an Ea index or restarting the
  377. // search.
  378. //
  379. //
  380. // The user has supplied a list of Ea names.
  381. //
  382. if (UserEaList != NULL) {
  383. Irp->IoStatus = FatQueryEaUserEaList( IrpContext,
  384. Ccb,
  385. FirstPackedEa,
  386. PackedEasLength,
  387. Buffer,
  388. UserBufferLength,
  389. UserEaList,
  390. UserEaListLength,
  391. ReturnSingleEntry );
  392. //
  393. // The user supplied an index into the Ea list.
  394. //
  395. } else if (IndexSpecified) {
  396. Irp->IoStatus = FatQueryEaIndexSpecified( IrpContext,
  397. Ccb,
  398. FirstPackedEa,
  399. PackedEasLength,
  400. Buffer,
  401. UserBufferLength,
  402. UserEaIndex,
  403. ReturnSingleEntry );
  404. //
  405. // Else perform a simple scan, taking into account the restart
  406. // flag and the position of the next Ea stored in the Ccb.
  407. //
  408. } else {
  409. Irp->IoStatus = FatQueryEaSimpleScan( IrpContext,
  410. Ccb,
  411. FirstPackedEa,
  412. PackedEasLength,
  413. Buffer,
  414. UserBufferLength,
  415. ReturnSingleEntry,
  416. RestartScan
  417. ? 0
  418. : Ccb->OffsetOfNextEaToReturn );
  419. }
  420. } except (!FsRtlIsNtstatusExpected(GetExceptionCode()) ?
  421. EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
  422. //
  423. // We must have had a problem filling in the user's buffer, so fail.
  424. //
  425. Irp->IoStatus.Status = GetExceptionCode();
  426. Irp->IoStatus.Information = 0;
  427. }
  428. Status = Irp->IoStatus.Status;
  429. try_exit: NOTHING;
  430. } finally {
  431. DebugUnwind( FatCommonQueryEa );
  432. //
  433. // Release the Fcb for the file object, and the Ea Fcb if
  434. // successfully locked.
  435. //
  436. FatReleaseFcb( IrpContext, Fcb );
  437. if (LockedEaFcb) {
  438. FatReleaseFcb( IrpContext, Vcb->EaFcb );
  439. }
  440. //
  441. // Unpin the dirents for the Fcb, EaFcb and EaSetFcb if necessary.
  442. //
  443. FatUnpinBcb( IrpContext, Bcb );
  444. FatUnpinBcb( IrpContext, EaBcb );
  445. FatUnpinEaRange( IrpContext, &EaSetRange );
  446. if (!AbnormalTermination()) {
  447. FatCompleteRequest( IrpContext, Irp, Status );
  448. }
  449. DebugTrace(-1, Dbg, "FatCommonQueryEa -> %08lx\n", Status);
  450. }
  451. return Status;
  452. }
  453. NTSTATUS
  454. FatCommonSetEa (
  455. IN PIRP_CONTEXT IrpContext,
  456. IN PIRP Irp
  457. )
  458. /*++
  459. Routine Description:
  460. This routine implements the common Set Ea File Api called by the
  461. the Fsd and Fsp threads
  462. Arguments:
  463. Irp - Supplies the Irp to process
  464. Return Value:
  465. NTSTATUS - The appropriate status for the Irp
  466. --*/
  467. {
  468. PIO_STACK_LOCATION IrpSp;
  469. NTSTATUS Status;
  470. USHORT ExtendedAttributes;
  471. PUCHAR Buffer;
  472. ULONG UserBufferLength;
  473. PVCB Vcb;
  474. PCCB Ccb;
  475. PFCB Fcb;
  476. PDIRENT Dirent;
  477. PBCB Bcb = NULL;
  478. PDIRENT EaDirent = NULL;
  479. PBCB EaBcb = NULL;
  480. PEA_SET_HEADER EaSetHeader = NULL;
  481. PEA_SET_HEADER PrevEaSetHeader;
  482. PEA_SET_HEADER NewEaSetHeader;
  483. EA_RANGE EaSetRange;
  484. BOOLEAN AcquiredVcb = FALSE;
  485. BOOLEAN AcquiredFcb = FALSE;
  486. BOOLEAN AcquiredParentDcb = FALSE;
  487. BOOLEAN AcquiredRootDcb = FALSE;
  488. BOOLEAN AcquiredEaFcb = FALSE;
  489. //
  490. // The following booleans are used in the unwind process.
  491. //
  492. //
  493. // Get the current Irp stack location
  494. //
  495. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  496. DebugTrace(+1, Dbg, "FatCommonSetEa...\n", 0);
  497. DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT));
  498. DebugTrace( 0, Dbg, " Irp = %08lx\n", Irp );
  499. DebugTrace( 0, Dbg, " ->SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer );
  500. DebugTrace( 0, Dbg, " ->Length = %08lx\n", IrpSp->Parameters.SetEa.Length );
  501. Irp->IoStatus.Status = STATUS_SUCCESS;
  502. Irp->IoStatus.Information = 0;
  503. //
  504. // Check that the file object is associated with either a user file
  505. // or directory open.
  506. //
  507. {
  508. TYPE_OF_OPEN OpenType;
  509. if (((OpenType = FatDecodeFileObject( IrpSp->FileObject,
  510. &Vcb,
  511. &Fcb,
  512. &Ccb )) != UserFileOpen
  513. && OpenType != UserDirectoryOpen) ||
  514. (NodeType( Fcb )) == FAT_NTC_ROOT_DCB) {
  515. FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  516. DebugTrace(-1, Dbg,
  517. "FatCommonSetEa -> %08lx\n",
  518. STATUS_INVALID_PARAMETER);
  519. return STATUS_INVALID_PARAMETER;
  520. }
  521. }
  522. //
  523. // Fat32 does not support ea's.
  524. //
  525. if (FatIsFat32(Vcb)) {
  526. FatCompleteRequest( IrpContext, Irp, STATUS_EAS_NOT_SUPPORTED );
  527. DebugTrace(-1, Dbg,
  528. "FatCommonSetEa -> %08lx\n",
  529. STATUS_EAS_NOT_SUPPORTED);
  530. return STATUS_EAS_NOT_SUPPORTED;
  531. }
  532. //
  533. // Reference our input parameters to make things easier
  534. //
  535. UserBufferLength = IrpSp->Parameters.SetEa.Length;
  536. //
  537. // Since we ask for no outside help (direct or buffered IO), it
  538. // is our responsibility to insulate ourselves from the
  539. // deviousness of the user above. Now, buffer and validate the
  540. // contents.
  541. //
  542. Buffer = FatBufferUserBuffer( IrpContext, Irp, UserBufferLength );
  543. //
  544. // Check the validity of the buffer with the new eas. We really
  545. // need to do this always since we don't know, if it was already
  546. // buffered, that we buffered and checked it or some overlying
  547. // filter buffered without checking.
  548. //
  549. Status = IoCheckEaBufferValidity( (PFILE_FULL_EA_INFORMATION) Buffer,
  550. UserBufferLength,
  551. (PULONG)&Irp->IoStatus.Information );
  552. if (!NT_SUCCESS( Status )) {
  553. FatCompleteRequest( IrpContext, Irp, Status );
  554. DebugTrace(-1, Dbg,
  555. "FatCommonSetEa -> %08lx\n",
  556. Status);
  557. return Status;
  558. }
  559. //
  560. // Acquire exclusive access to the Fcb. If this is a write-through operation
  561. // we will need to pick up the other possible streams that can be modified in
  562. // this operation so that the locking order is preserved - the root directory
  563. // (dirent addition if EA database doesn't already exist) and the parent
  564. // directory (addition of the EA handle to the object's dirent).
  565. //
  566. // We are primarily synchronizing with directory enumeration here.
  567. //
  568. // If we cannot wait need to send things off to the fsp.
  569. //
  570. if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
  571. DebugTrace(0, Dbg, "FatCommonSetEa: Set Ea must be waitable\n", 0);
  572. Status = FatFsdPostRequest( IrpContext, Irp );
  573. DebugTrace(-1, Dbg, "FatCommonSetEa -> %08lx\n", Status );
  574. return Status;
  575. }
  576. //
  577. // Set this handle as having modified the file
  578. //
  579. IrpSp->FileObject->Flags |= FO_FILE_MODIFIED;
  580. RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
  581. try {
  582. ULONG PackedEasLength;
  583. BOOLEAN PreviousEas;
  584. ULONG AllocationLength;
  585. ULONG BytesPerCluster;
  586. USHORT EaHandle;
  587. PFILE_FULL_EA_INFORMATION FullEa;
  588. //
  589. // Now go pick up everything
  590. //
  591. FatAcquireSharedVcb( IrpContext, Fcb->Vcb );
  592. AcquiredVcb = TRUE;
  593. FatAcquireExclusiveFcb( IrpContext, Fcb );
  594. AcquiredFcb = TRUE;
  595. if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH)) {
  596. if (Fcb->ParentDcb) {
  597. FatAcquireExclusiveFcb( IrpContext, Fcb->ParentDcb );
  598. AcquiredParentDcb = TRUE;
  599. }
  600. FatAcquireExclusiveFcb( IrpContext, Fcb->Vcb->RootDcb );
  601. AcquiredRootDcb = TRUE;
  602. }
  603. //
  604. // We verify that the Fcb is still valid.
  605. //
  606. FatVerifyFcb( IrpContext, Fcb );
  607. //
  608. // We need to get the dirent for the Fcb to recover the Ea handle.
  609. //
  610. FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb );
  611. DebugTrace(0, Dbg, "FatCommonSetEa: Dirent Address -> %08lx\n",
  612. Dirent );
  613. DebugTrace(0, Dbg, "FatCommonSetEa: Dirent Bcb -> %08lx\n",
  614. Bcb);
  615. //
  616. // If the handle value is 0, then the file has no Eas. In that
  617. // case we allocate memory to hold the Eas to be added. If there
  618. // are existing Eas for the file, then we must read from the
  619. // file and copy the Eas.
  620. //
  621. ExtendedAttributes = Dirent->ExtendedAttributes;
  622. FatUnpinBcb( IrpContext, Bcb );
  623. if (ExtendedAttributes == 0) {
  624. PreviousEas = FALSE;
  625. DebugTrace(0, Dbg,
  626. "FatCommonSetEa: File has no current Eas\n", 0 );
  627. } else {
  628. PreviousEas = TRUE;
  629. DebugTrace(0, Dbg, "FatCommonSetEa: File has previous Eas\n", 0 );
  630. FatGetEaFile( IrpContext,
  631. Vcb,
  632. &EaDirent,
  633. &EaBcb,
  634. FALSE,
  635. TRUE );
  636. AcquiredEaFcb = TRUE;
  637. //
  638. // If we didn't get the file then there is an error on
  639. // the disk.
  640. //
  641. if (Vcb->VirtualEaFile == NULL) {
  642. Status = STATUS_NO_EAS_ON_FILE;
  643. try_return( Status );
  644. }
  645. }
  646. DebugTrace(0, Dbg, "FatCommonSetEa: EaBcb -> %08lx\n", EaBcb);
  647. DebugTrace(0, Dbg, "FatCommonSetEa: EaDirent -> %08lx\n", EaDirent);
  648. //
  649. // If the file has existing ea's, we need to read them to
  650. // determine the size of the buffer allocation.
  651. //
  652. if (PreviousEas) {
  653. //
  654. // We need to try to get the Ea set for the desired file.
  655. //
  656. FatReadEaSet( IrpContext,
  657. Vcb,
  658. ExtendedAttributes,
  659. &Fcb->ShortName.Name.Oem,
  660. TRUE,
  661. &EaSetRange );
  662. PrevEaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
  663. //
  664. // We now must allocate pool memory for our copy of the
  665. // EaSetHeader and then copy the Ea data into it. At that
  666. // time we can unpin the EaSet.
  667. //
  668. PackedEasLength = GetcbList( PrevEaSetHeader ) - 4;
  669. //
  670. // Else we will create a dummy EaSetHeader.
  671. //
  672. } else {
  673. PackedEasLength = 0;
  674. }
  675. BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
  676. AllocationLength = (PackedEasLength
  677. + SIZE_OF_EA_SET_HEADER
  678. + BytesPerCluster - 1)
  679. & ~(BytesPerCluster - 1);
  680. EaSetHeader = FsRtlAllocatePoolWithTag( PagedPool,
  681. AllocationLength,
  682. TAG_EA_SET_HEADER );
  683. //
  684. // Copy the existing Eas over to pool memory.
  685. //
  686. if (PreviousEas) {
  687. RtlCopyMemory( EaSetHeader, PrevEaSetHeader, AllocationLength );
  688. FatUnpinEaRange( IrpContext, &EaSetRange );
  689. } else {
  690. RtlZeroMemory( EaSetHeader, AllocationLength );
  691. RtlCopyMemory( EaSetHeader->OwnerFileName,
  692. Fcb->ShortName.Name.Oem.Buffer,
  693. Fcb->ShortName.Name.Oem.Length );
  694. }
  695. AllocationLength -= SIZE_OF_EA_SET_HEADER;
  696. DebugTrace(0, Dbg, "FatCommonSetEa: Initial Ea set -> %08lx\n",
  697. EaSetHeader);
  698. //
  699. // At this point we have either read in the current eas for the file
  700. // or we have initialized a new empty buffer for the eas. Now for
  701. // each full ea in the input user buffer we do the specified operation
  702. // on the ea
  703. //
  704. for (FullEa = (PFILE_FULL_EA_INFORMATION) Buffer;
  705. FullEa < (PFILE_FULL_EA_INFORMATION) &Buffer[UserBufferLength];
  706. FullEa = (PFILE_FULL_EA_INFORMATION) (FullEa->NextEntryOffset == 0 ?
  707. &Buffer[UserBufferLength] :
  708. (PUCHAR) FullEa + FullEa->NextEntryOffset)) {
  709. OEM_STRING EaName;
  710. ULONG Offset;
  711. EaName.MaximumLength = EaName.Length = FullEa->EaNameLength;
  712. EaName.Buffer = &FullEa->EaName[0];
  713. DebugTrace(0, Dbg, "FatCommonSetEa: Next Ea name -> %Z\n",
  714. &EaName);
  715. //
  716. // Make sure the ea name is valid
  717. //
  718. if (!FatIsEaNameValid( IrpContext,EaName )) {
  719. Irp->IoStatus.Information = (PUCHAR)FullEa - Buffer;
  720. Status = STATUS_INVALID_EA_NAME;
  721. try_return( Status );
  722. }
  723. //
  724. // Check that no invalid ea flags are set.
  725. //
  726. //
  727. // TEMPCODE We are returning STATUS_INVALID_EA_NAME
  728. // until a more appropriate error code exists.
  729. //
  730. if (FullEa->Flags != 0
  731. && FullEa->Flags != FILE_NEED_EA) {
  732. Irp->IoStatus.Information = (PUCHAR)FullEa - (PUCHAR)Buffer;
  733. try_return( Status = STATUS_INVALID_EA_NAME );
  734. }
  735. //
  736. // See if we can locate the ea name in the ea set
  737. //
  738. if (FatLocateEaByName( IrpContext,
  739. (PPACKED_EA) EaSetHeader->PackedEas,
  740. PackedEasLength,
  741. &EaName,
  742. &Offset )) {
  743. DebugTrace(0, Dbg, "FatCommonSetEa: Found Ea name\n", 0);
  744. //
  745. // We found the ea name so now delete the current entry,
  746. // and if the new ea value length is not zero then we
  747. // replace if with the new ea
  748. //
  749. FatDeletePackedEa( IrpContext,
  750. EaSetHeader,
  751. &PackedEasLength,
  752. Offset );
  753. }
  754. if (FullEa->EaValueLength != 0) {
  755. FatAppendPackedEa( IrpContext,
  756. &EaSetHeader,
  757. &PackedEasLength,
  758. &AllocationLength,
  759. FullEa,
  760. BytesPerCluster );
  761. }
  762. }
  763. //
  764. // If there are any ea's not removed, we
  765. // call 'AddEaSet' to insert them into the Fat chain.
  766. //
  767. if (PackedEasLength != 0) {
  768. LARGE_INTEGER EaOffset;
  769. EaOffset.HighPart = 0;
  770. //
  771. // If the packed eas length (plus 4 bytes) is greater
  772. // than the maximum allowed ea size, we return an error.
  773. //
  774. if (PackedEasLength + 4 > MAXIMUM_EA_SIZE) {
  775. DebugTrace( 0, Dbg, "Ea length is greater than maximum\n", 0 );
  776. try_return( Status = STATUS_EA_TOO_LARGE );
  777. }
  778. //
  779. // We need to now read the ea file if we haven't already.
  780. //
  781. if (EaDirent == NULL) {
  782. FatGetEaFile( IrpContext,
  783. Vcb,
  784. &EaDirent,
  785. &EaBcb,
  786. TRUE,
  787. TRUE );
  788. AcquiredEaFcb = TRUE;
  789. }
  790. FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb );
  791. RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
  792. FatAddEaSet( IrpContext,
  793. Vcb,
  794. PackedEasLength + SIZE_OF_EA_SET_HEADER,
  795. EaBcb,
  796. EaDirent,
  797. &EaHandle,
  798. &EaSetRange );
  799. NewEaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
  800. DebugTrace(0, Dbg, "FatCommonSetEa: Adding an ea set\n", 0);
  801. //
  802. // Store the length of the new Ea's into the EaSetHeader.
  803. // This is the PackedEasLength + 4.
  804. //
  805. PackedEasLength += 4;
  806. CopyU4char( EaSetHeader->cbList, &PackedEasLength );
  807. //
  808. // Copy all but the first four bytes of EaSetHeader into
  809. // NewEaSetHeader. The signature and index fields have
  810. // already been filled in.
  811. //
  812. RtlCopyMemory( &NewEaSetHeader->NeedEaCount,
  813. &EaSetHeader->NeedEaCount,
  814. PackedEasLength + SIZE_OF_EA_SET_HEADER - 8 );
  815. FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange );
  816. FatUnpinEaRange( IrpContext, &EaSetRange );
  817. CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
  818. } else {
  819. FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb );
  820. EaHandle = 0;
  821. }
  822. //
  823. // Now we do a wholesale replacement of the ea for the file
  824. //
  825. if (PreviousEas) {
  826. FatDeleteEaSet( IrpContext,
  827. Vcb,
  828. EaBcb,
  829. EaDirent,
  830. ExtendedAttributes,
  831. &Fcb->ShortName.Name.Oem );
  832. CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
  833. }
  834. if (PackedEasLength != 0 ) {
  835. Fcb->EaModificationCount++;
  836. }
  837. //
  838. // Mark the dirent with the new ea's
  839. //
  840. Dirent->ExtendedAttributes = EaHandle;
  841. FatSetDirtyBcb( IrpContext, Bcb, Vcb, TRUE );
  842. //
  843. // We call the notify package to report that the ea's were
  844. // modified.
  845. //
  846. FatNotifyReportChange( IrpContext,
  847. Vcb,
  848. Fcb,
  849. FILE_NOTIFY_CHANGE_EA,
  850. FILE_ACTION_MODIFIED );
  851. Irp->IoStatus.Information = 0;
  852. Status = STATUS_SUCCESS;
  853. try_exit: NOTHING;
  854. //
  855. // Unpin the dirents for the Fcb and EaFcb if necessary.
  856. //
  857. FatUnpinBcb( IrpContext, Bcb );
  858. FatUnpinBcb( IrpContext, EaBcb );
  859. FatUnpinRepinnedBcbs( IrpContext );
  860. } finally {
  861. DebugUnwind( FatCommonSetEa );
  862. //
  863. // If this is an abnormal termination, we need to clean up
  864. // any locked resources.
  865. //
  866. if (AbnormalTermination()) {
  867. //
  868. // Unpin the dirents for the Fcb, EaFcb and EaSetFcb if necessary.
  869. //
  870. FatUnpinBcb( IrpContext, Bcb );
  871. FatUnpinBcb( IrpContext, EaBcb );
  872. FatUnpinEaRange( IrpContext, &EaSetRange );
  873. }
  874. //
  875. // Release the Fcbs/Vcb acquired.
  876. //
  877. if (AcquiredEaFcb) {
  878. FatReleaseFcb( IrpContext, Vcb->EaFcb );
  879. }
  880. if (AcquiredFcb) {
  881. FatReleaseFcb( IrpContext, Fcb );
  882. }
  883. if (AcquiredParentDcb) {
  884. FatReleaseFcb( IrpContext, Fcb->ParentDcb );
  885. }
  886. if (AcquiredRootDcb) {
  887. FatReleaseFcb( IrpContext, Fcb->Vcb->RootDcb );
  888. }
  889. if (AcquiredVcb) {
  890. FatReleaseVcb( IrpContext, Fcb->Vcb );
  891. }
  892. //
  893. // Deallocate our Ea buffer.
  894. //
  895. if (EaSetHeader != NULL) {
  896. ExFreePool( EaSetHeader );
  897. }
  898. //
  899. // Complete the irp.
  900. //
  901. if (!AbnormalTermination()) {
  902. FatCompleteRequest( IrpContext, Irp, Status );
  903. }
  904. DebugTrace(-1, Dbg, "FatCommonSetEa -> %08lx\n", Status);
  905. }
  906. //
  907. // And return to our caller
  908. //
  909. return Status;
  910. }
  911. //
  912. // Local Support Routine
  913. //
  914. IO_STATUS_BLOCK
  915. FatQueryEaUserEaList (
  916. IN PIRP_CONTEXT IrpContext,
  917. OUT PCCB Ccb,
  918. IN PPACKED_EA FirstPackedEa,
  919. IN ULONG PackedEasLength,
  920. OUT PUCHAR UserBuffer,
  921. IN ULONG UserBufferLength,
  922. IN PUCHAR UserEaList,
  923. IN ULONG UserEaListLength,
  924. IN BOOLEAN ReturnSingleEntry
  925. )
  926. /*++
  927. Routine Description:
  928. This routine is the work routine for querying EAs given an ea index
  929. Arguments:
  930. Ccb - Supplies the Ccb for the query
  931. FirstPackedEa - Supplies the first ea for the file being queried
  932. PackedEasLength - Supplies the length of the ea data
  933. UserBuffer - Supplies the buffer to receive the full eas
  934. UserBufferLength - Supplies the length, in bytes, of the user buffer
  935. UserEaList - Supplies the user specified ea name list
  936. UserEaListLength - Supplies the length, in bytes, of the user ea list
  937. ReturnSingleEntry - Indicates if we are to return a single entry or not
  938. Return Value:
  939. IO_STATUS_BLOCK - Receives the completion status for the operation
  940. --*/
  941. {
  942. IO_STATUS_BLOCK Iosb;
  943. ULONG Offset;
  944. ULONG RemainingUserBufferLength;
  945. PPACKED_EA PackedEa;
  946. ULONG PackedEaSize;
  947. PFILE_FULL_EA_INFORMATION LastFullEa;
  948. ULONG LastFullEaSize;
  949. PFILE_FULL_EA_INFORMATION NextFullEa;
  950. PFILE_GET_EA_INFORMATION GetEa;
  951. BOOLEAN Overflow;
  952. DebugTrace(+1, Dbg, "FatQueryEaUserEaList...\n", 0);
  953. LastFullEa = NULL;
  954. NextFullEa = (PFILE_FULL_EA_INFORMATION) UserBuffer;
  955. RemainingUserBufferLength = UserBufferLength;
  956. Overflow = FALSE;
  957. for (GetEa = (PFILE_GET_EA_INFORMATION) &UserEaList[0];
  958. GetEa < (PFILE_GET_EA_INFORMATION) ((PUCHAR) UserEaList
  959. + UserEaListLength);
  960. GetEa = (GetEa->NextEntryOffset == 0
  961. ? (PFILE_GET_EA_INFORMATION) MAXUINT_PTR
  962. : (PFILE_GET_EA_INFORMATION) ((PUCHAR) GetEa
  963. + GetEa->NextEntryOffset))) {
  964. OEM_STRING Str;
  965. OEM_STRING OutputEaName;
  966. DebugTrace(0, Dbg, "Top of loop, GetEa = %08lx\n", GetEa);
  967. DebugTrace(0, Dbg, "LastFullEa = %08lx\n", LastFullEa);
  968. DebugTrace(0, Dbg, "NextFullEa = %08lx\n", NextFullEa);
  969. DebugTrace(0, Dbg, "RemainingUserBufferLength = %08lx\n", RemainingUserBufferLength);
  970. //
  971. // Make a string reference to the GetEa and see if we can
  972. // locate the ea by name
  973. //
  974. Str.MaximumLength = Str.Length = GetEa->EaNameLength;
  975. Str.Buffer = &GetEa->EaName[0];
  976. //
  977. // Check for a valid name.
  978. //
  979. if (!FatIsEaNameValid( IrpContext, Str )) {
  980. DebugTrace(-1, Dbg,
  981. "FatQueryEaUserEaList: Invalid Ea Name -> %Z\n",
  982. &Str);
  983. Iosb.Information = (PUCHAR)GetEa - UserEaList;
  984. Iosb.Status = STATUS_INVALID_EA_NAME;
  985. return Iosb;
  986. }
  987. //
  988. // If this is a duplicate name, we skip to the next.
  989. //
  990. if (FatIsDuplicateEaName( IrpContext, GetEa, UserEaList )) {
  991. DebugTrace(0, Dbg, "FatQueryEaUserEaList: Duplicate name\n", 0);
  992. continue;
  993. }
  994. if (!FatLocateEaByName( IrpContext,
  995. FirstPackedEa,
  996. PackedEasLength,
  997. &Str,
  998. &Offset )) {
  999. Offset = 0xffffffff;
  1000. DebugTrace(0, Dbg, "Need to dummy up an ea\n", 0);
  1001. //
  1002. // We were not able to locate the name therefore we must
  1003. // dummy up a entry for the query. The needed Ea size is
  1004. // the size of the name + 4 (next entry offset) + 1 (flags)
  1005. // + 1 (name length) + 2 (value length) + the name length +
  1006. // 1 (null byte).
  1007. //
  1008. if ((ULONG)(4+1+1+2+GetEa->EaNameLength+1)
  1009. > RemainingUserBufferLength) {
  1010. Overflow = TRUE;
  1011. break;
  1012. }
  1013. //
  1014. // Everything is going to work fine, so copy over the name,
  1015. // set the name length and zero out the rest of the ea.
  1016. //
  1017. NextFullEa->NextEntryOffset = 0;
  1018. NextFullEa->Flags = 0;
  1019. NextFullEa->EaNameLength = GetEa->EaNameLength;
  1020. NextFullEa->EaValueLength = 0;
  1021. RtlCopyMemory( &NextFullEa->EaName[0],
  1022. &GetEa->EaName[0],
  1023. GetEa->EaNameLength );
  1024. //
  1025. // Upcase the name in the buffer.
  1026. //
  1027. OutputEaName.MaximumLength = OutputEaName.Length = Str.Length;
  1028. OutputEaName.Buffer = NextFullEa->EaName;
  1029. FatUpcaseEaName( IrpContext, &OutputEaName, &OutputEaName );
  1030. NextFullEa->EaName[GetEa->EaNameLength] = 0;
  1031. } else {
  1032. DebugTrace(0, Dbg, "Located the ea, Offset = %08lx\n", Offset);
  1033. //
  1034. // We were able to locate the packed ea
  1035. // Reference the packed ea
  1036. //
  1037. PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + Offset);
  1038. SizeOfPackedEa( PackedEa, &PackedEaSize );
  1039. DebugTrace(0, Dbg, "PackedEaSize = %08lx\n", PackedEaSize);
  1040. //
  1041. // We know that the packed ea is 4 bytes smaller than its
  1042. // equivalent full ea so we need to check the remaining
  1043. // user buffer length against the computed full ea size.
  1044. //
  1045. if (PackedEaSize + 4 > RemainingUserBufferLength) {
  1046. Overflow = TRUE;
  1047. break;
  1048. }
  1049. //
  1050. // Everything is going to work fine, so copy over the packed
  1051. // ea to the full ea and zero out the next entry offset field.
  1052. //
  1053. RtlCopyMemory( &NextFullEa->Flags,
  1054. &PackedEa->Flags,
  1055. PackedEaSize );
  1056. NextFullEa->NextEntryOffset = 0;
  1057. }
  1058. //
  1059. // At this point we've copied a new full ea into the next full ea
  1060. // location. So now go back and set the set full eas entry offset
  1061. // field to be the difference between out two pointers.
  1062. //
  1063. if (LastFullEa != NULL) {
  1064. LastFullEa->NextEntryOffset = (ULONG)((PUCHAR) NextFullEa
  1065. - (PUCHAR) LastFullEa);
  1066. }
  1067. //
  1068. // Set the last full ea to the next full ea, compute
  1069. // where the next full should be, and decrement the remaining user
  1070. // buffer length appropriately
  1071. //
  1072. LastFullEa = NextFullEa;
  1073. LastFullEaSize = LongAlign( SizeOfFullEa( LastFullEa ));
  1074. RemainingUserBufferLength -= LastFullEaSize;
  1075. NextFullEa = (PFILE_FULL_EA_INFORMATION) ((PUCHAR) NextFullEa
  1076. + LastFullEaSize);
  1077. //
  1078. // Remember the offset of the next ea in case we're asked to
  1079. // resume the iteration
  1080. //
  1081. Ccb->OffsetOfNextEaToReturn = FatLocateNextEa( IrpContext,
  1082. FirstPackedEa,
  1083. PackedEasLength,
  1084. Offset );
  1085. //
  1086. // If we were to return a single entry then break out of our loop
  1087. // now
  1088. //
  1089. if (ReturnSingleEntry) {
  1090. break;
  1091. }
  1092. }
  1093. //
  1094. // Now we've iterated all that can and we've exited the preceding loop
  1095. // with either all, some or no information stored in the return buffer.
  1096. // We can decide if we got everything to fit by checking the local
  1097. // Overflow variable
  1098. //
  1099. if (Overflow) {
  1100. Iosb.Information = 0;
  1101. Iosb.Status = STATUS_BUFFER_OVERFLOW;
  1102. } else {
  1103. //
  1104. // Otherwise we've been successful in returing at least one
  1105. // ea so we'll compute the number of bytes used to store the
  1106. // full ea information. The number of bytes used is the difference
  1107. // between the LastFullEa and the start of the buffer, and the
  1108. // non-aligned size of the last full ea.
  1109. //
  1110. Iosb.Information = ((PUCHAR) LastFullEa - UserBuffer)
  1111. + SizeOfFullEa(LastFullEa);
  1112. Iosb.Status = STATUS_SUCCESS;
  1113. }
  1114. DebugTrace(-1, Dbg, "FatQueryEaUserEaList -> Iosb.Status = %08lx\n",
  1115. Iosb.Status);
  1116. return Iosb;
  1117. }
  1118. //
  1119. // Local Support Routine
  1120. //
  1121. IO_STATUS_BLOCK
  1122. FatQueryEaIndexSpecified (
  1123. IN PIRP_CONTEXT IrpContext,
  1124. OUT PCCB Ccb,
  1125. IN PPACKED_EA FirstPackedEa,
  1126. IN ULONG PackedEasLength,
  1127. OUT PUCHAR UserBuffer,
  1128. IN ULONG UserBufferLength,
  1129. IN ULONG UserEaIndex,
  1130. IN BOOLEAN ReturnSingleEntry
  1131. )
  1132. /*++
  1133. Routine Description:
  1134. This routine is the work routine for querying EAs given an ea index
  1135. Arguments:
  1136. Ccb - Supplies the Ccb for the query
  1137. FirstPackedEa - Supplies the first ea for the file being queried
  1138. PackedEasLength - Supplies the length of the ea data
  1139. UserBuffer - Supplies the buffer to receive the full eas
  1140. UserBufferLength - Supplies the length, in bytes, of the user buffer
  1141. UserEaIndex - Supplies the index of the first ea to return.
  1142. RestartScan - Indicates if the first item to return is at the
  1143. beginning of the packed ea list or if we should resume our
  1144. previous iteration
  1145. Return Value:
  1146. IO_STATUS_BLOCK - Receives the completion status for the operation
  1147. --*/
  1148. {
  1149. IO_STATUS_BLOCK Iosb;
  1150. ULONG i;
  1151. ULONG Offset;
  1152. DebugTrace(+1, Dbg, "FatQueryEaIndexSpecified...\n", 0);
  1153. //
  1154. // Zero out the information field of the iosb
  1155. //
  1156. Iosb.Information = 0;
  1157. //
  1158. // If the index value is zero or there are no Eas on the file, then
  1159. // the specified index can't be returned.
  1160. //
  1161. if (UserEaIndex == 0
  1162. || PackedEasLength == 0) {
  1163. DebugTrace( -1, Dbg, "FatQueryEaIndexSpecified: Non-existant entry\n", 0 );
  1164. Iosb.Status = STATUS_NONEXISTENT_EA_ENTRY;
  1165. return Iosb;
  1166. }
  1167. //
  1168. // Iterate the eas until we find the index we're after.
  1169. //
  1170. for (i = 1, Offset = 0;
  1171. (i < UserEaIndex) && (Offset < PackedEasLength);
  1172. i += 1, Offset = FatLocateNextEa( IrpContext,
  1173. FirstPackedEa,
  1174. PackedEasLength, Offset )) {
  1175. NOTHING;
  1176. }
  1177. //
  1178. // Make sure the offset we're given to the ea is a real offset otherwise
  1179. // the ea doesn't exist
  1180. //
  1181. if (Offset >= PackedEasLength) {
  1182. //
  1183. // If we just passed the last Ea, we will return STATUS_NO_MORE_EAS.
  1184. // This is for the caller who may be enumerating the Eas.
  1185. //
  1186. if (i == UserEaIndex) {
  1187. Iosb.Status = STATUS_NO_MORE_EAS;
  1188. //
  1189. // Otherwise we report that this is a bad ea index.
  1190. //
  1191. } else {
  1192. Iosb.Status = STATUS_NONEXISTENT_EA_ENTRY;
  1193. }
  1194. DebugTrace(-1, Dbg, "FatQueryEaIndexSpecified -> %08lx\n", Iosb.Status);
  1195. return Iosb;
  1196. }
  1197. //
  1198. // We now have the offset of the first Ea to return to the user.
  1199. // We simply call our EaSimpleScan routine to do the actual work.
  1200. //
  1201. Iosb = FatQueryEaSimpleScan( IrpContext,
  1202. Ccb,
  1203. FirstPackedEa,
  1204. PackedEasLength,
  1205. UserBuffer,
  1206. UserBufferLength,
  1207. ReturnSingleEntry,
  1208. Offset );
  1209. DebugTrace(-1, Dbg, "FatQueryEaIndexSpecified -> %08lx\n", Iosb.Status);
  1210. return Iosb;
  1211. }
  1212. //
  1213. // Local Support Routine
  1214. //
  1215. IO_STATUS_BLOCK
  1216. FatQueryEaSimpleScan (
  1217. IN PIRP_CONTEXT IrpContext,
  1218. OUT PCCB Ccb,
  1219. IN PPACKED_EA FirstPackedEa,
  1220. IN ULONG PackedEasLength,
  1221. OUT PUCHAR UserBuffer,
  1222. IN ULONG UserBufferLength,
  1223. IN BOOLEAN ReturnSingleEntry,
  1224. ULONG StartOffset
  1225. )
  1226. /*++
  1227. Routine Description:
  1228. This routine is the work routine for querying EAs from the beginning of
  1229. the ea list.
  1230. Arguments:
  1231. Ccb - Supplies the Ccb for the query
  1232. FirstPackedEa - Supplies the first ea for the file being queried
  1233. PackedEasLength - Supplies the length of the ea data
  1234. UserBuffer - Supplies the buffer to receive the full eas
  1235. UserBufferLength - Supplies the length, in bytes, of the user buffer
  1236. ReturnSingleEntry - Indicates if we are to return a single entry or not
  1237. StartOffset - Indicates the offset within the Ea data to return the
  1238. first block of data.
  1239. Return Value:
  1240. IO_STATUS_BLOCK - Receives the completion status for the operation
  1241. --*/
  1242. {
  1243. IO_STATUS_BLOCK Iosb;
  1244. ULONG RemainingUserBufferLength;
  1245. PPACKED_EA PackedEa;
  1246. ULONG PackedEaSize;
  1247. PFILE_FULL_EA_INFORMATION LastFullEa;
  1248. ULONG LastFullEaSize;
  1249. PFILE_FULL_EA_INFORMATION NextFullEa;
  1250. BOOLEAN BufferOverflow = FALSE;
  1251. DebugTrace(+1, Dbg, "FatQueryEaSimpleScan...\n", 0);
  1252. //
  1253. // Zero out the information field in the Iosb
  1254. //
  1255. Iosb.Information = 0;
  1256. LastFullEa = NULL;
  1257. NextFullEa = (PFILE_FULL_EA_INFORMATION) UserBuffer;
  1258. RemainingUserBufferLength = UserBufferLength;
  1259. while (StartOffset < PackedEasLength) {
  1260. DebugTrace(0, Dbg, "Top of loop, Offset = %08lx\n", StartOffset);
  1261. DebugTrace(0, Dbg, "LastFullEa = %08lx\n", LastFullEa);
  1262. DebugTrace(0, Dbg, "NextFullEa = %08lx\n", NextFullEa);
  1263. DebugTrace(0, Dbg, "RemainingUserBufferLength = %08lx\n", RemainingUserBufferLength);
  1264. //
  1265. // Reference the packed ea of interest.
  1266. //
  1267. PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + StartOffset);
  1268. SizeOfPackedEa( PackedEa, &PackedEaSize );
  1269. DebugTrace(0, Dbg, "PackedEaSize = %08lx\n", PackedEaSize);
  1270. //
  1271. // We know that the packed ea is 4 bytes smaller than its
  1272. // equivalent full ea so we need to check the remaining
  1273. // user buffer length against the computed full ea size.
  1274. //
  1275. if (PackedEaSize + 4 > RemainingUserBufferLength) {
  1276. BufferOverflow = TRUE;
  1277. break;
  1278. }
  1279. //
  1280. // Everything is going to work fine, so copy over the packed
  1281. // ea to the full ea and zero out the next entry offset field.
  1282. // Then go back and set the last full eas entry offset field
  1283. // to be the difference between the two pointers.
  1284. //
  1285. RtlCopyMemory( &NextFullEa->Flags, &PackedEa->Flags, PackedEaSize );
  1286. NextFullEa->NextEntryOffset = 0;
  1287. if (LastFullEa != NULL) {
  1288. LastFullEa->NextEntryOffset = (ULONG)((PUCHAR) NextFullEa
  1289. - (PUCHAR) LastFullEa);
  1290. }
  1291. //
  1292. // Set the last full ea to the next full ea, compute
  1293. // where the next full should be, and decrement the remaining user
  1294. // buffer length appropriately
  1295. //
  1296. LastFullEa = NextFullEa;
  1297. LastFullEaSize = LongAlign( SizeOfFullEa( LastFullEa ));
  1298. RemainingUserBufferLength -= LastFullEaSize;
  1299. NextFullEa = (PFILE_FULL_EA_INFORMATION) ((PUCHAR) NextFullEa
  1300. + LastFullEaSize);
  1301. //
  1302. // Remember the offset of the next ea in case we're asked to
  1303. // resume the teration
  1304. //
  1305. StartOffset = FatLocateNextEa( IrpContext,
  1306. FirstPackedEa,
  1307. PackedEasLength,
  1308. StartOffset );
  1309. Ccb->OffsetOfNextEaToReturn = StartOffset;
  1310. //
  1311. // If we were to return a single entry then break out of our loop
  1312. // now
  1313. //
  1314. if (ReturnSingleEntry) {
  1315. break;
  1316. }
  1317. }
  1318. //
  1319. // Now we've iterated all that can and we've exited the preceding loop
  1320. // with either some or no information stored in the return buffer.
  1321. // We can decide which it is by checking if the last full ea is null
  1322. //
  1323. if (LastFullEa == NULL) {
  1324. Iosb.Information = 0;
  1325. //
  1326. // We were not able to return a single ea entry, now we need to find
  1327. // out if it is because we didn't have an entry to return or the
  1328. // buffer is too small. If the Offset variable is less than
  1329. // PackedEaList->UsedSize then the user buffer is too small
  1330. //
  1331. if (PackedEasLength == 0) {
  1332. Iosb.Status = STATUS_NO_EAS_ON_FILE;
  1333. } else if (StartOffset >= PackedEasLength) {
  1334. Iosb.Status = STATUS_NO_MORE_EAS;
  1335. } else {
  1336. Iosb.Status = STATUS_BUFFER_TOO_SMALL;
  1337. }
  1338. } else {
  1339. //
  1340. // Otherwise we've been successful in returing at least one
  1341. // ea so we'll compute the number of bytes used to store the
  1342. // full ea information. The number of bytes used is the difference
  1343. // between the LastFullEa and the start of the buffer, and the
  1344. // non-aligned size of the last full ea.
  1345. //
  1346. Iosb.Information = ((PUCHAR) LastFullEa - UserBuffer)
  1347. + SizeOfFullEa( LastFullEa );
  1348. //
  1349. // If there are more to return, report the buffer was too small.
  1350. // Otherwise return STATUS_SUCCESS.
  1351. //
  1352. if (BufferOverflow) {
  1353. Iosb.Status = STATUS_BUFFER_OVERFLOW;
  1354. } else {
  1355. Iosb.Status = STATUS_SUCCESS;
  1356. }
  1357. }
  1358. DebugTrace(-1, Dbg, "FatQueryEaSimpleScan -> Iosb.Status = %08lx\n",
  1359. Iosb.Status);
  1360. return Iosb;
  1361. }
  1362. //
  1363. // Local Support Routine
  1364. //
  1365. BOOLEAN
  1366. FatIsDuplicateEaName (
  1367. IN PIRP_CONTEXT IrpContext,
  1368. IN PFILE_GET_EA_INFORMATION GetEa,
  1369. IN PUCHAR UserBuffer
  1370. )
  1371. /*++
  1372. Routine Description:
  1373. This routine walks through a list of ea names to find a duplicate name.
  1374. 'GetEa' is an actual position in the list. We are only interested in
  1375. previous matching ea names, as the ea information for that ea name
  1376. would have been returned with the previous instance.
  1377. Arguments:
  1378. GetEa - Supplies the Ea name structure for the ea name to match.
  1379. UserBuffer - Supplies a pointer to the user buffer with the list
  1380. of ea names to search for.
  1381. Return Value:
  1382. BOOLEAN - TRUE if a previous match is found, FALSE otherwise.
  1383. --*/
  1384. {
  1385. PFILE_GET_EA_INFORMATION ThisGetEa;
  1386. BOOLEAN DuplicateFound;
  1387. OEM_STRING EaString;
  1388. DebugTrace(+1, Dbg, "FatIsDuplicateEaName...\n", 0);
  1389. EaString.MaximumLength = EaString.Length = GetEa->EaNameLength;
  1390. EaString.Buffer = &GetEa->EaName[0];
  1391. FatUpcaseEaName( IrpContext, &EaString, &EaString );
  1392. DuplicateFound = FALSE;
  1393. for (ThisGetEa = (PFILE_GET_EA_INFORMATION) &UserBuffer[0];
  1394. ThisGetEa < GetEa
  1395. && ThisGetEa->NextEntryOffset != 0;
  1396. ThisGetEa = (PFILE_GET_EA_INFORMATION) ((PUCHAR) ThisGetEa
  1397. + ThisGetEa->NextEntryOffset)) {
  1398. OEM_STRING Str;
  1399. DebugTrace(0, Dbg, "Top of loop, ThisGetEa = %08lx\n", ThisGetEa);
  1400. //
  1401. // Make a string reference to the GetEa and see if we can
  1402. // locate the ea by name
  1403. //
  1404. Str.MaximumLength = Str.Length = ThisGetEa->EaNameLength;
  1405. Str.Buffer = &ThisGetEa->EaName[0];
  1406. DebugTrace(0, Dbg, "FatIsDuplicateEaName: Next Name -> %Z\n", &Str);
  1407. if ( FatAreNamesEqual(IrpContext, Str, EaString) ) {
  1408. DebugTrace(0, Dbg, "FatIsDuplicateEaName: Duplicate found\n", 0);
  1409. DuplicateFound = TRUE;
  1410. break;
  1411. }
  1412. }
  1413. DebugTrace(-1, Dbg, "FatIsDuplicateEaName: Exit -> %04x\n", DuplicateFound);
  1414. return DuplicateFound;
  1415. }