Leaked source code of windows server 2003
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.

2720 lines
70 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. Ea.c
  5. Abstract:
  6. This module implements the File set and query Ea routines for Ntfs called
  7. by the dispatch driver.
  8. Author:
  9. Your Name [Email] dd-Mon-Year
  10. Revision History:
  11. --*/
  12. #include "NtfsProc.h"
  13. //
  14. // The local debug trace level
  15. //
  16. #define Dbg (DEBUG_TRACE_EA)
  17. //
  18. // Define a tag for general pool allocations from this module
  19. //
  20. #undef MODULE_POOL_TAG
  21. #define MODULE_POOL_TAG ('EFtN')
  22. //
  23. // Local definitions
  24. //
  25. //
  26. // The following gives us an empty name string.
  27. //
  28. UNICODE_STRING AttrNoName = CONSTANT_UNICODE_STRING( L"" );
  29. #define MAXIMUM_EA_SIZE 0x0000ffff
  30. //
  31. // The following macros compute the packed and unpacked size of the EAs.
  32. // We use the 1 char defined in the structure for the NULL terminator of
  33. // the name.
  34. //
  35. #define SizeOfEaInformation \
  36. (sizeof( ULONG ) + sizeof( USHORT ) + 3 * sizeof( UCHAR ))
  37. #define PackedEaSize(EA) \
  38. ((SizeOfEaInformation - 4) \
  39. + ((PFILE_FULL_EA_INFORMATION) EA)->EaNameLength \
  40. + ((PFILE_FULL_EA_INFORMATION) EA)->EaValueLength)
  41. #define RawUnpackedEaSize(EA) \
  42. (SizeOfEaInformation \
  43. + ((PFILE_FULL_EA_INFORMATION) EA)->EaNameLength \
  44. + ((PFILE_FULL_EA_INFORMATION) EA)->EaValueLength) \
  45. #define AlignedUnpackedEaSize(EA) \
  46. (((PFILE_FULL_EA_INFORMATION) EA)->NextEntryOffset != 0 \
  47. ? ((PFILE_FULL_EA_INFORMATION) EA)->NextEntryOffset \
  48. : (LongAlign( RawUnpackedEaSize( EA )))) \
  49. //
  50. // BOOLEAN
  51. // NtfsAreEaNamesEqual (
  52. // IN PIRP_CONTEXT IrpContext,
  53. // IN PSTRING NameA,
  54. // IN PSTRING NameB
  55. // );
  56. //
  57. #define NtfsAreEaNamesEqual(NAMEA, NAMEB ) ((BOOLEAN) \
  58. ((NAMEA)->Length == (NAMEB)->Length \
  59. && RtlEqualMemory( (NAMEA)->Buffer, \
  60. (NAMEB)->Buffer, \
  61. (NAMEA)->Length ) ) \
  62. )
  63. //
  64. // VOID
  65. // NtfsUpcaseEaName (
  66. // IN PSTRING EaName,
  67. // OUT PSTRING UpcasedEaName
  68. // );
  69. //
  70. #define NtfsUpcaseEaName( NAME, UPCASEDNAME ) \
  71. RtlUpperString( UPCASEDNAME, NAME )
  72. BOOLEAN
  73. NtfsIsEaNameValid (
  74. IN STRING Name
  75. );
  76. //
  77. // Local procedure prototypes
  78. //
  79. VOID
  80. NtfsAppendEa (
  81. IN PIRP_CONTEXT IrpContext,
  82. IN OUT PEA_LIST_HEADER EaListHeader,
  83. IN PFILE_FULL_EA_INFORMATION FullEa,
  84. IN PVCB Vcb
  85. );
  86. VOID
  87. NtfsDeleteEa (
  88. IN PIRP_CONTEXT IrpContext,
  89. IN OUT PEA_LIST_HEADER EaListHeader,
  90. IN ULONG Offset
  91. );
  92. BOOLEAN
  93. NtfsLocateEaByName (
  94. IN PFILE_FULL_EA_INFORMATION FullEa,
  95. IN ULONG EaBufferLength,
  96. IN PSTRING EaName,
  97. OUT PULONG Offset
  98. );
  99. IO_STATUS_BLOCK
  100. NtfsQueryEaUserEaList (
  101. IN PFILE_FULL_EA_INFORMATION CurrentEas,
  102. IN PEA_INFORMATION EaInformation,
  103. OUT PFILE_FULL_EA_INFORMATION EaBuffer,
  104. IN ULONG UserBufferLength,
  105. IN PFILE_GET_EA_INFORMATION UserEaList,
  106. IN BOOLEAN ReturnSingleEntry
  107. );
  108. IO_STATUS_BLOCK
  109. NtfsQueryEaIndexSpecified (
  110. OUT PCCB Ccb,
  111. IN PFILE_FULL_EA_INFORMATION CurrentEas,
  112. IN PEA_INFORMATION EaInformation,
  113. OUT PFILE_FULL_EA_INFORMATION EaBuffer,
  114. IN ULONG UserBufferLength,
  115. IN ULONG UserEaIndex,
  116. IN BOOLEAN ReturnSingleEntry
  117. );
  118. IO_STATUS_BLOCK
  119. NtfsQueryEaSimpleScan (
  120. OUT PCCB Ccb,
  121. IN PFILE_FULL_EA_INFORMATION CurrentEas,
  122. IN PEA_INFORMATION EaInformation,
  123. OUT PFILE_FULL_EA_INFORMATION EaBuffer,
  124. IN ULONG UserBufferLength,
  125. IN BOOLEAN ReturnSingleEntry,
  126. IN ULONG StartingOffset
  127. );
  128. BOOLEAN
  129. NtfsIsDuplicateGeaName (
  130. IN PFILE_GET_EA_INFORMATION CurrentGea,
  131. IN PFILE_GET_EA_INFORMATION UserGeaBuffer
  132. );
  133. #ifdef ALLOC_PRAGMA
  134. #pragma alloc_text(PAGE, NtfsAppendEa)
  135. #pragma alloc_text(PAGE, NtfsBuildEaList)
  136. #pragma alloc_text(PAGE, NtfsCommonQueryEa)
  137. #pragma alloc_text(PAGE, NtfsCommonSetEa)
  138. #pragma alloc_text(PAGE, NtfsDeleteEa)
  139. #pragma alloc_text(PAGE, NtfsIsDuplicateGeaName)
  140. #pragma alloc_text(PAGE, NtfsIsEaNameValid)
  141. #pragma alloc_text(PAGE, NtfsLocateEaByName)
  142. #pragma alloc_text(PAGE, NtfsMapExistingEas)
  143. #pragma alloc_text(PAGE, NtfsQueryEaIndexSpecified)
  144. #pragma alloc_text(PAGE, NtfsQueryEaSimpleScan)
  145. #pragma alloc_text(PAGE, NtfsQueryEaUserEaList)
  146. #pragma alloc_text(PAGE, NtfsReplaceFileEas)
  147. #endif
  148. NTSTATUS
  149. NtfsCommonQueryEa (
  150. IN PIRP_CONTEXT IrpContext,
  151. IN PIRP Irp
  152. )
  153. /*++
  154. Routine Description:
  155. This is the common routine for query Ea called by both the fsd and fsp
  156. threads.
  157. Arguments:
  158. Irp - Supplies the Irp to process
  159. Return Value:
  160. NTSTATUS - The return status for the operation
  161. --*/
  162. {
  163. NTSTATUS Status;
  164. PIO_STACK_LOCATION IrpSp;
  165. PFILE_OBJECT FileObject;
  166. TYPE_OF_OPEN TypeOfOpen;
  167. PVCB Vcb;
  168. PFCB Fcb;
  169. PSCB Scb;
  170. PCCB Ccb;
  171. PFILE_FULL_EA_INFORMATION EaBuffer = NULL;
  172. PFILE_FULL_EA_INFORMATION MappedEaBuffer = NULL;
  173. ULONG UserBufferLength;
  174. PFILE_GET_EA_INFORMATION UserEaList;
  175. ULONG UserEaListLength;
  176. ULONG UserEaIndex;
  177. ULONG EaLength;
  178. BOOLEAN RestartScan;
  179. BOOLEAN ReturnSingleEntry;
  180. BOOLEAN IndexSpecified;
  181. BOOLEAN TempBufferAllocated = FALSE;
  182. PFILE_FULL_EA_INFORMATION CurrentEas;
  183. PBCB EaBcb;
  184. ATTRIBUTE_ENUMERATION_CONTEXT EaInfoAttr;
  185. BOOLEAN CleanupEaInfoAttr;
  186. PEA_INFORMATION EaInformation;
  187. EA_INFORMATION DummyEaInformation;
  188. ASSERT_IRP_CONTEXT( IrpContext );
  189. ASSERT_IRP( Irp );
  190. ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
  191. PAGED_CODE();
  192. //
  193. // Get the current Irp stack location
  194. //
  195. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  196. DebugTrace( +1, Dbg, ("NtfsCommonQueryEa\n") );
  197. DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
  198. DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
  199. DebugTrace( 0, Dbg, ("SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer) );
  200. DebugTrace( 0, Dbg, ("Length = %08lx\n", IrpSp->Parameters.QueryEa.Length) );
  201. DebugTrace( 0, Dbg, ("EaList = %08lx\n", IrpSp->Parameters.QueryEa.EaList) );
  202. DebugTrace( 0, Dbg, ("EaListLength = %08lx\n", IrpSp->Parameters.QueryEa.EaListLength) );
  203. DebugTrace( 0, Dbg, ("EaIndex = %08lx\n", IrpSp->Parameters.QueryEa.EaIndex) );
  204. DebugTrace( 0, Dbg, ("RestartScan = %08lx\n", FlagOn(IrpSp->Flags, SL_RESTART_SCAN)) );
  205. DebugTrace( 0, Dbg, ("ReturnSingleEntry = %08lx\n", FlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY)) );
  206. DebugTrace( 0, Dbg, ("IndexSpecified = %08lx\n", FlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED)) );
  207. //
  208. // Extract and decode the file object
  209. //
  210. FileObject = IrpSp->FileObject;
  211. TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
  212. //
  213. // This must be a user file or directory and the Ccb must indicate that
  214. // the caller opened the entire file.
  215. //
  216. if ((TypeOfOpen != UserFileOpen && TypeOfOpen != UserDirectoryOpen) ||
  217. !FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE )) {
  218. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  219. DebugTrace( -1, Dbg, ("NtfsCommonQueryEa -> %08lx\n", STATUS_INVALID_PARAMETER) );
  220. return STATUS_INVALID_PARAMETER;
  221. }
  222. //
  223. // Acquire the Fcb exclusively.
  224. //
  225. NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, 0 );
  226. //
  227. // If this file is a reparse point it cannot support EAs.
  228. // Return to caller STATUS_EAS_NOT_SUPPORTED.
  229. //
  230. if (FlagOn( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_REPARSE_POINT )) {
  231. DebugTrace( 0, Dbg, ("Reparse point present. EAs not supported.\n") );
  232. Status = STATUS_EAS_NOT_SUPPORTED;
  233. //
  234. // Release the Fcb and return to caller.
  235. //
  236. NtfsReleaseFcb( IrpContext, Fcb );
  237. NtfsCompleteRequest( IrpContext, Irp, Status );
  238. DebugTrace( -1, Dbg, ("NtfsCommonQueryEa -> %08lx\n", Status) );
  239. return Status;
  240. }
  241. //
  242. // Make sure the volume is still mounted.
  243. //
  244. if (FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) {
  245. DebugTrace( 0, Dbg, ("Volume dismounted.\n") );
  246. Status = STATUS_VOLUME_DISMOUNTED;
  247. //
  248. // Release the Fcb and return to caller.
  249. //
  250. NtfsReleaseFcb( IrpContext, Fcb );
  251. NtfsCompleteRequest( IrpContext, Irp, Status );
  252. DebugTrace( -1, Dbg, ("NtfsCommonQueryEa -> %08lx\n", Status) );
  253. return Status;
  254. }
  255. //
  256. // Use a try-finally to facilitate cleanup.
  257. //
  258. try {
  259. //
  260. // Reference our input parameters to make things easier
  261. //
  262. UserBufferLength = IrpSp->Parameters.QueryEa.Length;
  263. UserEaList = (PFILE_GET_EA_INFORMATION) IrpSp->Parameters.QueryEa.EaList;
  264. UserEaListLength = IrpSp->Parameters.QueryEa.EaListLength;
  265. UserEaIndex = IrpSp->Parameters.QueryEa.EaIndex;
  266. RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN);
  267. ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY);
  268. IndexSpecified = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED);
  269. //
  270. // Initialize our local variables.
  271. //
  272. Status = STATUS_SUCCESS;
  273. CleanupEaInfoAttr = FALSE;
  274. EaBcb = NULL;
  275. //
  276. // Map the user's buffer.
  277. //
  278. if (UserBufferLength != 0) {
  279. EaBuffer = NtfsMapUserBuffer( Irp, NormalPagePriority );
  280. //
  281. // Allocate a system buffer to work on, out of paranoia.
  282. // This buffer will get zeroed later before we actually use it.
  283. //
  284. if (Irp->RequestorMode != KernelMode) {
  285. MappedEaBuffer = EaBuffer;
  286. EaBuffer = NtfsAllocatePool( PagedPool, UserBufferLength );
  287. TempBufferAllocated = TRUE;
  288. }
  289. //
  290. // Let's clear the output buffer.
  291. //
  292. RtlZeroMemory( EaBuffer, UserBufferLength );
  293. }
  294. //
  295. // Verify that the Ea file is in a consistant state. If the
  296. // Ea modification count in the Fcb doesn't match that in
  297. // the CCB, then the Ea file has been changed from under
  298. // us. If we are not starting the search from the beginning
  299. // of the Ea set, we return an error.
  300. //
  301. if ((UserEaList == NULL) &&
  302. (Ccb->NextEaOffset != 0) &&
  303. !IndexSpecified &&
  304. !RestartScan &&
  305. (Fcb->EaModificationCount != Ccb->EaModificationCount)) {
  306. DebugTrace( 0, Dbg, ("NtfsCommonQueryEa: Ea file in unknown state\n") );
  307. Status = STATUS_EA_CORRUPT_ERROR;
  308. try_return( Status );
  309. }
  310. //
  311. // Show that the Ea's for this file are consistant for this
  312. // file handle.
  313. //
  314. Ccb->EaModificationCount = Fcb->EaModificationCount;
  315. //
  316. // We need to look up the attribute for the Ea information.
  317. // If we don't find the attribute, then there are no EA's for
  318. // this file. In that case we dummy up an ea list to use below.
  319. //
  320. NtfsInitializeAttributeContext( &EaInfoAttr );
  321. CleanupEaInfoAttr = TRUE;
  322. {
  323. BOOLEAN EasOnFile;
  324. EasOnFile = FALSE;
  325. if (NtfsLookupAttributeByCode( IrpContext,
  326. Fcb,
  327. &Fcb->FileReference,
  328. $EA_INFORMATION,
  329. &EaInfoAttr)) {
  330. //
  331. // As a sanity check we will check that the unpacked length is
  332. // non-zero. It should always be so.
  333. //
  334. EaInformation = (PEA_INFORMATION) NtfsAttributeValue( NtfsFoundAttribute( &EaInfoAttr ));
  335. if (EaInformation->UnpackedEaSize != 0) {
  336. EasOnFile = TRUE;
  337. }
  338. }
  339. if (EasOnFile) {
  340. //
  341. // We obtain a pointer to the start of the existing Ea's for the file.
  342. //
  343. CurrentEas = NtfsMapExistingEas( IrpContext,
  344. Fcb,
  345. &EaBcb,
  346. &EaLength );
  347. } else {
  348. CurrentEas = NULL;
  349. EaLength = 0;
  350. DummyEaInformation.PackedEaSize = 0;
  351. DummyEaInformation.NeedEaCount = 0;
  352. DummyEaInformation.UnpackedEaSize = 0;
  353. EaInformation = &DummyEaInformation;
  354. }
  355. }
  356. //
  357. // We now satisfy the user's request depending on whether he
  358. // specified an Ea name list, an Ea index or restarting the
  359. // search.
  360. //
  361. //
  362. // The user has supplied a list of Ea names.
  363. //
  364. if (UserEaList != NULL) {
  365. Irp->IoStatus = NtfsQueryEaUserEaList( CurrentEas,
  366. EaInformation,
  367. EaBuffer,
  368. UserBufferLength,
  369. UserEaList,
  370. ReturnSingleEntry );
  371. //
  372. // The user supplied an index into the Ea list.
  373. //
  374. } else if (IndexSpecified) {
  375. Irp->IoStatus = NtfsQueryEaIndexSpecified( Ccb,
  376. CurrentEas,
  377. EaInformation,
  378. EaBuffer,
  379. UserBufferLength,
  380. UserEaIndex,
  381. ReturnSingleEntry );
  382. //
  383. // Else perform a simple scan, taking into account the restart
  384. // flag and the position of the next Ea stored in the Ccb.
  385. //
  386. } else {
  387. Irp->IoStatus = NtfsQueryEaSimpleScan( Ccb,
  388. CurrentEas,
  389. EaInformation,
  390. EaBuffer,
  391. UserBufferLength,
  392. ReturnSingleEntry,
  393. RestartScan
  394. ? 0
  395. : Ccb->NextEaOffset );
  396. }
  397. Status = Irp->IoStatus.Status;
  398. //
  399. // Copy the data onto the user buffer if we ended up allocating
  400. // a temporary buffer to work on.
  401. //
  402. if ((UserBufferLength != 0) && (MappedEaBuffer != NULL)) {
  403. try {
  404. RtlCopyMemory( MappedEaBuffer, EaBuffer, UserBufferLength );
  405. } except( EXCEPTION_EXECUTE_HANDLER ) {
  406. try_return( Status = STATUS_INVALID_USER_BUFFER );
  407. }
  408. }
  409. try_exit: NOTHING;
  410. } finally {
  411. DebugUnwind( NtfsCommonQueryEa );
  412. //
  413. // We cleanup any attribute contexts.
  414. //
  415. if (CleanupEaInfoAttr) {
  416. NtfsCleanupAttributeContext( IrpContext, &EaInfoAttr );
  417. }
  418. //
  419. // Unpin the stream file if pinned.
  420. //
  421. NtfsUnpinBcb( IrpContext, &EaBcb );
  422. //
  423. // Release the Fcb.
  424. //
  425. NtfsReleaseFcb( IrpContext, Fcb );
  426. if (TempBufferAllocated) {
  427. NtfsFreePool( EaBuffer );
  428. }
  429. if (!AbnormalTermination()) {
  430. NtfsCompleteRequest( IrpContext, Irp, Status );
  431. }
  432. //
  433. // And return to our caller
  434. //
  435. DebugTrace( -1, Dbg, ("NtfsCommonQueryEa -> %08lx\n", Status) );
  436. }
  437. return Status;
  438. }
  439. NTSTATUS
  440. NtfsCommonSetEa (
  441. IN PIRP_CONTEXT IrpContext,
  442. IN PIRP Irp
  443. )
  444. /*++
  445. Routine Description:
  446. This is the common routine for set Ea called by both the fsd and fsp
  447. threads.
  448. Arguments:
  449. Irp - Supplies the Irp to process
  450. Return Value:
  451. NTSTATUS - The return status for the operation
  452. --*/
  453. {
  454. NTSTATUS Status;
  455. PIO_STACK_LOCATION IrpSp;
  456. PFILE_OBJECT FileObject;
  457. TYPE_OF_OPEN TypeOfOpen;
  458. PVCB Vcb;
  459. PFCB Fcb;
  460. PSCB Scb;
  461. PCCB Ccb;
  462. ULONG Offset;
  463. ATTRIBUTE_ENUMERATION_CONTEXT EaInfoAttr;
  464. PEA_INFORMATION EaInformation;
  465. PFILE_FULL_EA_INFORMATION SafeBuffer = NULL;
  466. BOOLEAN PreviousEas;
  467. EA_LIST_HEADER EaList;
  468. PBCB EaBcb;
  469. ASSERT_IRP_CONTEXT( IrpContext );
  470. ASSERT_IRP( Irp );
  471. ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
  472. PAGED_CODE();
  473. NtfsInitializeAttributeContext( &EaInfoAttr );
  474. //
  475. // Get the current Irp stack location
  476. //
  477. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  478. DebugTrace( +1, Dbg, ("NtfsCommonSetEa\n") );
  479. DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
  480. DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
  481. //
  482. // Extract and decode the file object
  483. //
  484. FileObject = IrpSp->FileObject;
  485. TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
  486. //
  487. // Initialize the IoStatus values.
  488. //
  489. Irp->IoStatus.Information = 0;
  490. Irp->IoStatus.Status = STATUS_SUCCESS;
  491. //
  492. // Check that the file object is associated with either a user file or
  493. // user directory open or an open by file ID.
  494. //
  495. if ((Ccb == NULL) ||
  496. !FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE ) ||
  497. ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen))) {
  498. DebugTrace( 0, Dbg, ("Invalid file object\n") );
  499. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  500. DebugTrace( -1, Dbg, ("NtfsCommonSetEa -> %08lx\n", STATUS_INVALID_PARAMETER) );
  501. return STATUS_INVALID_PARAMETER;
  502. }
  503. //
  504. // We must be writable.
  505. //
  506. if (NtfsIsVolumeReadOnly( Vcb )) {
  507. Status = STATUS_MEDIA_WRITE_PROTECTED;
  508. NtfsCompleteRequest( IrpContext, Irp, Status );
  509. DebugTrace( -1, Dbg, ("NtfsCommonSetEa -> %08lx\n", Status) );
  510. return Status;
  511. }
  512. //
  513. // We must be waitable.
  514. //
  515. if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT )) {
  516. Status = NtfsPostRequest( IrpContext, Irp );
  517. DebugTrace( -1, Dbg, ("NtfsCommonSetEa -> %08lx\n", Status) );
  518. return Status;
  519. }
  520. //
  521. // Acquire the paging file resource. We need to protect ourselves against collided
  522. // page waits in the case where we need to do a ConvertToNonresident in this path.
  523. // If we acquire the main and then take the fault we can see the deadlock. Acquire
  524. // the paging io resource to lock everyone else out.
  525. //
  526. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_PAGING );
  527. NtfsAcquireFcbWithPaging( IrpContext, Fcb, 0 );
  528. //
  529. // Use a try-finally to facilitate cleanup.
  530. //
  531. try {
  532. ULONG UserBufferLength;
  533. PFILE_FULL_EA_INFORMATION Buffer;
  534. PFILE_FULL_EA_INFORMATION CurrentEas;
  535. //
  536. // Reference the input parameters and initialize our local variables.
  537. //
  538. UserBufferLength = IrpSp->Parameters.SetEa.Length;
  539. EaBcb = NULL;
  540. Offset = 0;
  541. EaList.FullEa = NULL;
  542. //
  543. // If this file is a reparse point one cannot establish an EA in it.
  544. // Return to caller STATUS_EAS_NOT_SUPPORTED.
  545. //
  546. if (FlagOn( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_REPARSE_POINT )) {
  547. DebugTrace( 0, Dbg, ("Reparse point present, cannot set EA.\n") );
  548. Status = STATUS_EAS_NOT_SUPPORTED;
  549. leave;
  550. }
  551. //
  552. // Make sure the volume is still mounted.
  553. //
  554. if (FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) {
  555. DebugTrace( 0, Dbg, ("Volume dismounted.\n") );
  556. Status = STATUS_VOLUME_DISMOUNTED;
  557. leave;
  558. }
  559. //
  560. // Map the user's Ea buffer.
  561. //
  562. Buffer = NtfsMapUserBuffer( Irp, NormalPagePriority );
  563. if (UserBufferLength != 0) {
  564. //
  565. // Be paranoid and copy the user buffer into kernel space.
  566. //
  567. if (Irp->RequestorMode != KernelMode) {
  568. SafeBuffer = NtfsAllocatePool( PagedPool, UserBufferLength );
  569. try {
  570. RtlCopyMemory( SafeBuffer, Buffer, UserBufferLength );
  571. } except( EXCEPTION_EXECUTE_HANDLER ) {
  572. try_return( Status = STATUS_INVALID_USER_BUFFER );
  573. }
  574. Buffer = SafeBuffer;
  575. }
  576. }
  577. //
  578. // Check the user's buffer for validity.
  579. //
  580. {
  581. ULONG ErrorOffset;
  582. Status = IoCheckEaBufferValidity( Buffer,
  583. UserBufferLength,
  584. &ErrorOffset );
  585. if (!NT_SUCCESS( Status )) {
  586. Irp->IoStatus.Information = ErrorOffset;
  587. try_return( Status );
  588. }
  589. }
  590. //
  591. // Check if the file has existing Ea's.
  592. //
  593. if (NtfsLookupAttributeByCode( IrpContext,
  594. Fcb,
  595. &Fcb->FileReference,
  596. $EA_INFORMATION,
  597. &EaInfoAttr)) {
  598. PreviousEas = TRUE;
  599. EaInformation = (PEA_INFORMATION) NtfsAttributeValue( NtfsFoundAttribute( &EaInfoAttr ));
  600. } else {
  601. PreviousEas = FALSE;
  602. }
  603. //
  604. // Sanity check.
  605. //
  606. ASSERT( !PreviousEas || EaInformation->UnpackedEaSize != 0 );
  607. //
  608. // Initialize our Ea list structure depending on whether there
  609. // were previous Ea's or not.
  610. //
  611. if (PreviousEas) {
  612. //
  613. // Copy the information out of the Ea information attribute.
  614. //
  615. EaList.PackedEaSize = (ULONG) EaInformation->PackedEaSize;
  616. EaList.NeedEaCount = EaInformation->NeedEaCount;
  617. EaList.UnpackedEaSize = EaInformation->UnpackedEaSize;
  618. CurrentEas = NtfsMapExistingEas( IrpContext,
  619. Fcb,
  620. &EaBcb,
  621. &EaList.BufferSize );
  622. //
  623. // The allocated size of the Ea buffer is the Unpacked length.
  624. //
  625. EaList.FullEa = NtfsAllocatePool(PagedPool, EaList.BufferSize );
  626. //
  627. // Now copy the mapped Eas.
  628. //
  629. RtlCopyMemory( EaList.FullEa,
  630. CurrentEas,
  631. EaList.BufferSize );
  632. //
  633. // Upin the stream file.
  634. //
  635. NtfsUnpinBcb( IrpContext, &EaBcb );
  636. } else {
  637. //
  638. // Set this up as an empty list.
  639. //
  640. EaList.PackedEaSize = 0;
  641. EaList.NeedEaCount = 0;
  642. EaList.UnpackedEaSize = 0;
  643. EaList.BufferSize = 0;
  644. EaList.FullEa = NULL;
  645. }
  646. //
  647. // Build the new ea list.
  648. //
  649. Status = NtfsBuildEaList( IrpContext,
  650. Vcb,
  651. &EaList,
  652. Buffer,
  653. &Irp->IoStatus.Information );
  654. if (!NT_SUCCESS( Status )) {
  655. try_return( Status );
  656. }
  657. //
  658. // Replace the existing Eas.
  659. //
  660. NtfsReplaceFileEas( IrpContext, Fcb, &EaList );
  661. //
  662. // Increment the Modification count for the Eas.
  663. //
  664. Fcb->EaModificationCount++;
  665. //
  666. // Update the information in the duplicate information and mark
  667. // the Fcb as info modified.
  668. //
  669. if (EaList.UnpackedEaSize == 0) {
  670. Fcb->Info.PackedEaSize = 0;
  671. } else {
  672. Fcb->Info.PackedEaSize = (USHORT) EaList.PackedEaSize;
  673. }
  674. //
  675. // Update the caller's Iosb.
  676. //
  677. Irp->IoStatus.Information = 0;
  678. Status = STATUS_SUCCESS;
  679. try_exit: NOTHING;
  680. //
  681. // Check if there are transactions to cleanup.
  682. //
  683. NtfsCleanupTransaction( IrpContext, Status, FALSE );
  684. //
  685. // Show that we changed the Ea's and also set the Ccb flag so we will
  686. // update the time stamps.
  687. //
  688. SetFlag( Ccb->Flags,
  689. CCB_FLAG_UPDATE_LAST_CHANGE | CCB_FLAG_SET_ARCHIVE );
  690. } finally {
  691. DebugUnwind( NtfsCommonSetEa );
  692. //
  693. // Free the in-memory copy of the Eas.
  694. //
  695. if (EaList.FullEa != NULL) {
  696. NtfsFreePool( EaList.FullEa );
  697. }
  698. //
  699. // Unpin the Bcb.
  700. //
  701. NtfsUnpinBcb( IrpContext, &EaBcb );
  702. //
  703. // Cleanup any attribute contexts used.
  704. //
  705. NtfsCleanupAttributeContext( IrpContext, &EaInfoAttr );
  706. //
  707. // If we allocated a temporary buffer, free it.
  708. //
  709. if (SafeBuffer != NULL) {
  710. NtfsFreePool( SafeBuffer );
  711. }
  712. DebugTrace( -1, Dbg, ("NtfsCommonSetEa -> %08lx\n", Status) );
  713. }
  714. //
  715. // Complete the Irp.
  716. //
  717. NtfsCompleteRequest( IrpContext, Irp, Status );
  718. return Status;
  719. }
  720. //
  721. // Local support routine
  722. //
  723. VOID
  724. NtfsAppendEa (
  725. IN PIRP_CONTEXT IrpContext,
  726. IN OUT PEA_LIST_HEADER EaListHeader,
  727. IN PFILE_FULL_EA_INFORMATION FullEa,
  728. IN PVCB Vcb
  729. )
  730. /*++
  731. Routine Description:
  732. This routine appends a new packed ea onto an existing ea list,
  733. it also will allocate/dealloate pool as necessary to hold the ea list.
  734. Arguments:
  735. EaListHeader - Supplies a pointer the Ea list header structure.
  736. FullEa - Supplies a pointer to the new full ea that is to be appended
  737. to the ea list.
  738. Vcb - Vcb for this volume.
  739. Return Value:
  740. None.
  741. --*/
  742. {
  743. ULONG UnpackedEaLength;
  744. STRING EaName;
  745. PFILE_FULL_EA_INFORMATION ThisEa;
  746. PAGED_CODE();
  747. DebugTrace( +1, Dbg, ("NtfsAppendEa...\n") );
  748. UnpackedEaLength = AlignedUnpackedEaSize( FullEa );
  749. //
  750. // As a quick check see if the computed packed ea size plus the
  751. // current ea list size will overflow the buffer.
  752. //
  753. if (UnpackedEaLength + EaListHeader->UnpackedEaSize > EaListHeader->BufferSize) {
  754. //
  755. // We will overflow our current work buffer so allocate a larger
  756. // one and copy over the current buffer
  757. //
  758. PVOID Temp;
  759. ULONG NewAllocationSize;
  760. DebugTrace( 0, Dbg, ("Allocate a new ea list buffer\n") );
  761. //
  762. // Compute a new size and allocate space. Always increase the
  763. // allocation in cluster increments.
  764. //
  765. NewAllocationSize = ClusterAlign( Vcb,
  766. UnpackedEaLength
  767. + EaListHeader->UnpackedEaSize );
  768. Temp = NtfsAllocatePool(PagedPool, NewAllocationSize );
  769. //
  770. // Move over the existing ea list and zero the remaining space.
  771. //
  772. RtlCopyMemory( Temp,
  773. EaListHeader->FullEa,
  774. EaListHeader->BufferSize );
  775. RtlZeroMemory( Add2Ptr( Temp, EaListHeader->BufferSize ),
  776. NewAllocationSize - EaListHeader->BufferSize );
  777. //
  778. // Deallocate the current Ea list and use the freshly allocated list.
  779. //
  780. if (EaListHeader->FullEa != NULL) {
  781. NtfsFreePool( EaListHeader->FullEa );
  782. }
  783. EaListHeader->FullEa = Temp;
  784. EaListHeader->BufferSize = NewAllocationSize;
  785. }
  786. //
  787. // Determine if we need to increment our need ea changes count
  788. //
  789. if (FlagOn( FullEa->Flags, FILE_NEED_EA )) {
  790. EaListHeader->NeedEaCount += 1;
  791. }
  792. //
  793. // Now copy over the ea.
  794. //
  795. // Before:
  796. // UsedSize Allocated
  797. // | |
  798. // V V
  799. // +xxxxxxxx+-----------------------------+
  800. //
  801. // After:
  802. // UsedSize Allocated
  803. // | |
  804. // V V
  805. // +xxxxxxxx+yyyyyyyyyyyyyyyy+------------+
  806. //
  807. ThisEa = (PFILE_FULL_EA_INFORMATION) Add2Ptr( EaListHeader->FullEa,
  808. EaListHeader->UnpackedEaSize );
  809. RtlCopyMemory( ThisEa,
  810. FullEa,
  811. UnpackedEaLength );
  812. //
  813. // We always store the offset of this Ea in the next entry offset field.
  814. //
  815. ThisEa->NextEntryOffset = UnpackedEaLength;
  816. //
  817. // Upcase the name.
  818. //
  819. EaName.MaximumLength = EaName.Length = ThisEa->EaNameLength;
  820. EaName.Buffer = &ThisEa->EaName[0];
  821. NtfsUpcaseEaName( &EaName, &EaName );
  822. //
  823. // Increment the used size in the ea list structure
  824. //
  825. EaListHeader->UnpackedEaSize += UnpackedEaLength;
  826. EaListHeader->PackedEaSize += PackedEaSize( FullEa );
  827. //
  828. // And return to our caller
  829. //
  830. DebugTrace( -1, Dbg, ("NtfsAppendEa -> VOID\n") );
  831. return;
  832. UNREFERENCED_PARAMETER( IrpContext );
  833. }
  834. //
  835. // Local support routine
  836. //
  837. VOID
  838. NtfsDeleteEa (
  839. IN PIRP_CONTEXT IrpContext,
  840. IN OUT PEA_LIST_HEADER EaListHeader,
  841. IN ULONG Offset
  842. )
  843. /*++
  844. Routine Description:
  845. This routine deletes an individual packed ea from the supplied
  846. ea list.
  847. Arguments:
  848. EaListHeader - Supplies a pointer to the Ea list header structure.
  849. Offset - Supplies the offset to the individual ea in the list to delete
  850. Return Value:
  851. None.
  852. --*/
  853. {
  854. PFILE_FULL_EA_INFORMATION ThisEa;
  855. ULONG UnpackedEaLength;
  856. PAGED_CODE();
  857. DebugTrace( +1, Dbg, ("NtfsDeletePackedEa, Offset = %08lx\n", Offset) );
  858. //
  859. // Get a reference to the Ea to delete.
  860. //
  861. ThisEa = Add2Ptr( EaListHeader->FullEa, Offset );
  862. //
  863. // Determine if we need to decrement our need ea changes count
  864. //
  865. if (FlagOn( ThisEa->Flags, FILE_NEED_EA )) {
  866. EaListHeader->NeedEaCount--;
  867. }
  868. //
  869. // Decrement the Ea size values.
  870. //
  871. EaListHeader->PackedEaSize -= PackedEaSize( ThisEa );
  872. UnpackedEaLength = AlignedUnpackedEaSize( ThisEa );
  873. EaListHeader->UnpackedEaSize -= UnpackedEaLength;
  874. //
  875. // Shrink the ea list over the deleted ea. The amount to copy is the
  876. // total size of the ea list minus the offset to the end of the ea
  877. // we're deleting.
  878. //
  879. // Before:
  880. // Offset Offset+UnpackedEaLength UsedSize Allocated
  881. // | | | |
  882. // V V V V
  883. // +xxxxxxxx+yyyyyyyyyyyyyyyy+zzzzzzzzzzzzzzzzzz+------------+
  884. //
  885. // After
  886. // Offset UsedSize Allocated
  887. // | | |
  888. // V V V
  889. // +xxxxxxxx+zzzzzzzzzzzzzzzzzz+-----------------------------+
  890. //
  891. RtlMoveMemory( ThisEa,
  892. Add2Ptr( ThisEa, ThisEa->NextEntryOffset ),
  893. EaListHeader->UnpackedEaSize - Offset );
  894. //
  895. // And zero out the remaing part of the ea list, to make things
  896. // nice and more robust
  897. //
  898. RtlZeroMemory( Add2Ptr( EaListHeader->FullEa, EaListHeader->UnpackedEaSize ),
  899. UnpackedEaLength );
  900. //
  901. // And return to our caller
  902. //
  903. DebugTrace( -1, Dbg, ("NtfsDeleteEa -> VOID\n") );
  904. return;
  905. UNREFERENCED_PARAMETER( IrpContext );
  906. }
  907. //
  908. // Local support routine
  909. //
  910. BOOLEAN
  911. NtfsLocateEaByName (
  912. IN PFILE_FULL_EA_INFORMATION FullEa,
  913. IN ULONG EaBufferLength,
  914. IN PSTRING EaName,
  915. OUT PULONG Offset
  916. )
  917. /*++
  918. Routine Description:
  919. This routine locates the offset for the next individual packed ea
  920. inside of a ea list, given the name of the ea to locate.
  921. Arguments:
  922. FullEa - Pointer to the first Ea to look at.
  923. EaBufferLength - This is the ulong-aligned size of the Ea buffer.
  924. EaName - Supplies the name of the ea search for
  925. Offset - Receives the offset to the located individual ea in the list
  926. if one exists.
  927. Return Value:
  928. BOOLEAN - TRUE if the named ea exists in the list and FALSE
  929. otherwise.
  930. --*/
  931. {
  932. PFILE_FULL_EA_INFORMATION ThisEa;
  933. STRING Name;
  934. PAGED_CODE();
  935. DebugTrace( +1, Dbg, ("NtfsLocateEaByName, EaName = %Z\n", EaName) );
  936. //
  937. // If the Ea list is NULL, there is nothing to do.
  938. //
  939. if (FullEa == NULL) {
  940. DebugTrace( -1, Dbg, ("NtfsLocateEaByName: No work to do\n") );
  941. return FALSE;
  942. }
  943. //
  944. // For each ea in the list check its name against the
  945. // ea name we're searching for
  946. //
  947. *Offset = 0;
  948. //
  949. // We assume there is at least one Ea in the list.
  950. //
  951. do {
  952. ThisEa = Add2Ptr( FullEa, *Offset );
  953. //
  954. // Make a string out of the name in the Ea and compare it to the
  955. // given string.
  956. //
  957. RtlInitString( &Name, &ThisEa->EaName[0] );
  958. if ( RtlCompareString( EaName, &Name, TRUE ) == 0 ) {
  959. DebugTrace( -1, Dbg, ("NtfsLocateEaByName -> TRUE, *Offset = %08lx\n", *Offset) );
  960. return TRUE;
  961. }
  962. //
  963. // Update the offset to get to the next Ea.
  964. //
  965. *Offset += AlignedUnpackedEaSize( ThisEa );
  966. } while ( *Offset < EaBufferLength );
  967. //
  968. // We've exhausted the ea list without finding a match so return false
  969. //
  970. DebugTrace( -1, Dbg, ("NtfsLocateEaByName -> FALSE\n") );
  971. return FALSE;
  972. }
  973. //
  974. // Local support routine.
  975. //
  976. PFILE_FULL_EA_INFORMATION
  977. NtfsMapExistingEas (
  978. IN PIRP_CONTEXT IrpContext,
  979. IN PFCB Fcb,
  980. OUT PBCB *EaBcb,
  981. OUT PULONG EaLength
  982. )
  983. /*++
  984. Routine Description:
  985. This routine maps the current Eas for the file, either through the
  986. Mft record for the file if resident or the Scb for the non-resident
  987. Eas.
  988. Arguments:
  989. Fcb - Pointer to the Fcb for the file whose Ea's are being queried.
  990. EaBcb - Pointer to the Bcb to use if we are mapping data in the
  991. Ea attribute stream file.
  992. EaLength - Returns the length of the unpacked Eas in bytes.
  993. Return Value:
  994. PFILE_FULL_EA_INFORMATION - Pointer to the mapped attributes.
  995. --*/
  996. {
  997. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  998. PFILE_FULL_EA_INFORMATION CurrentEas;
  999. PAGED_CODE();
  1000. DebugTrace( +1, Dbg, ("NtfsMapExistingEas: Entered\n") );
  1001. //
  1002. // We start by looking up the Ea attribute. It better be there.
  1003. //
  1004. NtfsInitializeAttributeContext( &Context );
  1005. if (!NtfsLookupAttributeByCode( IrpContext,
  1006. Fcb,
  1007. &Fcb->FileReference,
  1008. $EA,
  1009. &Context )) {
  1010. //
  1011. // This is a disk corrupt error.
  1012. //
  1013. DebugTrace( -1, Dbg, ("NtfsMapExistingEas: Corrupt disk\n") );
  1014. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  1015. }
  1016. try {
  1017. NtfsMapAttributeValue( IrpContext,
  1018. Fcb,
  1019. (PVOID *)&CurrentEas,
  1020. EaLength,
  1021. EaBcb,
  1022. &Context );
  1023. } finally {
  1024. NtfsCleanupAttributeContext( IrpContext, &Context );
  1025. }
  1026. DebugTrace( -1, Dbg, ("NtfsMapExistingEas: Exit\n") );
  1027. return CurrentEas;
  1028. }
  1029. //
  1030. // Local support routine.
  1031. //
  1032. IO_STATUS_BLOCK
  1033. NtfsQueryEaUserEaList (
  1034. IN PFILE_FULL_EA_INFORMATION CurrentEas,
  1035. IN PEA_INFORMATION EaInformation,
  1036. OUT PFILE_FULL_EA_INFORMATION EaBuffer,
  1037. IN ULONG UserBufferLength,
  1038. IN PFILE_GET_EA_INFORMATION UserEaList,
  1039. IN BOOLEAN ReturnSingleEntry
  1040. )
  1041. /*++
  1042. Routine Description:
  1043. This routine is the work routine for querying EAs given a list
  1044. of Ea's to search for.
  1045. Arguments:
  1046. CurrentEas - This is a pointer to the current Eas for the file
  1047. EaInformation - This is a pointer to an Ea information attribute.
  1048. EaBuffer - Supplies the buffer to receive the full eas
  1049. UserBufferLength - Supplies the length, in bytes, of the user buffer
  1050. UserEaList - Supplies the user specified ea name list
  1051. ReturnSingleEntry - Indicates if we are to return a single entry or not
  1052. Return Value:
  1053. IO_STATUS_BLOCK - Receives the completion status for the operation
  1054. --*/
  1055. {
  1056. IO_STATUS_BLOCK Iosb;
  1057. ULONG GeaOffset;
  1058. ULONG FeaOffset;
  1059. ULONG Offset;
  1060. PFILE_FULL_EA_INFORMATION LastFullEa;
  1061. PFILE_FULL_EA_INFORMATION NextFullEa;
  1062. PFILE_GET_EA_INFORMATION GetEa;
  1063. BOOLEAN Overflow;
  1064. ULONG PrevEaPadding;
  1065. PAGED_CODE();
  1066. DebugTrace( +1, Dbg, ("NtfsQueryEaUserEaList: Entered\n") );
  1067. //
  1068. // Setup pointer in the output buffer so we can track the Ea being
  1069. // written to it and the last Ea written.
  1070. //
  1071. LastFullEa = NULL;
  1072. Overflow = FALSE;
  1073. //
  1074. // Initialize our next offset value.
  1075. //
  1076. GeaOffset = 0;
  1077. Offset = 0;
  1078. PrevEaPadding = 0;
  1079. //
  1080. // Loop through all the entries in the user's ea list.
  1081. //
  1082. while (TRUE) {
  1083. STRING GeaName;
  1084. STRING OutputEaName;
  1085. ULONG RawEaSize;
  1086. //
  1087. // Get the next entry in the user's list.
  1088. //
  1089. GetEa = (PFILE_GET_EA_INFORMATION) Add2Ptr( UserEaList, GeaOffset );
  1090. //
  1091. // Make a string reference to the name and see if we can locate
  1092. // the ea by name.
  1093. //
  1094. GeaName.MaximumLength = GeaName.Length = GetEa->EaNameLength;
  1095. GeaName.Buffer = &GetEa->EaName[0];
  1096. //
  1097. // Upcase the name so we can do a case-insensitive compare.
  1098. //
  1099. NtfsUpcaseEaName( &GeaName, &GeaName );
  1100. //
  1101. // Check for a valid name.
  1102. //
  1103. if (!NtfsIsEaNameValid( GeaName )) {
  1104. DebugTrace( -1, Dbg, ("NtfsQueryEaUserEaList: Invalid Ea Name\n") );
  1105. Iosb.Information = GeaOffset;
  1106. Iosb.Status = STATUS_INVALID_EA_NAME;
  1107. return Iosb;
  1108. }
  1109. GeaOffset += GetEa->NextEntryOffset;
  1110. //
  1111. // If this is a duplicate name, then step over this entry.
  1112. //
  1113. if (NtfsIsDuplicateGeaName( GetEa, UserEaList )) {
  1114. //
  1115. // If we've exhausted the entries in the Get Ea list, then we are
  1116. // done.
  1117. //
  1118. if (GetEa->NextEntryOffset == 0) {
  1119. break;
  1120. } else {
  1121. continue;
  1122. }
  1123. }
  1124. //
  1125. // Generate a pointer in the Ea buffer.
  1126. //
  1127. NextFullEa = (PFILE_FULL_EA_INFORMATION) Add2Ptr( EaBuffer, Offset + PrevEaPadding );
  1128. //
  1129. // Try to find a matching Ea.
  1130. // If we couldn't, let's dummy up an Ea to give to the user.
  1131. //
  1132. if (!NtfsLocateEaByName( CurrentEas,
  1133. EaInformation->UnpackedEaSize,
  1134. &GeaName,
  1135. &FeaOffset )) {
  1136. //
  1137. // We were not able to locate the name therefore we must
  1138. // dummy up a entry for the query. The needed Ea size is
  1139. // the size of the name + 4 (next entry offset) + 1 (flags)
  1140. // + 1 (name length) + 2 (value length) + the name length +
  1141. // 1 (null byte).
  1142. //
  1143. RawEaSize = 4+1+1+2+GetEa->EaNameLength+1;
  1144. if ((RawEaSize + PrevEaPadding) > UserBufferLength) {
  1145. Overflow = TRUE;
  1146. break;
  1147. }
  1148. //
  1149. // Everything is going to work fine, so copy over the name,
  1150. // set the name length and zero out the rest of the ea.
  1151. //
  1152. NextFullEa->NextEntryOffset = 0;
  1153. NextFullEa->Flags = 0;
  1154. NextFullEa->EaNameLength = GetEa->EaNameLength;
  1155. NextFullEa->EaValueLength = 0;
  1156. RtlCopyMemory( &NextFullEa->EaName[0],
  1157. &GetEa->EaName[0],
  1158. GetEa->EaNameLength );
  1159. //
  1160. // Upcase the name in the buffer.
  1161. //
  1162. OutputEaName.MaximumLength = OutputEaName.Length = GeaName.Length;
  1163. OutputEaName.Buffer = NextFullEa->EaName;
  1164. NtfsUpcaseEaName( &OutputEaName, &OutputEaName );
  1165. NextFullEa->EaName[GetEa->EaNameLength] = 0;
  1166. //
  1167. // Otherwise return the Ea we found back to the user.
  1168. //
  1169. } else {
  1170. PFILE_FULL_EA_INFORMATION ThisEa;
  1171. //
  1172. // Reference this ea.
  1173. //
  1174. ThisEa = (PFILE_FULL_EA_INFORMATION) Add2Ptr( CurrentEas, FeaOffset );
  1175. //
  1176. // Check if this Ea can fit in the user's buffer.
  1177. //
  1178. RawEaSize = RawUnpackedEaSize( ThisEa );
  1179. if (RawEaSize > (UserBufferLength - PrevEaPadding)) {
  1180. Overflow = TRUE;
  1181. break;
  1182. }
  1183. //
  1184. // Copy this ea to the user's buffer.
  1185. //
  1186. RtlCopyMemory( NextFullEa,
  1187. ThisEa,
  1188. RawEaSize);
  1189. NextFullEa->NextEntryOffset = 0;
  1190. }
  1191. //
  1192. // Compute the next offset in the user's buffer.
  1193. //
  1194. Offset += (RawEaSize + PrevEaPadding);
  1195. //
  1196. // If we were to return a single entry then break out of our loop
  1197. // now
  1198. //
  1199. if (ReturnSingleEntry) {
  1200. break;
  1201. }
  1202. //
  1203. // If we have a new Ea entry, go back and update the offset field
  1204. // of the previous Ea entry.
  1205. //
  1206. if (LastFullEa != NULL) {
  1207. LastFullEa->NextEntryOffset = PtrOffset( LastFullEa, NextFullEa );
  1208. }
  1209. //
  1210. // If we've exhausted the entries in the Get Ea list, then we are
  1211. // done.
  1212. //
  1213. if (GetEa->NextEntryOffset == 0) {
  1214. break;
  1215. }
  1216. //
  1217. // Remember this as the previous ea value. Also update the buffer
  1218. // length values and the buffer offset values.
  1219. //
  1220. LastFullEa = NextFullEa;
  1221. UserBufferLength -= (RawEaSize + PrevEaPadding);
  1222. //
  1223. // Now remember the padding bytes needed for this call.
  1224. //
  1225. PrevEaPadding = LongAlign( RawEaSize ) - RawEaSize;
  1226. }
  1227. //
  1228. // If the Ea information won't fit in the user's buffer, then return
  1229. // an overflow status.
  1230. //
  1231. if (Overflow) {
  1232. Iosb.Information = 0;
  1233. Iosb.Status = STATUS_BUFFER_OVERFLOW;
  1234. //
  1235. // Otherwise return the length of the data returned.
  1236. //
  1237. } else {
  1238. //
  1239. // Return the length of the buffer filled and a success
  1240. // status.
  1241. //
  1242. Iosb.Information = Offset;
  1243. Iosb.Status = STATUS_SUCCESS;
  1244. }
  1245. DebugTrace( 0, Dbg, ("Status -> %08lx\n", Iosb.Status) );
  1246. DebugTrace( 0, Dbg, ("Information -> %08lx\n", Iosb.Information) );
  1247. DebugTrace( -1, Dbg, ("NtfsQueryEaUserEaList: Exit\n") );
  1248. return Iosb;
  1249. }
  1250. //
  1251. // Local support routine
  1252. //
  1253. IO_STATUS_BLOCK
  1254. NtfsQueryEaIndexSpecified (
  1255. OUT PCCB Ccb,
  1256. IN PFILE_FULL_EA_INFORMATION CurrentEas,
  1257. IN PEA_INFORMATION EaInformation,
  1258. OUT PFILE_FULL_EA_INFORMATION EaBuffer,
  1259. IN ULONG UserBufferLength,
  1260. IN ULONG UserEaIndex,
  1261. IN BOOLEAN ReturnSingleEntry
  1262. )
  1263. /*++
  1264. Routine Description:
  1265. This routine is the work routine for querying EAs given an ea index
  1266. Arguments:
  1267. Ccb - This is the Ccb for the caller.
  1268. CurrentEas - This is a pointer to the current Eas for the file.
  1269. EaInformation - This is a pointer to an Ea information attribute.
  1270. EaBuffer - Supplies the buffer to receive the full eas
  1271. UserBufferLength - Supplies the length, in bytes, of the user buffer
  1272. UserEaIndex - This is the Index for the first ea to return. The value
  1273. 1 indicates the first ea of the file.
  1274. ReturnSingleEntry - Indicates if we are to return a single entry or not
  1275. Return Value:
  1276. IO_STATUS_BLOCK - Receives the completion status for the operation
  1277. --*/
  1278. {
  1279. IO_STATUS_BLOCK Iosb;
  1280. ULONG i;
  1281. ULONG Offset;
  1282. PFILE_FULL_EA_INFORMATION ThisEa;
  1283. PAGED_CODE();
  1284. DebugTrace( +1, Dbg, ("NtfsQueryEaIndexSpecified: Entered\n") );
  1285. i = 1;
  1286. Offset = 0;
  1287. ThisEa = NULL;
  1288. //
  1289. // If the index value is zero, there are no Eas to return.
  1290. //
  1291. if (UserEaIndex == 0
  1292. || EaInformation->UnpackedEaSize == 0) {
  1293. DebugTrace( -1, Dbg, ("NtfsQueryEaIndexSpecified: Non-existant entry\n") );
  1294. Iosb.Information = 0;
  1295. Iosb.Status = STATUS_NONEXISTENT_EA_ENTRY;
  1296. return Iosb;
  1297. }
  1298. //
  1299. // Walk through the CurrentEas until we find the starting Ea offset.
  1300. //
  1301. while (i < UserEaIndex
  1302. && Offset < EaInformation->UnpackedEaSize) {
  1303. ThisEa = (PFILE_FULL_EA_INFORMATION) Add2Ptr( CurrentEas, Offset );
  1304. Offset += AlignedUnpackedEaSize( ThisEa );
  1305. i += 1;
  1306. }
  1307. if (Offset >= EaInformation->UnpackedEaSize) {
  1308. //
  1309. // If we just passed the last Ea, we will return STATUS_NO_MORE_EAS.
  1310. // This is for the caller who may be enumerating the Eas.
  1311. //
  1312. if (i == UserEaIndex) {
  1313. Iosb.Status = STATUS_NO_MORE_EAS;
  1314. //
  1315. // Otherwise we report that this is a bad ea index.
  1316. //
  1317. } else {
  1318. Iosb.Status = STATUS_NONEXISTENT_EA_ENTRY;
  1319. }
  1320. DebugTrace( -1, Dbg, ("NtfsQueryEaIndexSpecified -> %08lx\n", Iosb.Status) );
  1321. return Iosb;
  1322. }
  1323. //
  1324. // We now have the offset of the first Ea to return to the user.
  1325. // We simply call our EaSimpleScan routine to do the actual work.
  1326. //
  1327. Iosb = NtfsQueryEaSimpleScan( Ccb,
  1328. CurrentEas,
  1329. EaInformation,
  1330. EaBuffer,
  1331. UserBufferLength,
  1332. ReturnSingleEntry,
  1333. Offset );
  1334. DebugTrace( -1, Dbg, ("NtfsQueryEaIndexSpecified: Exit\n") );
  1335. return Iosb;
  1336. }
  1337. //
  1338. // Local support routine
  1339. //
  1340. IO_STATUS_BLOCK
  1341. NtfsQueryEaSimpleScan (
  1342. OUT PCCB Ccb,
  1343. IN PFILE_FULL_EA_INFORMATION CurrentEas,
  1344. IN PEA_INFORMATION EaInformation,
  1345. OUT PFILE_FULL_EA_INFORMATION EaBuffer,
  1346. IN ULONG UserBufferLength,
  1347. IN BOOLEAN ReturnSingleEntry,
  1348. IN ULONG StartingOffset
  1349. )
  1350. /*++
  1351. Routine Description:
  1352. This routine is the work routine for querying EAs starting from a given
  1353. offset within the Ea attribute.
  1354. Arguments:
  1355. Ccb - This is the Ccb for the caller.
  1356. CurrentEas - This is a pointer to the current Eas for the file.
  1357. EaInformation - This is a pointer to an Ea information attribute.
  1358. EaBuffer - Supplies the buffer to receive the full eas
  1359. UserBufferLength - Supplies the length, in bytes, of the user buffer
  1360. ReturnSingleEntry - Indicates if we are to return a single entry or not
  1361. StartingOffset - Supplies the offset of the first Ea to return
  1362. Return Value:
  1363. IO_STATUS_BLOCK - Receives the completion status for the operation
  1364. --*/
  1365. {
  1366. IO_STATUS_BLOCK Iosb;
  1367. PFILE_FULL_EA_INFORMATION LastFullEa;
  1368. PFILE_FULL_EA_INFORMATION NextFullEa;
  1369. PFILE_FULL_EA_INFORMATION ThisEa;
  1370. BOOLEAN BufferOverflow = FALSE;
  1371. ULONG BufferOffset;
  1372. ULONG PrevEaPadding;
  1373. PAGED_CODE();
  1374. DebugTrace( +1, Dbg, ("NtfsQueryEaSimpleScan: Entered\n") );
  1375. //
  1376. // Initialize our Ea pointers and the offsets into the user buffer
  1377. // and our Ea buffer.
  1378. //
  1379. LastFullEa = NULL;
  1380. BufferOffset = 0;
  1381. PrevEaPadding = 0;
  1382. //
  1383. // Loop until the Ea offset is beyond the valid range of Eas.
  1384. //
  1385. while (StartingOffset < EaInformation->UnpackedEaSize) {
  1386. ULONG EaSize;
  1387. //
  1388. // Reference the next EA to return.
  1389. //
  1390. ThisEa = (PFILE_FULL_EA_INFORMATION) Add2Ptr( CurrentEas, StartingOffset);
  1391. //
  1392. // If the size of this Ea is greater than the remaining buffer size,
  1393. // we exit the loop. We need to remember to include any padding bytes
  1394. // from the previous Eas.
  1395. //
  1396. EaSize = RawUnpackedEaSize( ThisEa );
  1397. if ((EaSize + PrevEaPadding) > UserBufferLength) {
  1398. BufferOverflow = TRUE;
  1399. break;
  1400. }
  1401. //
  1402. // Copy the Ea into the user's buffer.
  1403. //
  1404. BufferOffset += PrevEaPadding;
  1405. NextFullEa = (PFILE_FULL_EA_INFORMATION) Add2Ptr( EaBuffer, BufferOffset );
  1406. RtlCopyMemory( NextFullEa, ThisEa, EaSize );
  1407. //
  1408. // Move to the next Ea.
  1409. //
  1410. LastFullEa = NextFullEa;
  1411. UserBufferLength -= (EaSize + PrevEaPadding);
  1412. BufferOffset += EaSize;
  1413. StartingOffset += LongAlign( EaSize );
  1414. //
  1415. // Remember the padding needed for this entry.
  1416. //
  1417. PrevEaPadding = LongAlign( EaSize ) - EaSize;
  1418. //
  1419. // If the user only wanted one entry, exit now.
  1420. //
  1421. if (ReturnSingleEntry) {
  1422. break;
  1423. }
  1424. }
  1425. //
  1426. // If we didn't find any entries, it could be because there were no
  1427. // more to find or that we ran out of buffer space.
  1428. //
  1429. if (LastFullEa == NULL) {
  1430. Iosb.Information = 0;
  1431. //
  1432. // We were not able to return a single ea entry, now we need to find
  1433. // out if it is because we didn't have an entry to return or the
  1434. // buffer is too small. If the Offset variable is less than
  1435. // the size of the Ea attribute, then the user buffer is too small.
  1436. //
  1437. if (EaInformation->UnpackedEaSize == 0) {
  1438. Iosb.Status = STATUS_NO_EAS_ON_FILE;
  1439. } else if (StartingOffset >= EaInformation->UnpackedEaSize) {
  1440. Iosb.Status = STATUS_NO_MORE_EAS;
  1441. } else {
  1442. Iosb.Status = STATUS_BUFFER_TOO_SMALL;
  1443. }
  1444. //
  1445. // Otherwise we have returned some Ea's. Update the Iosb to return.
  1446. //
  1447. } else {
  1448. //
  1449. // Update the Ccb to show where to start the next search.
  1450. //
  1451. Ccb->NextEaOffset = StartingOffset;
  1452. //
  1453. // Zero the next entry field of the last Ea.
  1454. //
  1455. LastFullEa->NextEntryOffset = 0;
  1456. //
  1457. // Now update the Iosb.
  1458. //
  1459. Iosb.Information = BufferOffset;
  1460. //
  1461. // If there are more to return, report the buffer was too small.
  1462. // Otherwise return STATUS_SUCCESS.
  1463. //
  1464. if (BufferOverflow) {
  1465. Iosb.Status = STATUS_BUFFER_OVERFLOW;
  1466. } else {
  1467. Iosb.Status = STATUS_SUCCESS;
  1468. }
  1469. }
  1470. DebugTrace( -1, Dbg, ("NtfsQueryEaSimpleScan: Exit\n") );
  1471. return Iosb;
  1472. }
  1473. //
  1474. // Local support routine
  1475. //
  1476. BOOLEAN
  1477. NtfsIsDuplicateGeaName (
  1478. IN PFILE_GET_EA_INFORMATION GetEa,
  1479. IN PFILE_GET_EA_INFORMATION UserGeaBuffer
  1480. )
  1481. /*++
  1482. Routine Description:
  1483. This routine walks through a list of Gea names to find a duplicate name.
  1484. 'GetEa' is an actual position in the list, 'UserGeaBuffer' is the beginning
  1485. of the list. We are only interested in
  1486. previous matching ea names, as the ea information for that ea name
  1487. would have been returned with the previous instance.
  1488. Arguments:
  1489. GetEa - Supplies the Ea name structure for the ea name to match.
  1490. UserGeaBuffer - Supplies a pointer to the user buffer with the list
  1491. of ea names to search for.
  1492. Return Value:
  1493. BOOLEAN - TRUE if a previous match is found, FALSE otherwise.
  1494. --*/
  1495. {
  1496. BOOLEAN DuplicateFound;
  1497. STRING GeaString;
  1498. PFILE_GET_EA_INFORMATION ThisGetEa;
  1499. PAGED_CODE();
  1500. DebugTrace( +1, Dbg, ("NtfsIsDuplicateGeaName: Entered\n") );
  1501. //
  1502. // Set up the string structure.
  1503. //
  1504. GeaString.MaximumLength = GeaString.Length = GetEa->EaNameLength;
  1505. GeaString.Buffer = &GetEa->EaName[0];
  1506. DuplicateFound = FALSE;
  1507. ThisGetEa = UserGeaBuffer;
  1508. //
  1509. // We loop until we reach the given Gea or a match is found.
  1510. //
  1511. while (ThisGetEa != GetEa) {
  1512. STRING ThisGea;
  1513. //
  1514. // Create a string structure for the current Gea.
  1515. //
  1516. ThisGea.MaximumLength = ThisGea.Length = ThisGetEa->EaNameLength;
  1517. ThisGea.Buffer = &ThisGetEa->EaName[0];
  1518. //
  1519. // Check if the Gea names match, exit if they do.
  1520. //
  1521. if (NtfsAreEaNamesEqual( &GeaString,
  1522. &ThisGea )) {
  1523. DuplicateFound = TRUE;
  1524. break;
  1525. }
  1526. //
  1527. // Move to the next Gea entry.
  1528. //
  1529. ThisGetEa = (PFILE_GET_EA_INFORMATION) Add2Ptr( ThisGetEa,
  1530. ThisGetEa->NextEntryOffset );
  1531. }
  1532. DebugTrace( -1, Dbg, ("NtfsIsDuplicateGeaName: Exit\n") );
  1533. return DuplicateFound;
  1534. }
  1535. //
  1536. // Local support routine
  1537. //
  1538. NTSTATUS
  1539. NtfsBuildEaList (
  1540. IN PIRP_CONTEXT IrpContext,
  1541. IN PVCB Vcb,
  1542. IN OUT PEA_LIST_HEADER EaListHeader,
  1543. IN PFILE_FULL_EA_INFORMATION UserEaList,
  1544. OUT PULONG_PTR ErrorOffset
  1545. )
  1546. /*++
  1547. Routine Description:
  1548. This routine is called to build an up-to-date Ea list based on the
  1549. given existing Ea list and the user-specified Ea list.
  1550. Arguments:
  1551. Vcb - The Vcb for the volume.
  1552. EaListHeader - This is the Ea list to modify.
  1553. UserEaList - This is the user specified Ea list.
  1554. ErrorOffset - Supplies the address to store the offset of an invalid
  1555. Ea in the user's list.
  1556. Return Value:
  1557. NTSTATUS - The result of modifying the Ea list.
  1558. --*/
  1559. {
  1560. NTSTATUS Status;
  1561. BOOLEAN MoreEas;
  1562. ULONG Offset;
  1563. PAGED_CODE();
  1564. DebugTrace( +1, Dbg, ("NtfsBuildEaList: Entered\n") );
  1565. Status = STATUS_SUCCESS;
  1566. Offset = 0;
  1567. //
  1568. // Now for each full ea in the input user buffer we do the specified operation
  1569. // on the ea.
  1570. //
  1571. do {
  1572. STRING EaName;
  1573. ULONG EaOffset;
  1574. PFILE_FULL_EA_INFORMATION ThisEa;
  1575. ThisEa = (PFILE_FULL_EA_INFORMATION) Add2Ptr( UserEaList, Offset );
  1576. //
  1577. // Create a string out of the name in the user's Ea.
  1578. //
  1579. EaName.MaximumLength = EaName.Length = ThisEa->EaNameLength;
  1580. EaName.Buffer = &ThisEa->EaName[0];
  1581. //
  1582. // If the Ea isn't valid, return error offset to caller.
  1583. //
  1584. if (!NtfsIsEaNameValid( EaName )) {
  1585. *ErrorOffset = Offset;
  1586. Status = STATUS_INVALID_EA_NAME;
  1587. break;
  1588. }
  1589. //
  1590. // Verify that no invalid ea flags are set.
  1591. //
  1592. if (ThisEa->Flags != 0
  1593. && ThisEa->Flags != FILE_NEED_EA) {
  1594. *ErrorOffset = Offset;
  1595. Status = STATUS_INVALID_EA_NAME;
  1596. break;
  1597. }
  1598. //
  1599. // If we can find the name in the Ea set, we remove it.
  1600. //
  1601. if (NtfsLocateEaByName( EaListHeader->FullEa,
  1602. EaListHeader->UnpackedEaSize,
  1603. &EaName,
  1604. &EaOffset )) {
  1605. NtfsDeleteEa( IrpContext,
  1606. EaListHeader,
  1607. EaOffset );
  1608. }
  1609. //
  1610. // If the user specified a non-zero value length, we add this
  1611. // ea to the in memory Ea list.
  1612. //
  1613. if (ThisEa->EaValueLength != 0) {
  1614. NtfsAppendEa( IrpContext,
  1615. EaListHeader,
  1616. ThisEa,
  1617. Vcb );
  1618. }
  1619. //
  1620. // Move to the next Ea in the list.
  1621. //
  1622. Offset += AlignedUnpackedEaSize( ThisEa );
  1623. MoreEas = (BOOLEAN) (ThisEa->NextEntryOffset != 0);
  1624. } while( MoreEas );
  1625. //
  1626. // First we check that the packed size of the Eas does not exceed the
  1627. // maximum value. We have to reserve the 4 bytes for the OS/2 list
  1628. // header.
  1629. //
  1630. if (NT_SUCCESS( Status )) {
  1631. if (EaListHeader->PackedEaSize > (MAXIMUM_EA_SIZE - 4)) {
  1632. Status = STATUS_EA_TOO_LARGE;
  1633. }
  1634. }
  1635. DebugTrace( -1, Dbg, ("NtfsBuildEaList: Exit\n") );
  1636. return Status;
  1637. }
  1638. //
  1639. // Local support routine
  1640. //
  1641. VOID
  1642. NtfsReplaceFileEas (
  1643. IN PIRP_CONTEXT IrpContext,
  1644. IN PFCB Fcb,
  1645. IN PEA_LIST_HEADER EaList
  1646. )
  1647. /*++
  1648. Routine Description:
  1649. This routine will replace an existing Ea list with a new Ea list. It
  1650. correctly handles the case where there was no previous Eas and where we
  1651. are removing all of the previous EAs.
  1652. Arguments:
  1653. Fcb - Fcb for the file with the EAs
  1654. EaList - This contains the modified Ea list.
  1655. Return Value:
  1656. None.
  1657. --*/
  1658. {
  1659. EA_INFORMATION ThisEaInformation;
  1660. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  1661. PSCB EaScb;
  1662. BOOLEAN EaChange = FALSE;
  1663. BOOLEAN EaScbAcquired = FALSE;
  1664. PAGED_CODE();
  1665. DebugTrace( +1, Dbg, ("NtfsReplaceFileEas: Entered\n") );
  1666. ThisEaInformation.PackedEaSize = (USHORT) EaList->PackedEaSize;
  1667. ThisEaInformation.UnpackedEaSize = EaList->UnpackedEaSize;
  1668. ThisEaInformation.NeedEaCount = EaList->NeedEaCount;
  1669. NtfsInitializeAttributeContext( &Context );
  1670. //
  1671. // First we handle $EA_INFORMATION and then the $EA attribute in the
  1672. // same fashion.
  1673. //
  1674. try {
  1675. //
  1676. // Lookup the $EA_INFORMATION attribute. If it does not exist then we
  1677. // will need to create one.
  1678. //
  1679. if (!NtfsLookupAttributeByCode( IrpContext,
  1680. Fcb,
  1681. &Fcb->FileReference,
  1682. $EA_INFORMATION,
  1683. &Context )) {
  1684. if (EaList->UnpackedEaSize != 0) {
  1685. DebugTrace( 0, Dbg, ("Create a new $EA_INFORMATION attribute\n") );
  1686. NtfsCleanupAttributeContext( IrpContext, &Context );
  1687. NtfsInitializeAttributeContext( &Context );
  1688. NtfsCreateAttributeWithValue( IrpContext,
  1689. Fcb,
  1690. $EA_INFORMATION,
  1691. NULL, // attribute name
  1692. &ThisEaInformation,
  1693. sizeof(EA_INFORMATION),
  1694. 0, // attribute flags
  1695. NULL, // where indexed
  1696. TRUE, // logit
  1697. &Context );
  1698. EaChange = TRUE;
  1699. }
  1700. } else {
  1701. //
  1702. // If it exists, and we are writing an EA, then we have to update it.
  1703. //
  1704. if (EaList->UnpackedEaSize != 0) {
  1705. DebugTrace( 0, Dbg, ("Change an existing $EA_INFORMATION attribute\n") );
  1706. NtfsChangeAttributeValue( IrpContext,
  1707. Fcb,
  1708. 0, // Value offset
  1709. &ThisEaInformation,
  1710. sizeof(EA_INFORMATION),
  1711. TRUE, // SetNewLength
  1712. TRUE, // LogNonResidentToo
  1713. FALSE, // CreateSectionUnderway
  1714. FALSE,
  1715. &Context );
  1716. //
  1717. // If it exists, but our new length is zero, then delete it.
  1718. //
  1719. } else {
  1720. DebugTrace( 0, Dbg, ("Delete existing $EA_INFORMATION attribute\n") );
  1721. NtfsDeleteAttributeRecord( IrpContext,
  1722. Fcb,
  1723. DELETE_LOG_OPERATION |
  1724. DELETE_RELEASE_FILE_RECORD |
  1725. DELETE_RELEASE_ALLOCATION,
  1726. &Context );
  1727. }
  1728. EaChange = TRUE;
  1729. }
  1730. //
  1731. // Now we will cleanup and reinitialize the context for reuse.
  1732. //
  1733. NtfsCleanupAttributeContext( IrpContext, &Context );
  1734. NtfsInitializeAttributeContext( &Context );
  1735. //
  1736. // Lookup the $EA attribute. If it does not exist then we will need to create
  1737. // one.
  1738. //
  1739. if (!NtfsLookupAttributeByCode( IrpContext,
  1740. Fcb,
  1741. &Fcb->FileReference,
  1742. $EA,
  1743. &Context )) {
  1744. if (EaList->UnpackedEaSize != 0) {
  1745. DebugTrace( 0, Dbg, ("Create a new $EA attribute\n") );
  1746. NtfsCleanupAttributeContext( IrpContext, &Context );
  1747. NtfsInitializeAttributeContext( &Context );
  1748. NtfsCreateAttributeWithValue( IrpContext,
  1749. Fcb,
  1750. $EA,
  1751. NULL, // attribute name
  1752. EaList->FullEa,
  1753. EaList->UnpackedEaSize,
  1754. 0, // attribute flags
  1755. NULL, // where indexed
  1756. TRUE, // logit
  1757. &Context );
  1758. EaChange = TRUE;
  1759. }
  1760. } else {
  1761. //
  1762. // If it exists, and we are writing an EA, then we have to update it.
  1763. //
  1764. if (EaList->UnpackedEaSize != 0) {
  1765. DebugTrace( 0, Dbg, ("Change an existing $EA attribute\n") );
  1766. NtfsChangeAttributeValue( IrpContext,
  1767. Fcb,
  1768. 0, // Value offset
  1769. EaList->FullEa,
  1770. EaList->UnpackedEaSize,
  1771. TRUE, // SetNewLength
  1772. TRUE, // LogNonResidentToo
  1773. FALSE, // CreateSectionUnderway
  1774. FALSE,
  1775. &Context );
  1776. //
  1777. // If it exists, but our new length is zero, then delete it.
  1778. //
  1779. } else {
  1780. DebugTrace( 0, Dbg, ("Delete existing $EA attribute\n") );
  1781. //
  1782. // If the stream is non-resident then get hold of an
  1783. // Scb for this.
  1784. //
  1785. if (!NtfsIsAttributeResident( NtfsFoundAttribute( &Context ))) {
  1786. EaScb = NtfsCreateScb( IrpContext,
  1787. Fcb,
  1788. $EA,
  1789. &NtfsEmptyString,
  1790. FALSE,
  1791. NULL );
  1792. NtfsAcquireExclusiveScb( IrpContext, EaScb );
  1793. EaScbAcquired = TRUE;
  1794. }
  1795. NtfsDeleteAttributeRecord( IrpContext,
  1796. Fcb,
  1797. DELETE_LOG_OPERATION |
  1798. DELETE_RELEASE_FILE_RECORD |
  1799. DELETE_RELEASE_ALLOCATION,
  1800. &Context );
  1801. //
  1802. // If we have acquired the Scb then knock the sizes back
  1803. // to zero.
  1804. //
  1805. if (EaScbAcquired) {
  1806. EaScb->Header.FileSize =
  1807. EaScb->Header.ValidDataLength =
  1808. EaScb->Header.AllocationSize = Li0;
  1809. SetFlag( EaScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
  1810. }
  1811. }
  1812. EaChange = TRUE;
  1813. }
  1814. //
  1815. // Increment the Modification count for the Eas.
  1816. //
  1817. Fcb->EaModificationCount++;
  1818. if (EaList->UnpackedEaSize == 0) {
  1819. Fcb->Info.PackedEaSize = 0;
  1820. } else {
  1821. Fcb->Info.PackedEaSize = (USHORT) EaList->PackedEaSize;
  1822. }
  1823. //
  1824. // Post a USN journal record for this change
  1825. //
  1826. if (EaChange) {
  1827. NtfsPostUsnChange( IrpContext, Fcb, USN_REASON_EA_CHANGE );
  1828. }
  1829. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_EA_SIZE );
  1830. } finally {
  1831. DebugUnwind( NtfsReplaceFileEas );
  1832. if (EaScbAcquired) {
  1833. NtfsReleaseScb( IrpContext, EaScb );
  1834. }
  1835. //
  1836. // Cleanup our attribute enumeration context
  1837. //
  1838. NtfsCleanupAttributeContext( IrpContext, &Context );
  1839. }
  1840. DebugTrace( -1, Dbg, ("NtfsReplaceFileEas: Exit\n") );
  1841. return;
  1842. }
  1843. BOOLEAN
  1844. NtfsIsEaNameValid (
  1845. IN STRING Name
  1846. )
  1847. /*++
  1848. Routine Description:
  1849. This routine simple returns whether the specified file names conforms
  1850. to the file system specific rules for legal Ea names.
  1851. For Ea names, the following rules apply:
  1852. A. An Ea name may not contain any of the following characters:
  1853. 0x0000 - 0x001F \ / : * ? " < > | , + = [ ] ;
  1854. Arguments:
  1855. Name - Supllies the name to check.
  1856. Return Value:
  1857. BOOLEAN - TRUE if the name is legal, FALSE otherwise.
  1858. --*/
  1859. {
  1860. ULONG Index;
  1861. UCHAR Char;
  1862. PAGED_CODE();
  1863. //
  1864. // Empty names are not valid.
  1865. //
  1866. if ( Name.Length == 0 ) { return FALSE; }
  1867. //
  1868. // At this point we should only have a single name, which can't have
  1869. // more than 254 characters
  1870. //
  1871. if ( Name.Length > 254 ) { return FALSE; }
  1872. for ( Index = 0; Index < (ULONG)Name.Length; Index += 1 ) {
  1873. Char = Name.Buffer[ Index ];
  1874. //
  1875. // Skip over and Dbcs chacters
  1876. //
  1877. if ( FsRtlIsLeadDbcsCharacter( Char ) ) {
  1878. ASSERT( Index != (ULONG)(Name.Length - 1) );
  1879. Index += 1;
  1880. continue;
  1881. }
  1882. //
  1883. // Make sure this character is legal, and if a wild card, that
  1884. // wild cards are permissible.
  1885. //
  1886. if ( !FsRtlIsAnsiCharacterLegalFat(Char, FALSE) ) {
  1887. return FALSE;
  1888. }
  1889. }
  1890. return TRUE;
  1891. }