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.

10925 lines
325 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. FileInfo.c
  5. Abstract:
  6. This module implements the set and query file information routines for Ntfs
  7. called by the dispatch driver.
  8. Author:
  9. Brian Andrew [BrianAn] 15-Jan-1992
  10. Revision History:
  11. --*/
  12. #include "NtfsProc.h"
  13. //
  14. // The Bug check file id for this module
  15. //
  16. #define BugCheckFileId (NTFS_BUG_CHECK_FILEINFO)
  17. //
  18. // The local debug trace level
  19. //
  20. #define Dbg (DEBUG_TRACE_FILEINFO)
  21. //
  22. // Define a tag for general pool allocations from this module
  23. //
  24. #undef MODULE_POOL_TAG
  25. #define MODULE_POOL_TAG ('FFtN')
  26. #define SIZEOF_FILE_NAME_INFORMATION (FIELD_OFFSET( FILE_NAME_INFORMATION, FileName[0]) \
  27. + sizeof( WCHAR ))
  28. //
  29. // Local flags for rename and set link
  30. //
  31. #define TRAVERSE_MATCH (0x00000001)
  32. #define EXACT_CASE_MATCH (0x00000002)
  33. #define ACTIVELY_REMOVE_SOURCE_LINK (0x00000004)
  34. #define REMOVE_SOURCE_LINK (0x00000008)
  35. #define REMOVE_TARGET_LINK (0x00000010)
  36. #define ADD_TARGET_LINK (0x00000020)
  37. #define REMOVE_TRAVERSE_LINK (0x00000040)
  38. #define REUSE_TRAVERSE_LINK (0x00000080)
  39. #define MOVE_TO_NEW_DIR (0x00000100)
  40. #define ADD_PRIMARY_LINK (0x00000200)
  41. #define OVERWRITE_SOURCE_LINK (0x00000400)
  42. //
  43. // Additional local flags for set link
  44. //
  45. #define CREATE_IN_NEW_DIR (0x00000400)
  46. //
  47. // Local procedure prototypes
  48. //
  49. //
  50. // VOID
  51. // NtfsBuildLastFileName (
  52. // IN PIRP_CONTEXT IrpContext,
  53. // IN PFILE_OBJECT FileObject,
  54. // IN ULONG FileNameOffset,
  55. // OUT PUNICODE_STRING FileName
  56. // );
  57. //
  58. #define NtfsBuildLastFileName(IC,FO,OFF,FN) { \
  59. (FN)->MaximumLength = (FN)->Length = (FO)->FileName.Length - OFF; \
  60. (FN)->Buffer = (PWSTR) Add2Ptr( (FO)->FileName.Buffer, OFF ); \
  61. }
  62. VOID
  63. NtfsQueryBasicInfo (
  64. IN PIRP_CONTEXT IrpContext,
  65. IN PFILE_OBJECT FileObject,
  66. IN PSCB Scb,
  67. IN OUT PFILE_BASIC_INFORMATION Buffer,
  68. IN OUT PULONG Length
  69. );
  70. VOID
  71. NtfsQueryStandardInfo (
  72. IN PIRP_CONTEXT IrpContext,
  73. IN PFILE_OBJECT FileObject,
  74. IN PSCB Scb,
  75. IN OUT PFILE_STANDARD_INFORMATION Buffer,
  76. IN OUT PULONG Length,
  77. IN PCCB Ccb OPTIONAL
  78. );
  79. VOID
  80. NtfsQueryInternalInfo (
  81. IN PIRP_CONTEXT IrpContext,
  82. IN PFILE_OBJECT FileObject,
  83. IN PSCB Scb,
  84. IN OUT PFILE_INTERNAL_INFORMATION Buffer,
  85. IN OUT PULONG Length
  86. );
  87. VOID
  88. NtfsQueryEaInfo (
  89. IN PIRP_CONTEXT IrpContext,
  90. IN PFILE_OBJECT FileObject,
  91. IN PSCB Scb,
  92. IN OUT PFILE_EA_INFORMATION Buffer,
  93. IN OUT PULONG Length
  94. );
  95. VOID
  96. NtfsQueryAttributeTagInfo (
  97. IN PIRP_CONTEXT IrpContext,
  98. IN PFILE_OBJECT FileObject,
  99. IN PSCB Scb,
  100. IN PCCB Ccb,
  101. IN OUT PFILE_ATTRIBUTE_TAG_INFORMATION Buffer,
  102. IN OUT PULONG Length
  103. );
  104. VOID
  105. NtfsQueryPositionInfo (
  106. IN PIRP_CONTEXT IrpContext,
  107. IN PFILE_OBJECT FileObject,
  108. IN PSCB Scb,
  109. IN OUT PFILE_POSITION_INFORMATION Buffer,
  110. IN OUT PULONG Length
  111. );
  112. NTSTATUS
  113. NtfsQueryNameInfo (
  114. IN PIRP_CONTEXT IrpContext,
  115. IN PFILE_OBJECT FileObject,
  116. IN PSCB Scb,
  117. IN OUT PFILE_NAME_INFORMATION Buffer,
  118. IN OUT PULONG Length,
  119. IN PCCB Ccb
  120. );
  121. NTSTATUS
  122. NtfsQueryAlternateNameInfo (
  123. IN PIRP_CONTEXT IrpContext,
  124. IN PSCB Scb,
  125. IN PLCB Lcb,
  126. IN OUT PFILE_NAME_INFORMATION Buffer,
  127. IN OUT PULONG Length
  128. );
  129. NTSTATUS
  130. NtfsQueryStreamsInfo (
  131. IN PIRP_CONTEXT IrpContext,
  132. IN PFCB Fcb,
  133. IN OUT PFILE_STREAM_INFORMATION Buffer,
  134. IN OUT PULONG Length
  135. );
  136. NTSTATUS
  137. NtfsQueryCompressedFileSize (
  138. IN PIRP_CONTEXT IrpContext,
  139. IN PSCB Scb,
  140. IN OUT PFILE_COMPRESSION_INFORMATION Buffer,
  141. IN OUT PULONG Length
  142. );
  143. VOID
  144. NtfsQueryNetworkOpenInfo (
  145. IN PIRP_CONTEXT IrpContext,
  146. IN PFILE_OBJECT FileObject,
  147. IN PSCB Scb,
  148. IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer,
  149. IN OUT PULONG Length
  150. );
  151. NTSTATUS
  152. NtfsSetBasicInfo (
  153. IN PIRP_CONTEXT IrpContext,
  154. IN PFILE_OBJECT FileObject,
  155. IN PIRP Irp,
  156. IN PSCB Scb,
  157. IN PCCB Ccb
  158. );
  159. NTSTATUS
  160. NtfsSetDispositionInfo (
  161. IN PIRP_CONTEXT IrpContext,
  162. IN PFILE_OBJECT FileObject,
  163. IN PIRP Irp,
  164. IN PSCB Scb,
  165. IN PCCB Ccb
  166. );
  167. NTSTATUS
  168. NtfsSetRenameInfo (
  169. IN PIRP_CONTEXT IrpContext,
  170. IN PFILE_OBJECT FileObject,
  171. IN PIRP Irp,
  172. IN PVCB Vcb,
  173. IN PSCB Scb,
  174. IN PCCB Ccb,
  175. IN OUT PBOOLEAN VcbAcquired
  176. );
  177. NTSTATUS
  178. NtfsSetLinkInfo (
  179. IN PIRP_CONTEXT IrpContext,
  180. IN PIRP Irp,
  181. IN PVCB Vcb,
  182. IN PSCB Scb,
  183. IN PCCB Ccb,
  184. IN OUT PBOOLEAN VcbAcquired
  185. );
  186. NTSTATUS
  187. NtfsSetShortNameInfo (
  188. IN PIRP_CONTEXT IrpContext,
  189. IN PFILE_OBJECT FileObject,
  190. IN PIRP Irp,
  191. IN PVCB Vcb,
  192. IN PSCB Scb,
  193. IN PCCB Ccb
  194. );
  195. NTSTATUS
  196. NtfsSetPositionInfo (
  197. IN PIRP_CONTEXT IrpContext,
  198. IN PFILE_OBJECT FileObject,
  199. IN PIRP Irp,
  200. IN PSCB Scb
  201. );
  202. NTSTATUS
  203. NtfsSetAllocationInfo (
  204. IN PIRP_CONTEXT IrpContext,
  205. IN PFILE_OBJECT FileObject,
  206. IN PIRP Irp,
  207. IN PSCB Scb,
  208. IN PCCB Ccb
  209. );
  210. NTSTATUS
  211. NtfsSetEndOfFileInfo (
  212. IN PIRP_CONTEXT IrpContext,
  213. IN PFILE_OBJECT FileObject,
  214. IN PIRP Irp,
  215. IN PSCB Scb,
  216. IN PCCB Ccb OPTIONAL,
  217. IN BOOLEAN VcbAcquired
  218. );
  219. NTSTATUS
  220. NtfsSetValidDataLengthInfo (
  221. IN PIRP_CONTEXT IrpContext,
  222. IN PIRP Irp,
  223. IN PSCB Scb,
  224. IN PCCB Ccb
  225. );
  226. NTSTATUS
  227. NtfsCheckScbForLinkRemoval (
  228. IN PSCB Scb,
  229. OUT PSCB *BatchOplockScb,
  230. OUT PULONG BatchOplockCount
  231. );
  232. VOID
  233. NtfsFindTargetElements (
  234. IN PIRP_CONTEXT IrpContext,
  235. IN PFILE_OBJECT TargetFileObject,
  236. IN PSCB ParentScb,
  237. OUT PSCB *TargetParentScb,
  238. OUT PUNICODE_STRING FullTargetFileName,
  239. OUT PUNICODE_STRING TargetFileName
  240. );
  241. BOOLEAN
  242. NtfsCheckLinkForNewLink (
  243. IN PFCB Fcb,
  244. IN PFILE_NAME FileNameAttr,
  245. IN FILE_REFERENCE FileReference,
  246. IN PUNICODE_STRING NewLinkName,
  247. OUT PULONG LinkFlags
  248. );
  249. VOID
  250. NtfsCheckLinkForRename (
  251. IN PFCB Fcb,
  252. IN PLCB Lcb,
  253. IN PFILE_NAME FileNameAttr,
  254. IN FILE_REFERENCE FileReference,
  255. IN PUNICODE_STRING TargetFileName,
  256. IN BOOLEAN IgnoreCase,
  257. IN OUT PULONG RenameFlags
  258. );
  259. VOID
  260. NtfsCleanupLinkForRemoval (
  261. IN PFCB PreviousFcb,
  262. IN PSCB ParentScb,
  263. IN BOOLEAN ExistingFcb
  264. );
  265. VOID
  266. NtfsUpdateFcbFromLinkRemoval (
  267. IN PIRP_CONTEXT IrpContext,
  268. IN PSCB ParentScb,
  269. IN PFCB Fcb,
  270. IN UNICODE_STRING FileName,
  271. IN UCHAR FileNameFlags
  272. );
  273. VOID
  274. NtfsReplaceLinkInDir (
  275. IN PIRP_CONTEXT IrpContext,
  276. IN PSCB ParentScb,
  277. IN PFCB Fcb,
  278. IN PUNICODE_STRING NewLinkName,
  279. IN UCHAR FileNameFlags,
  280. IN PUNICODE_STRING PrevLinkName,
  281. IN UCHAR PrevLinkNameFlags
  282. );
  283. VOID
  284. NtfsMoveLinkToNewDir (
  285. IN PIRP_CONTEXT IrpContext,
  286. IN PUNICODE_STRING NewFullLinkName,
  287. IN PUNICODE_STRING NewLinkName,
  288. IN UCHAR NewLinkNameFlags,
  289. IN PSCB ParentScb,
  290. IN PFCB Fcb,
  291. IN OUT PLCB Lcb,
  292. IN ULONG RenameFlags,
  293. IN PUNICODE_STRING PrevLinkName,
  294. IN UCHAR PrevLinkNameFlags
  295. );
  296. VOID
  297. NtfsRenameLinkInDir (
  298. IN PIRP_CONTEXT IrpContext,
  299. IN PSCB ParentScb,
  300. IN PFCB Fcb,
  301. IN OUT PLCB Lcb,
  302. IN PUNICODE_STRING NewLinkName,
  303. IN UCHAR FileNameFlags,
  304. IN ULONG RenameFlags,
  305. IN PUNICODE_STRING PrevLinkName,
  306. IN UCHAR PrevLinkNameFlags
  307. );
  308. VOID
  309. NtfsUpdateFileDupInfo (
  310. IN PIRP_CONTEXT IrpContext,
  311. IN PFCB Fcb,
  312. IN PCCB Ccb OPTIONAL
  313. );
  314. NTSTATUS
  315. NtfsStreamRename(
  316. IN PIRP_CONTEXT IrpContext,
  317. IN PFILE_OBJECT FileObject,
  318. IN PFCB Fcb,
  319. IN PSCB Scb,
  320. IN PCCB Ccb,
  321. IN BOOLEAN ReplaceIfExists,
  322. IN PUNICODE_STRING NewStreamName
  323. );
  324. NTSTATUS
  325. NtfsPrepareToShrinkFileSize (
  326. IN PIRP_CONTEXT IrpContext,
  327. IN PFILE_OBJECT FileObject,
  328. IN PSCB Scb,
  329. LONGLONG NewFileSize
  330. );
  331. NTSTATUS
  332. NtfsCheckTreeForBatchOplocks (
  333. IN PIRP_CONTEXT IrpContext,
  334. IN PIRP Irp,
  335. IN PSCB DirectoryScb
  336. );
  337. #ifdef ALLOC_PRAGMA
  338. #pragma alloc_text(PAGE, NtfsCheckLinkForNewLink)
  339. #pragma alloc_text(PAGE, NtfsCheckLinkForRename)
  340. #pragma alloc_text(PAGE, NtfsCheckScbForLinkRemoval)
  341. #pragma alloc_text(PAGE, NtfsCheckTreeForBatchOplocks)
  342. #pragma alloc_text(PAGE, NtfsCleanupLinkForRemoval)
  343. #pragma alloc_text(PAGE, NtfsCommonQueryInformation)
  344. #pragma alloc_text(PAGE, NtfsCommonSetInformation)
  345. #pragma alloc_text(PAGE, NtfsFindTargetElements)
  346. #pragma alloc_text(PAGE, NtfsMoveLinkToNewDir)
  347. #pragma alloc_text(PAGE, NtfsPrepareToShrinkFileSize)
  348. #pragma alloc_text(PAGE, NtfsQueryAlternateNameInfo)
  349. #pragma alloc_text(PAGE, NtfsQueryBasicInfo)
  350. #pragma alloc_text(PAGE, NtfsQueryEaInfo)
  351. #pragma alloc_text(PAGE, NtfsQueryAttributeTagInfo)
  352. #pragma alloc_text(PAGE, NtfsQueryInternalInfo)
  353. #pragma alloc_text(PAGE, NtfsQueryNameInfo)
  354. #pragma alloc_text(PAGE, NtfsQueryPositionInfo)
  355. #pragma alloc_text(PAGE, NtfsQueryStandardInfo)
  356. #pragma alloc_text(PAGE, NtfsQueryStreamsInfo)
  357. #pragma alloc_text(PAGE, NtfsQueryCompressedFileSize)
  358. #pragma alloc_text(PAGE, NtfsQueryNetworkOpenInfo)
  359. #pragma alloc_text(PAGE, NtfsRenameLinkInDir)
  360. #pragma alloc_text(PAGE, NtfsReplaceLinkInDir)
  361. #pragma alloc_text(PAGE, NtfsSetAllocationInfo)
  362. #pragma alloc_text(PAGE, NtfsSetBasicInfo)
  363. #pragma alloc_text(PAGE, NtfsSetDispositionInfo)
  364. #pragma alloc_text(PAGE, NtfsSetEndOfFileInfo)
  365. #pragma alloc_text(PAGE, NtfsSetLinkInfo)
  366. #pragma alloc_text(PAGE, NtfsSetPositionInfo)
  367. #pragma alloc_text(PAGE, NtfsSetRenameInfo)
  368. #pragma alloc_text(PAGE, NtfsSetShortNameInfo)
  369. #pragma alloc_text(PAGE, NtfsSetValidDataLengthInfo)
  370. #pragma alloc_text(PAGE, NtfsStreamRename)
  371. #pragma alloc_text(PAGE, NtfsUpdateFcbFromLinkRemoval)
  372. #pragma alloc_text(PAGE, NtfsUpdateFileDupInfo)
  373. #endif
  374. NTSTATUS
  375. NtfsFsdSetInformation (
  376. IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
  377. IN PIRP Irp
  378. )
  379. /*++
  380. Routine Description:
  381. This routine implements the FSD part of set file information.
  382. Arguments:
  383. VolumeDeviceObject - Supplies the volume device object where the
  384. file exists
  385. Irp - Supplies the Irp being processed
  386. Return Value:
  387. NTSTATUS - The FSD status for the IRP
  388. --*/
  389. {
  390. TOP_LEVEL_CONTEXT TopLevelContext;
  391. PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
  392. NTSTATUS Status = STATUS_SUCCESS;
  393. PIRP_CONTEXT IrpContext = NULL;
  394. ULONG LogFileFullCount = 0;
  395. ASSERT_IRP( Irp );
  396. UNREFERENCED_PARAMETER( VolumeDeviceObject );
  397. DebugTrace( +1, Dbg, ("NtfsFsdSetInformation\n") );
  398. //
  399. // Call the common set Information routine
  400. //
  401. FsRtlEnterFileSystem();
  402. ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, FALSE, FALSE );
  403. do {
  404. try {
  405. //
  406. // We are either initiating this request or retrying it.
  407. //
  408. if (IrpContext == NULL) {
  409. //
  410. // Allocate and initialize the Irp.
  411. //
  412. NtfsInitializeIrpContext( Irp, CanFsdWait( Irp ), &IrpContext );
  413. //
  414. // Initialize the thread top level structure, if needed.
  415. //
  416. NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
  417. } else if (Status == STATUS_LOG_FILE_FULL) {
  418. NtfsCheckpointForLogFileFull( IrpContext );
  419. LogFileFullCount += 1;
  420. if (LogFileFullCount >= 2) {
  421. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_EXCESS_LOG_FULL );
  422. }
  423. }
  424. Status = NtfsCommonSetInformation( IrpContext, Irp );
  425. break;
  426. } except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
  427. NTSTATUS ExceptionCode;
  428. PIO_STACK_LOCATION IrpSp;
  429. //
  430. // We had some trouble trying to perform the requested
  431. // operation, so we'll abort the I/O request with
  432. // the error status that we get back from the
  433. // execption code
  434. //
  435. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  436. ExceptionCode = GetExceptionCode();
  437. if ((ExceptionCode == STATUS_FILE_DELETED) &&
  438. (IrpSp->Parameters.SetFile.FileInformationClass == FileEndOfFileInformation)) {
  439. IrpContext->ExceptionStatus = ExceptionCode = STATUS_SUCCESS;
  440. }
  441. Status = NtfsProcessException( IrpContext, Irp, ExceptionCode );
  442. }
  443. } while (Status == STATUS_CANT_WAIT ||
  444. Status == STATUS_LOG_FILE_FULL);
  445. ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
  446. FsRtlExitFileSystem();
  447. //
  448. // And return to our caller
  449. //
  450. DebugTrace( -1, Dbg, ("NtfsFsdSetInformation -> %08lx\n", Status) );
  451. return Status;
  452. }
  453. NTSTATUS
  454. NtfsCommonQueryInformation (
  455. IN PIRP_CONTEXT IrpContext,
  456. IN PIRP Irp
  457. )
  458. /*++
  459. Routine Description:
  460. This is the common routine for query file information called by both the
  461. fsd and fsp threads.
  462. Arguments:
  463. Irp - Supplies the Irp to process
  464. Return Value:
  465. NTSTATUS - The return status for the operation
  466. --*/
  467. {
  468. NTSTATUS Status = STATUS_SUCCESS;
  469. PIO_STACK_LOCATION IrpSp;
  470. PFILE_OBJECT FileObject;
  471. TYPE_OF_OPEN TypeOfOpen;
  472. PVCB Vcb;
  473. PFCB Fcb;
  474. PSCB Scb;
  475. PCCB Ccb;
  476. ULONG Length;
  477. FILE_INFORMATION_CLASS FileInformationClass;
  478. PVOID Buffer;
  479. BOOLEAN FcbAcquired = FALSE;
  480. BOOLEAN VcbAcquired = FALSE;
  481. BOOLEAN FsRtlHeaderLocked = FALSE;
  482. PFILE_ALL_INFORMATION AllInfo;
  483. ASSERT_IRP_CONTEXT( IrpContext );
  484. ASSERT_IRP( Irp );
  485. ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
  486. PAGED_CODE();
  487. //
  488. // Get the current Irp stack location
  489. //
  490. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  491. DebugTrace( +1, Dbg, ("NtfsCommonQueryInformation\n") );
  492. DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
  493. DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
  494. DebugTrace( 0, Dbg, ("Length = %08lx\n", IrpSp->Parameters.QueryFile.Length) );
  495. DebugTrace( 0, Dbg, ("FileInformationClass = %08lx\n", IrpSp->Parameters.QueryFile.FileInformationClass) );
  496. DebugTrace( 0, Dbg, ("Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer) );
  497. //
  498. // Reference our input parameters to make things easier
  499. //
  500. Length = IrpSp->Parameters.QueryFile.Length;
  501. FileInformationClass = IrpSp->Parameters.QueryFile.FileInformationClass;
  502. Buffer = Irp->AssociatedIrp.SystemBuffer;
  503. //
  504. // Extract and decode the file object
  505. //
  506. FileObject = IrpSp->FileObject;
  507. TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
  508. try {
  509. //
  510. // Case on the type of open we're dealing with
  511. //
  512. switch (TypeOfOpen) {
  513. case UserVolumeOpen:
  514. //
  515. // We cannot query the user volume open.
  516. //
  517. Status = STATUS_INVALID_PARAMETER;
  518. break;
  519. case UserFileOpen:
  520. case UserDirectoryOpen:
  521. case UserViewIndexOpen:
  522. if (FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_FILE_ID )) {
  523. //
  524. // We don't allow this operation on with open by file id.
  525. //
  526. if ((Ccb->Lcb == NULL) &&
  527. (FileInformationClass == FileAlternateNameInformation)) {
  528. Status = STATUS_INVALID_PARAMETER;
  529. break;
  530. } else if ((FileInformationClass == FileAllInformation) ||
  531. (FileInformationClass == FileNameInformation)) {
  532. if (FlagOn( Ccb->Flags, CCB_FLAG_TRAVERSE_CHECK )) {
  533. //
  534. // If this file was opened by Id by a user without traversal privilege,
  535. // we can't return any info level that includes a file or path name
  536. // unless this open is relative to a directory by file id.
  537. // Look at the file name in the Ccb in that case. We'll return
  538. // only that portion of the name.
  539. //
  540. if (Ccb->FullFileName.MaximumLength == 0) {
  541. Status = STATUS_INVALID_PARAMETER;
  542. break;
  543. }
  544. }
  545. //
  546. // We'll need to hold the Vcb exclusively through the filename
  547. // synthesis, since it walks up the directory tree.
  548. //
  549. NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
  550. VcbAcquired = TRUE;
  551. }
  552. }
  553. //
  554. // Deliberate fall through to StreamFileOpen case.
  555. //
  556. case StreamFileOpen:
  557. //
  558. // Acquire the Vcb if there is no Ccb. This is for the
  559. // case where the cache manager is querying the name.
  560. //
  561. if (Ccb == NULL) {
  562. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  563. VcbAcquired = TRUE;
  564. }
  565. if ((Scb->Header.PagingIoResource != NULL) &&
  566. ((FileInformationClass == FileAllInformation) ||
  567. (FileInformationClass == FileStandardInformation) ||
  568. (FileInformationClass == FileCompressionInformation))) {
  569. ExAcquireResourceSharedLite( Scb->Header.PagingIoResource, TRUE );
  570. FsRtlLockFsRtlHeader( &Scb->Header );
  571. FsRtlHeaderLocked = TRUE;
  572. }
  573. NtfsAcquireSharedFcb( IrpContext, Fcb, Scb, 0 );
  574. FcbAcquired = TRUE;
  575. //
  576. // Fail this request if the volume has been dismounted.
  577. // System files may not have the scbstate flag set - so we test the vcb as well
  578. // Holding any file's main resource lets us test the vcb since we call acquireallfiles
  579. // before doing a dismount
  580. //
  581. if (FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED ) ||
  582. !FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
  583. NtfsRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED, NULL, NULL );
  584. }
  585. //
  586. // Based on the information class we'll do different
  587. // actions. Each of hte procedures that we're calling fills
  588. // up the output buffer, if possible. They will raise the
  589. // status STATUS_BUFFER_OVERFLOW for an insufficient buffer.
  590. // This is considered a somewhat unusual case and is handled
  591. // more cleanly with the exception mechanism rather than
  592. // testing a return status value for each call.
  593. //
  594. switch (FileInformationClass) {
  595. case FileAllInformation:
  596. //
  597. // For the all information class we'll typecast a local
  598. // pointer to the output buffer and then call the
  599. // individual routines to fill in the buffer.
  600. //
  601. AllInfo = Buffer;
  602. Length -= (sizeof(FILE_ACCESS_INFORMATION)
  603. + sizeof(FILE_MODE_INFORMATION)
  604. + sizeof(FILE_ALIGNMENT_INFORMATION));
  605. NtfsQueryBasicInfo( IrpContext, FileObject, Scb, &AllInfo->BasicInformation, &Length );
  606. NtfsQueryStandardInfo( IrpContext, FileObject, Scb, &AllInfo->StandardInformation, &Length, Ccb );
  607. NtfsQueryInternalInfo( IrpContext, FileObject, Scb, &AllInfo->InternalInformation, &Length );
  608. NtfsQueryEaInfo( IrpContext, FileObject, Scb, &AllInfo->EaInformation, &Length );
  609. NtfsQueryPositionInfo( IrpContext, FileObject, Scb, &AllInfo->PositionInformation, &Length );
  610. Status =
  611. NtfsQueryNameInfo( IrpContext, FileObject, Scb, &AllInfo->NameInformation, &Length, Ccb );
  612. break;
  613. case FileBasicInformation:
  614. NtfsQueryBasicInfo( IrpContext, FileObject, Scb, Buffer, &Length );
  615. break;
  616. case FileStandardInformation:
  617. NtfsQueryStandardInfo( IrpContext, FileObject, Scb, Buffer, &Length, Ccb );
  618. break;
  619. case FileInternalInformation:
  620. NtfsQueryInternalInfo( IrpContext, FileObject, Scb, Buffer, &Length );
  621. break;
  622. case FileEaInformation:
  623. NtfsQueryEaInfo( IrpContext, FileObject, Scb, Buffer, &Length );
  624. break;
  625. case FileAttributeTagInformation:
  626. NtfsQueryAttributeTagInfo( IrpContext, FileObject, Scb, Ccb, Buffer, &Length );
  627. break;
  628. case FilePositionInformation:
  629. NtfsQueryPositionInfo( IrpContext, FileObject, Scb, Buffer, &Length );
  630. break;
  631. case FileNameInformation:
  632. Status = NtfsQueryNameInfo( IrpContext, FileObject, Scb, Buffer, &Length, Ccb );
  633. break;
  634. case FileAlternateNameInformation:
  635. Status = NtfsQueryAlternateNameInfo( IrpContext, Scb, Ccb->Lcb, Buffer, &Length );
  636. break;
  637. case FileStreamInformation:
  638. Status = NtfsQueryStreamsInfo( IrpContext, Fcb, Buffer, &Length );
  639. break;
  640. case FileCompressionInformation:
  641. Status = NtfsQueryCompressedFileSize( IrpContext, Scb, Buffer, &Length );
  642. break;
  643. case FileNetworkOpenInformation:
  644. NtfsQueryNetworkOpenInfo( IrpContext, FileObject, Scb, Buffer, &Length );
  645. break;
  646. default:
  647. Status = STATUS_INVALID_PARAMETER;
  648. break;
  649. }
  650. break;
  651. default:
  652. Status = STATUS_INVALID_PARAMETER;
  653. }
  654. //
  655. // Set the information field to the number of bytes actually filled in
  656. // and then complete the request
  657. //
  658. Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - Length;
  659. //
  660. // Abort transaction on error by raising.
  661. //
  662. NtfsCleanupTransaction( IrpContext, Status, FALSE );
  663. } finally {
  664. DebugUnwind( NtfsCommonQueryInformation );
  665. if (FsRtlHeaderLocked) {
  666. FsRtlUnlockFsRtlHeader( &Scb->Header );
  667. ExReleaseResourceLite( Scb->Header.PagingIoResource );
  668. }
  669. if (FcbAcquired) { NtfsReleaseFcb( IrpContext, Fcb ); }
  670. if (VcbAcquired) { NtfsReleaseVcb( IrpContext, Vcb ); }
  671. if (!AbnormalTermination()) {
  672. NtfsCompleteRequest( IrpContext, Irp, Status );
  673. }
  674. DebugTrace( -1, Dbg, ("NtfsCommonQueryInformation -> %08lx\n", Status) );
  675. }
  676. return Status;
  677. }
  678. NTSTATUS
  679. NtfsCommonSetInformation (
  680. IN PIRP_CONTEXT IrpContext,
  681. IN PIRP Irp
  682. )
  683. /*++
  684. Routine Description:
  685. This is the common routine for set file information called by both the
  686. fsd and fsp threads.
  687. Arguments:
  688. Irp - Supplies the Irp to process
  689. Return Value:
  690. NTSTATUS - The return status for the operation
  691. --*/
  692. {
  693. NTSTATUS Status = STATUS_SUCCESS;
  694. PIO_STACK_LOCATION IrpSp;
  695. PFILE_OBJECT FileObject;
  696. TYPE_OF_OPEN TypeOfOpen;
  697. PVCB Vcb;
  698. PFCB Fcb;
  699. PSCB Scb;
  700. PCCB Ccb;
  701. FILE_INFORMATION_CLASS FileInformationClass;
  702. BOOLEAN VcbAcquired = FALSE;
  703. BOOLEAN ReleaseScbPaging = FALSE;
  704. BOOLEAN LazyWriterCallback = FALSE;
  705. ULONG WaitState;
  706. ASSERT_IRP_CONTEXT( IrpContext );
  707. ASSERT_IRP( Irp );
  708. ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
  709. PAGED_CODE();
  710. //
  711. // Get the current Irp stack location
  712. //
  713. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  714. DebugTrace( +1, Dbg, ("NtfsCommonSetInformation\n") );
  715. DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
  716. DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
  717. DebugTrace( 0, Dbg, ("Length = %08lx\n", IrpSp->Parameters.SetFile.Length) );
  718. DebugTrace( 0, Dbg, ("FileInformationClass = %08lx\n", IrpSp->Parameters.SetFile.FileInformationClass) );
  719. DebugTrace( 0, Dbg, ("FileObject = %08lx\n", IrpSp->Parameters.SetFile.FileObject) );
  720. DebugTrace( 0, Dbg, ("ReplaceIfExists = %08lx\n", IrpSp->Parameters.SetFile.ReplaceIfExists) );
  721. DebugTrace( 0, Dbg, ("Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer) );
  722. //
  723. // Reference our input parameters to make things easier
  724. //
  725. FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
  726. //
  727. // Extract and decode the file object
  728. //
  729. FileObject = IrpSp->FileObject;
  730. TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
  731. //
  732. // We can reject volume opens immediately.
  733. //
  734. if (TypeOfOpen == UserVolumeOpen ||
  735. TypeOfOpen == UnopenedFileObject ||
  736. TypeOfOpen == UserViewIndexOpen ||
  737. ((TypeOfOpen != UserFileOpen) &&
  738. (FileInformationClass == FileValidDataLengthInformation))) {
  739. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  740. DebugTrace( -1, Dbg, ("NtfsCommonSetInformation -> STATUS_INVALID_PARAMETER\n") );
  741. return STATUS_INVALID_PARAMETER;
  742. }
  743. if (NtfsIsVolumeReadOnly( Vcb )) {
  744. NtfsCompleteRequest( IrpContext, Irp, STATUS_MEDIA_WRITE_PROTECTED );
  745. DebugTrace( -1, Dbg, ("NtfsCommonSetInformation -> STATUS_MEDIA_WRITE_PROTECTED\n") );
  746. return STATUS_MEDIA_WRITE_PROTECTED;
  747. }
  748. try {
  749. //
  750. // The typical path here is for the lazy writer callback. Go ahead and
  751. // remember this first.
  752. //
  753. if (FileInformationClass == FileEndOfFileInformation) {
  754. LazyWriterCallback = IrpSp->Parameters.SetFile.AdvanceOnly;
  755. }
  756. //
  757. // Perform the oplock check for changes to allocation or EOF if called
  758. // by the user.
  759. //
  760. if (!LazyWriterCallback &&
  761. ((FileInformationClass == FileEndOfFileInformation) ||
  762. (FileInformationClass == FileAllocationInformation) ||
  763. (FileInformationClass == FileValidDataLengthInformation)) &&
  764. (TypeOfOpen == UserFileOpen) &&
  765. !FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
  766. //
  767. // We check whether we can proceed based on the state of the file oplocks.
  768. // This call might block this request.
  769. //
  770. Status = FsRtlCheckOplock( &Scb->ScbType.Data.Oplock,
  771. Irp,
  772. IrpContext,
  773. NULL,
  774. NULL );
  775. if (Status != STATUS_SUCCESS) {
  776. try_return( NOTHING );
  777. }
  778. //
  779. // Update the FastIoField.
  780. //
  781. NtfsAcquireFsrtlHeader( Scb );
  782. Scb->Header.IsFastIoPossible = NtfsIsFastIoPossible( Scb );
  783. NtfsReleaseFsrtlHeader( Scb );
  784. }
  785. //
  786. // If this call is for EOF then we need to acquire the Vcb if we may
  787. // have to perform an update duplicate call. Don't block waiting for
  788. // the Vcb in the Valid data callback case.
  789. // We don't want to block the lazy write threads in the clean checkpoint
  790. // case.
  791. //
  792. switch (FileInformationClass) {
  793. case FileEndOfFileInformation:
  794. //
  795. // If this is not a system file then we will need to update duplicate info.
  796. //
  797. if (!FlagOn( Fcb->FcbState, FCB_STATE_SYSTEM_FILE )) {
  798. WaitState = FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  799. ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  800. //
  801. // Only acquire the Vcb for the Lazy writer if we know the file size in the Fcb
  802. // is out of date or can compare the Scb with that in the Fcb. An unsafe comparison
  803. // is OK because if they are changing then someone else can do the work.
  804. // We also want to update the duplicate information if the total allocated
  805. // has changed and there are no user handles remaining to perform the update.
  806. //
  807. if (LazyWriterCallback) {
  808. if ((FlagOn( Fcb->InfoFlags, FCB_INFO_CHANGED_FILE_SIZE ) ||
  809. ((Scb->Header.FileSize.QuadPart != Fcb->Info.FileSize) &&
  810. FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA ))) ||
  811. (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK ) &&
  812. (Scb->CleanupCount == 0) &&
  813. (Scb->ValidDataToDisk >= Scb->Header.ValidDataLength.QuadPart) &&
  814. (FlagOn( Fcb->InfoFlags, FCB_INFO_CHANGED_ALLOC_SIZE ) ||
  815. (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA ) &&
  816. (Scb->TotalAllocated != Fcb->Info.AllocatedLength))))) {
  817. //
  818. // Go ahead and try to acquire the Vcb without waiting.
  819. //
  820. if (NtfsAcquireSharedVcb( IrpContext, Vcb, FALSE )) {
  821. VcbAcquired = TRUE;
  822. } else {
  823. SetFlag( IrpContext->State, WaitState );
  824. //
  825. // If we could not get the Vcb for any reason then return. Let's
  826. // not block an essential thread waiting for the Vcb. Typically
  827. // we will only be blocked during a clean checkpoint. The Lazy
  828. // Writer will periodically come back and retry this call.
  829. //
  830. try_return( Status = STATUS_FILE_LOCK_CONFLICT );
  831. }
  832. }
  833. //
  834. // Otherwise we always want to wait for the Vcb except if we were called from
  835. // MM extending a section. We will try to get this without waiting and test
  836. // if called from MM if unsuccessful.
  837. //
  838. } else {
  839. if (NtfsAcquireSharedVcb( IrpContext, Vcb, FALSE )) {
  840. VcbAcquired = TRUE;
  841. } else if ((Scb->Header.PagingIoResource == NULL) ||
  842. !NtfsIsExclusiveScbPagingIo( Scb )) {
  843. SetFlag( IrpContext->State, WaitState );
  844. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  845. VcbAcquired = TRUE;
  846. }
  847. }
  848. SetFlag( IrpContext->State, WaitState );
  849. }
  850. break;
  851. //
  852. // Acquire the Vcb shared for changes to allocation or basic
  853. // information.
  854. //
  855. case FileAllocationInformation:
  856. case FileBasicInformation:
  857. case FileDispositionInformation:
  858. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  859. VcbAcquired = TRUE;
  860. break;
  861. //
  862. // If this is a rename or link operation then we need to make sure
  863. // we have the user's context and acquire the Vcb.
  864. //
  865. case FileRenameInformation:
  866. case FileLinkInformation:
  867. if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ALLOC_SECURITY )) {
  868. IrpContext->Union.SubjectContext = NtfsAllocatePool( PagedPool,
  869. sizeof( SECURITY_SUBJECT_CONTEXT ));
  870. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_ALLOC_SECURITY );
  871. SeCaptureSubjectContext( IrpContext->Union.SubjectContext );
  872. }
  873. // Fall thru
  874. //
  875. // For the two above plus the shortname we might need the Vcb exclusive for either directories
  876. // or possible deadlocks.
  877. //
  878. case FileShortNameInformation:
  879. if (IsDirectory( &Fcb->Info )) {
  880. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX );
  881. }
  882. if (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX )) {
  883. NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
  884. } else {
  885. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  886. }
  887. VcbAcquired = TRUE;
  888. break;
  889. default:
  890. NOTHING;
  891. }
  892. //
  893. // The Lazy Writer must still synchronize with Eof to keep the
  894. // stream sizes from changing. This will be cleaned up when we
  895. // complete.
  896. //
  897. if (LazyWriterCallback) {
  898. //
  899. // Acquire either the paging io resource shared to serialize with
  900. // the flush case where the main resource is acquired before IoAtEOF
  901. //
  902. if (Scb->Header.PagingIoResource != NULL) {
  903. ExAcquireResourceSharedLite( Scb->Header.PagingIoResource, TRUE );
  904. ReleaseScbPaging = TRUE;
  905. }
  906. FsRtlLockFsRtlHeader( &Scb->Header );
  907. IrpContext->CleanupStructure = Scb;
  908. //
  909. // Anyone potentially shrinking/deleting allocation must get the paging I/O
  910. // resource first. Special cases are the rename path and SetBasicInfo. The
  911. // rename path to lock the mapped page writer out of this file for deadlock
  912. // prevention. SetBasicInfo since we may call WriteFileSizes and we
  913. // don't want to bump up the file size on disk from the value in the Scb
  914. // if a write to EOF is underway.
  915. //
  916. } else if ((Scb->Header.PagingIoResource != NULL) &&
  917. ((FileInformationClass == FileEndOfFileInformation) ||
  918. (FileInformationClass == FileAllocationInformation) ||
  919. (FileInformationClass == FileRenameInformation) ||
  920. (FileInformationClass == FileBasicInformation) ||
  921. (FileInformationClass == FileLinkInformation) ||
  922. (FileInformationClass == FileValidDataLengthInformation))) {
  923. NtfsAcquireExclusivePagingIo( IrpContext, Fcb );
  924. }
  925. //
  926. // Acquire exclusive access to the Fcb, We use exclusive
  927. // because it is probable that one of the subroutines
  928. // that we call will need to monkey with file allocation,
  929. // create/delete extra fcbs. So we're willing to pay the
  930. // cost of exclusive Fcb access.
  931. //
  932. NtfsAcquireExclusiveFcb( IrpContext, Fcb, Scb, 0 );
  933. //
  934. // Make sure the Scb state test we're about to do is properly synchronized.
  935. // There's no point in testing the SCB_STATE_VOLUME_DISMOUNTED flag below
  936. // if the volume can still get dismounted below us during this operation.
  937. //
  938. ASSERT( NtfsIsExclusiveScb( Scb ) || NtfsIsSharedScb( Scb ) );
  939. //
  940. // The lazy writer callback is the only caller who can get this far if the
  941. // volume has been dismounted. We know that there are no user handles or
  942. // writeable file objects or dirty pages. Make one last check to see
  943. // if this stream is on a dismounted or locked volume. Note the
  944. // vcb tests are unsafe
  945. //
  946. if (FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED ) ||
  947. FlagOn( Vcb->VcbState, VCB_STATE_LOCK_IN_PROGRESS ) ||
  948. !(FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED ))) {
  949. NtfsRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED, NULL, NULL );
  950. }
  951. //
  952. // Based on the information class we'll do different
  953. // actions. We will perform checks, when appropriate
  954. // to insure that the requested operation is allowed.
  955. //
  956. switch (FileInformationClass) {
  957. case FileBasicInformation:
  958. Status = NtfsSetBasicInfo( IrpContext, FileObject, Irp, Scb, Ccb );
  959. break;
  960. case FileDispositionInformation:
  961. Status = NtfsSetDispositionInfo( IrpContext, FileObject, Irp, Scb, Ccb );
  962. break;
  963. case FileRenameInformation:
  964. Status = NtfsSetRenameInfo( IrpContext, FileObject, Irp, Vcb, Scb, Ccb, &VcbAcquired );
  965. break;
  966. case FilePositionInformation:
  967. Status = NtfsSetPositionInfo( IrpContext, FileObject, Irp, Scb );
  968. break;
  969. case FileLinkInformation:
  970. Status = NtfsSetLinkInfo( IrpContext, Irp, Vcb, Scb, Ccb, &VcbAcquired );
  971. break;
  972. case FileAllocationInformation:
  973. if (TypeOfOpen == UserDirectoryOpen ||
  974. TypeOfOpen == UserViewIndexOpen) {
  975. Status = STATUS_INVALID_PARAMETER;
  976. } else {
  977. Status = NtfsSetAllocationInfo( IrpContext, FileObject, Irp, Scb, Ccb );
  978. }
  979. break;
  980. case FileEndOfFileInformation:
  981. if (TypeOfOpen == UserDirectoryOpen ||
  982. TypeOfOpen == UserViewIndexOpen) {
  983. Status = STATUS_INVALID_PARAMETER;
  984. } else {
  985. Status = NtfsSetEndOfFileInfo( IrpContext, FileObject, Irp, Scb, Ccb, VcbAcquired );
  986. }
  987. break;
  988. case FileValidDataLengthInformation:
  989. Status = NtfsSetValidDataLengthInfo( IrpContext, Irp, Scb, Ccb );
  990. break;
  991. case FileShortNameInformation:
  992. //
  993. // Disallow setshortname on the root - its meaningless anyway
  994. //
  995. if (Scb->Header.NodeTypeCode == NTFS_NTC_SCB_ROOT_INDEX) {
  996. Status = STATUS_INVALID_PARAMETER;
  997. } else {
  998. Status = NtfsSetShortNameInfo( IrpContext, FileObject, Irp, Vcb, Scb, Ccb );
  999. }
  1000. break;
  1001. default:
  1002. Status = STATUS_INVALID_PARAMETER;
  1003. break;
  1004. }
  1005. //
  1006. // Abort transaction on error by raising.
  1007. //
  1008. if (Status != STATUS_PENDING) {
  1009. NtfsCleanupTransaction( IrpContext, Status, FALSE );
  1010. }
  1011. try_exit: NOTHING;
  1012. } finally {
  1013. DebugUnwind( NtfsCommonSetInformation );
  1014. //
  1015. // Release the paging io resource if acquired shared.
  1016. //
  1017. if (ReleaseScbPaging) {
  1018. ExReleaseResourceLite( Scb->Header.PagingIoResource );
  1019. }
  1020. if (VcbAcquired) {
  1021. NtfsReleaseVcb( IrpContext, Vcb );
  1022. }
  1023. DebugTrace( -1, Dbg, ("NtfsCommonSetInformation -> %08lx\n", Status) );
  1024. }
  1025. //
  1026. // Complete the request unless it is being done in the oplock
  1027. // package.
  1028. //
  1029. if (Status != STATUS_PENDING) {
  1030. NtfsCompleteRequest( IrpContext, Irp, Status );
  1031. }
  1032. return Status;
  1033. }
  1034. //
  1035. // Internal Support Routine
  1036. //
  1037. VOID
  1038. NtfsQueryBasicInfo (
  1039. IN PIRP_CONTEXT IrpContext,
  1040. IN PFILE_OBJECT FileObject,
  1041. IN PSCB Scb,
  1042. IN OUT PFILE_BASIC_INFORMATION Buffer,
  1043. IN OUT PULONG Length
  1044. )
  1045. /*++
  1046. Routine Description:
  1047. This routine performs the query basic information function.
  1048. Arguments:
  1049. FileObject - Supplies the file object being processed
  1050. Scb - Supplies the Scb being queried
  1051. Buffer - Supplies a pointer to the buffer where the information is to
  1052. be returned
  1053. Length - Supplies the length of the buffer in bytes, and receives the
  1054. remaining bytes free in the buffer upon return.
  1055. Return Value:
  1056. None
  1057. --*/
  1058. {
  1059. ASSERT_IRP_CONTEXT( IrpContext );
  1060. ASSERT_FILE_OBJECT( FileObject );
  1061. ASSERT_SCB( Scb );
  1062. PAGED_CODE();
  1063. DebugTrace( +1, Dbg, ("NtfsQueryBasicInfo...\n") );
  1064. //
  1065. // Update the length used.
  1066. //
  1067. *Length -= sizeof( FILE_BASIC_INFORMATION );
  1068. //
  1069. // Copy over the time information
  1070. //
  1071. NtfsFillBasicInfo( Buffer, Scb );
  1072. //
  1073. // And return to our caller
  1074. //
  1075. DebugTrace( 0, Dbg, ("*Length = %08lx\n", *Length) );
  1076. DebugTrace( -1, Dbg, ("NtfsQueryBasicInfo -> VOID\n") );
  1077. return;
  1078. }
  1079. //
  1080. // Internal Support Routine
  1081. //
  1082. VOID
  1083. NtfsQueryStandardInfo (
  1084. IN PIRP_CONTEXT IrpContext,
  1085. IN PFILE_OBJECT FileObject,
  1086. IN PSCB Scb,
  1087. IN OUT PFILE_STANDARD_INFORMATION Buffer,
  1088. IN OUT PULONG Length,
  1089. IN PCCB Ccb OPTIONAL
  1090. )
  1091. /*++
  1092. Routine Description:
  1093. This routine performs the query standard information function.
  1094. Arguments:
  1095. FileObject - Supplies the file object being processed
  1096. Scb - Supplies the Scb being queried
  1097. Ccb - Optionally supplies the ccb for the opened file object.
  1098. Buffer - Supplies a pointer to the buffer where the information is to
  1099. be returned
  1100. Length - Supplies the length of the buffer in bytes, and receives the
  1101. remaining bytes free in the buffer upon return.
  1102. Return Value:
  1103. None
  1104. --*/
  1105. {
  1106. ASSERT_IRP_CONTEXT( IrpContext );
  1107. ASSERT_FILE_OBJECT( FileObject );
  1108. ASSERT_SCB( Scb );
  1109. PAGED_CODE();
  1110. DebugTrace( +1, Dbg, ("NtfsQueryStandardInfo...\n") );
  1111. //
  1112. // Update the length field.
  1113. //
  1114. *Length -= sizeof( FILE_STANDARD_INFORMATION );
  1115. //
  1116. // If the Scb is uninitialized, we initialize it now.
  1117. //
  1118. if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED ) &&
  1119. (Scb->AttributeTypeCode != $INDEX_ALLOCATION)) {
  1120. DebugTrace( 0, Dbg, ("Initializing Scb -> %08lx\n", Scb) );
  1121. NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
  1122. }
  1123. //
  1124. // Call the common routine to fill the output buffer.
  1125. //
  1126. NtfsFillStandardInfo( Buffer, Scb, Ccb );
  1127. //
  1128. // And return to our caller
  1129. //
  1130. DebugTrace( 0, Dbg, ("*Length = %08lx\n", *Length) );
  1131. DebugTrace( -1, Dbg, ("NtfsQueryStandardInfo -> VOID\n") );
  1132. return;
  1133. }
  1134. //
  1135. // Internal Support Routine
  1136. //
  1137. VOID
  1138. NtfsQueryInternalInfo (
  1139. IN PIRP_CONTEXT IrpContext,
  1140. IN PFILE_OBJECT FileObject,
  1141. IN PSCB Scb,
  1142. IN OUT PFILE_INTERNAL_INFORMATION Buffer,
  1143. IN OUT PULONG Length
  1144. )
  1145. /*++
  1146. Routine Description:
  1147. This routine performs the query internal information function.
  1148. Arguments:
  1149. FileObject - Supplies the file object being processed
  1150. Scb - Supplies the Scb being queried
  1151. Buffer - Supplies a pointer to the buffer where the information is to
  1152. be returned
  1153. Length - Supplies the length of the buffer in bytes, and receives the
  1154. remaining bytes free in the buffer upon return.
  1155. Return Value:
  1156. None
  1157. --*/
  1158. {
  1159. ASSERT_IRP_CONTEXT( IrpContext );
  1160. ASSERT_FILE_OBJECT( FileObject );
  1161. ASSERT_SCB( Scb );
  1162. PAGED_CODE();
  1163. DebugTrace( +1, Dbg, ("NtfsQueryInternalInfo...\n") );
  1164. RtlZeroMemory( Buffer, sizeof(FILE_INTERNAL_INFORMATION) );
  1165. *Length -= sizeof( FILE_INTERNAL_INFORMATION );
  1166. //
  1167. // Copy over the entire file reference including the sequence number
  1168. //
  1169. Buffer->IndexNumber = *(PLARGE_INTEGER)&Scb->Fcb->FileReference;
  1170. //
  1171. // And return to our caller
  1172. //
  1173. DebugTrace( 0, Dbg, ("*Length = %08lx\n", *Length) );
  1174. DebugTrace( -1, Dbg, ("NtfsQueryInternalInfo -> VOID\n") );
  1175. return;
  1176. }
  1177. //
  1178. // Internal Support Routine
  1179. //
  1180. VOID
  1181. NtfsQueryEaInfo (
  1182. IN PIRP_CONTEXT IrpContext,
  1183. IN PFILE_OBJECT FileObject,
  1184. IN PSCB Scb,
  1185. IN OUT PFILE_EA_INFORMATION Buffer,
  1186. IN OUT PULONG Length
  1187. )
  1188. /*++
  1189. Routine Description:
  1190. This routine performs the query EA information function.
  1191. Arguments:
  1192. FileObject - Supplies the file object being processed
  1193. Scb - Supplies the Scb being queried
  1194. Buffer - Supplies a pointer to the buffer where the information is to
  1195. be returned
  1196. Length - Supplies the length of the buffer in bytes, and receives the
  1197. remaining bytes free in the buffer upon return.
  1198. Return Value:
  1199. None
  1200. --*/
  1201. {
  1202. ASSERT_IRP_CONTEXT( IrpContext );
  1203. ASSERT_FILE_OBJECT( FileObject );
  1204. ASSERT_SCB( Scb );
  1205. PAGED_CODE();
  1206. DebugTrace( +1, Dbg, ("NtfsQueryEaInfo...\n") );
  1207. RtlZeroMemory( Buffer, sizeof(FILE_EA_INFORMATION) );
  1208. *Length -= sizeof( FILE_EA_INFORMATION );
  1209. //
  1210. // EAs and reparse points cannot both be in a file at the same
  1211. // time. We return different information for each case.
  1212. //
  1213. if (FlagOn( Scb->Fcb->Info.FileAttributes, FILE_ATTRIBUTE_REPARSE_POINT)) {
  1214. Buffer->EaSize = 0;
  1215. } else {
  1216. Buffer->EaSize = Scb->Fcb->Info.PackedEaSize;
  1217. //
  1218. // Add 4 bytes for the CbListHeader.
  1219. //
  1220. if (Buffer->EaSize != 0) {
  1221. Buffer->EaSize += 4;
  1222. }
  1223. }
  1224. //
  1225. // And return to our caller
  1226. //
  1227. DebugTrace( 0, Dbg, ("*Length = %08lx\n", *Length) );
  1228. DebugTrace( -1, Dbg, ("NtfsQueryEaInfo -> VOID\n") );
  1229. return;
  1230. }
  1231. //
  1232. // Internal Support Routine
  1233. //
  1234. VOID
  1235. NtfsQueryAttributeTagInfo (
  1236. IN PIRP_CONTEXT IrpContext,
  1237. IN PFILE_OBJECT FileObject,
  1238. IN PSCB Scb,
  1239. IN PCCB Ccb,
  1240. IN OUT PFILE_ATTRIBUTE_TAG_INFORMATION Buffer,
  1241. IN OUT PULONG Length
  1242. )
  1243. /*++
  1244. Routine Description:
  1245. This routine performs the query of attributes and tag information function.
  1246. Arguments:
  1247. FileObject - Supplies the file object being processed
  1248. Scb - Supplies the Scb being queried
  1249. Buffer - Supplies a pointer to the buffer where the information is to
  1250. be returned
  1251. Length - Supplies the length of the buffer in bytes, and receives the
  1252. remaining bytes free in the buffer upon return.
  1253. Return Value:
  1254. None
  1255. --*/
  1256. {
  1257. PFCB Fcb;
  1258. ASSERT_IRP_CONTEXT( IrpContext );
  1259. ASSERT_FILE_OBJECT( FileObject );
  1260. ASSERT_SCB( Scb );
  1261. PAGED_CODE();
  1262. DebugTrace( +1, Dbg, ("NtfsQueryAttributeTagInfo...\n") );
  1263. Fcb = Scb->Fcb;
  1264. //
  1265. // Zero the output buffer and update the length.
  1266. //
  1267. RtlZeroMemory( Buffer, sizeof(FILE_ATTRIBUTE_TAG_INFORMATION) );
  1268. *Length -= sizeof( FILE_ATTRIBUTE_TAG_INFORMATION );
  1269. //
  1270. // Load the file attributes as in NtfsQueryBasicInfo.
  1271. //
  1272. // For the file attribute information if the flags in the attribute are zero then we
  1273. // return the file normal attribute otherwise we return the mask of the set attribute
  1274. // bits. Note that only the valid attribute bits are returned to the user.
  1275. //
  1276. Buffer->FileAttributes = Fcb->Info.FileAttributes;
  1277. ClearFlag( Buffer->FileAttributes,
  1278. (~FILE_ATTRIBUTE_VALID_FLAGS |
  1279. FILE_ATTRIBUTE_TEMPORARY |
  1280. FILE_ATTRIBUTE_SPARSE_FILE |
  1281. FILE_ATTRIBUTE_ENCRYPTED) );
  1282. //
  1283. // Pick up the sparse bit for this stream from the Scb.
  1284. //
  1285. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE )) {
  1286. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_SPARSE_FILE );
  1287. }
  1288. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_ENCRYPTED )) {
  1289. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_ENCRYPTED );
  1290. }
  1291. //
  1292. // If this is the main steam then the compression flag is correct but
  1293. // we need to test if this is a directory.
  1294. //
  1295. if (FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE )) {
  1296. if (IsDirectory( &Fcb->Info ) || IsViewIndex( &Fcb->Info )) {
  1297. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_DIRECTORY );
  1298. }
  1299. //
  1300. // If this is not the main stream on the file then use the stream based
  1301. // compressed bit.
  1302. //
  1303. } else {
  1304. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  1305. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
  1306. } else {
  1307. ClearFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
  1308. }
  1309. }
  1310. //
  1311. // If the temporary flag is set, then return it to the caller.
  1312. //
  1313. if (FlagOn( Scb->ScbState, SCB_STATE_TEMPORARY )) {
  1314. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
  1315. }
  1316. //
  1317. // If there are no flags set then explicitly set the NORMAL flag.
  1318. //
  1319. if (Buffer->FileAttributes == 0) {
  1320. Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
  1321. }
  1322. //
  1323. // Load the reparse point tag.
  1324. // As EAs and reparse points cannot both be in a file at the same time, we return
  1325. // the appropriate information for each case.
  1326. //
  1327. if (FlagOn( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_REPARSE_POINT)) {
  1328. Buffer->ReparseTag = Fcb->Info.ReparsePointTag;
  1329. } else {
  1330. Buffer->ReparseTag = IO_REPARSE_TAG_RESERVED_ZERO;
  1331. }
  1332. //
  1333. // And return to our caller
  1334. //
  1335. DebugTrace( 0, Dbg, ("*Length = %08lx\n", *Length) );
  1336. DebugTrace( -1, Dbg, ("NtfsQueryAttributeTagInfo -> VOID\n") );
  1337. return;
  1338. }
  1339. //
  1340. // Internal Support Routine
  1341. //
  1342. VOID
  1343. NtfsQueryPositionInfo (
  1344. IN PIRP_CONTEXT IrpContext,
  1345. IN PFILE_OBJECT FileObject,
  1346. IN PSCB Scb,
  1347. IN OUT PFILE_POSITION_INFORMATION Buffer,
  1348. IN OUT PULONG Length
  1349. )
  1350. /*++
  1351. Routine Description:
  1352. This routine performs the query position information function.
  1353. Arguments:
  1354. FileObject - Supplies the file object being processed
  1355. Scb - Supplies the Scb being queried
  1356. Buffer - Supplies a pointer to the buffer where the information is to
  1357. be returned
  1358. Length - Supplies the length of the buffer in bytes, and receives the
  1359. remaining bytes free in the buffer upon return.
  1360. Return Value:
  1361. None
  1362. --*/
  1363. {
  1364. ASSERT_IRP_CONTEXT( IrpContext );
  1365. ASSERT_FILE_OBJECT( FileObject );
  1366. ASSERT_SCB( Scb );
  1367. PAGED_CODE();
  1368. DebugTrace( +1, Dbg, ("NtfsQueryPositionInfo...\n") );
  1369. RtlZeroMemory( Buffer, sizeof(FILE_POSITION_INFORMATION) );
  1370. *Length -= sizeof( FILE_POSITION_INFORMATION );
  1371. //
  1372. // Get the current position found in the file object.
  1373. //
  1374. Buffer->CurrentByteOffset = FileObject->CurrentByteOffset;
  1375. //
  1376. // And return to our caller
  1377. //
  1378. DebugTrace( 0, Dbg, ("*Length = %08lx\n", *Length) );
  1379. DebugTrace( -1, Dbg, ("NtfsQueryPositionInfo -> VOID\n") );
  1380. return;
  1381. }
  1382. //
  1383. // Internal Support Routine
  1384. //
  1385. NTSTATUS
  1386. NtfsQueryNameInfo (
  1387. IN PIRP_CONTEXT IrpContext,
  1388. IN PFILE_OBJECT FileObject,
  1389. IN PSCB Scb,
  1390. IN OUT PFILE_NAME_INFORMATION Buffer,
  1391. IN OUT PULONG Length,
  1392. IN PCCB Ccb
  1393. )
  1394. /*++
  1395. Routine Description:
  1396. This routine performs the query name information function.
  1397. Arguments:
  1398. FileObject - Supplies the file object being processed
  1399. Scb - Supplies the Scb being queried
  1400. Buffer - Supplies a pointer to the buffer where the information is to
  1401. be returned
  1402. Length - Supplies the length of the buffer in bytes, and receives the
  1403. remaining bytes free in the buffer upon return.
  1404. Ccb - This is the Ccb for this file object. If NULL then this request
  1405. is from the Lazy Writer.
  1406. Return Value:
  1407. NTSTATUS - STATUS_SUCCESS if the whole name would fit into the user buffer,
  1408. STATUS_BUFFER_OVERFLOW otherwise.
  1409. --*/
  1410. {
  1411. ULONG BytesToCopy;
  1412. NTSTATUS Status;
  1413. UNICODE_STRING NormalizedName;
  1414. PUNICODE_STRING SourceName;
  1415. ULONG AvailableNameLength;
  1416. ASSERT_IRP_CONTEXT( IrpContext );
  1417. ASSERT_FILE_OBJECT( FileObject );
  1418. ASSERT_SCB( Scb );
  1419. PAGED_CODE();
  1420. DebugTrace( +1, Dbg, ("NtfsQueryNameInfo...\n") );
  1421. NormalizedName.Buffer = NULL;
  1422. //
  1423. // Reduce the buffer length by the size of the fixed part of the structure.
  1424. //
  1425. RtlZeroMemory( Buffer, SIZEOF_FILE_NAME_INFORMATION );
  1426. *Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]);
  1427. //
  1428. // If the name length in this file object is zero, then we try to
  1429. // construct the name with the Lcb chain. This means we have been
  1430. // called by the system for a lazy write that failed.
  1431. //
  1432. if (Ccb == NULL) {
  1433. FILE_REFERENCE FileReference;
  1434. NtfsSetSegmentNumber( &FileReference, 0, UPCASE_TABLE_NUMBER );
  1435. //
  1436. // If this is a system file with a known name then just use our constant names.
  1437. //
  1438. if (NtfsLeqMftRef( &Scb->Fcb->FileReference, &FileReference )) {
  1439. SourceName =
  1440. (PUNICODE_STRING) &NtfsSystemFiles[ Scb->Fcb->FileReference.SegmentNumberLowPart ];
  1441. } else {
  1442. NtfsBuildNormalizedName( IrpContext, Scb->Fcb, FALSE, &NormalizedName );
  1443. SourceName = &NormalizedName;
  1444. }
  1445. } else {
  1446. if (FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_FILE_ID ) &&
  1447. !FlagOn( Ccb->Flags, CCB_FLAG_TRAVERSE_CHECK)) {
  1448. //
  1449. // If the file was opened by id, the Ccb doesn't
  1450. // have a full file name in it.
  1451. //
  1452. NtfsBuildNormalizedName( IrpContext, Scb->Fcb, FALSE, &NormalizedName );
  1453. SourceName = &NormalizedName;
  1454. } else {
  1455. //
  1456. // Use the name in the Ccb. This may be a relative name for
  1457. // some of the open by ID relative cases.
  1458. //
  1459. SourceName = &Ccb->FullFileName;
  1460. }
  1461. }
  1462. Buffer->FileNameLength = SourceName->Length;
  1463. if ((Scb->AttributeName.Length != 0) &&
  1464. NtfsIsTypeCodeUserData( Scb->AttributeTypeCode )) {
  1465. Buffer->FileNameLength += sizeof( WCHAR ) + Scb->AttributeName.Length;
  1466. }
  1467. //
  1468. // Figure out how many bytes we can copy.
  1469. //
  1470. AvailableNameLength = Buffer->FileNameLength;
  1471. if (*Length >= Buffer->FileNameLength) {
  1472. Status = STATUS_SUCCESS;
  1473. } else {
  1474. //
  1475. // If we don't have enough for the entire buffer then make sure we only
  1476. // return full characters (characters are UNICODE).
  1477. //
  1478. Status = STATUS_BUFFER_OVERFLOW;
  1479. AvailableNameLength = *Length & 0xfffffffe;
  1480. }
  1481. //
  1482. // Update the Length
  1483. //
  1484. *Length -= AvailableNameLength;
  1485. //
  1486. // Copy over the file name
  1487. //
  1488. if (SourceName->Length <= AvailableNameLength) {
  1489. BytesToCopy = SourceName->Length;
  1490. } else {
  1491. BytesToCopy = AvailableNameLength;
  1492. }
  1493. if (BytesToCopy) {
  1494. RtlCopyMemory( &Buffer->FileName[0],
  1495. SourceName->Buffer,
  1496. BytesToCopy );
  1497. }
  1498. BytesToCopy = AvailableNameLength - BytesToCopy;
  1499. if (BytesToCopy) {
  1500. PWCHAR DestBuffer;
  1501. DestBuffer = (PWCHAR) Add2Ptr( &Buffer->FileName, SourceName->Length );
  1502. *DestBuffer = L':';
  1503. DestBuffer += 1;
  1504. BytesToCopy -= sizeof( WCHAR );
  1505. if (BytesToCopy) {
  1506. RtlCopyMemory( DestBuffer,
  1507. Scb->AttributeName.Buffer,
  1508. BytesToCopy );
  1509. }
  1510. }
  1511. if ((SourceName == &NormalizedName) &&
  1512. (SourceName->Buffer != NULL)) {
  1513. NtfsFreePool( SourceName->Buffer );
  1514. }
  1515. //
  1516. // And return to our caller
  1517. //
  1518. DebugTrace( 0, Dbg, ("*Length = %08lx\n", *Length) );
  1519. DebugTrace( -1, Dbg, ("NtfsQueryNameInfo -> 0x%8lx\n", Status) );
  1520. return Status;
  1521. }
  1522. //
  1523. // Internal Support Routine
  1524. //
  1525. NTSTATUS
  1526. NtfsQueryAlternateNameInfo (
  1527. IN PIRP_CONTEXT IrpContext,
  1528. IN PSCB Scb,
  1529. IN PLCB Lcb,
  1530. IN OUT PFILE_NAME_INFORMATION Buffer,
  1531. IN OUT PULONG Length
  1532. )
  1533. /*++
  1534. Routine Description:
  1535. This routine performs the query alternate name information function.
  1536. We will return the alternate name as long as this opener has opened
  1537. a primary link. We don't return the alternate name if the user
  1538. has opened a hard link because there is no reason to expect that
  1539. the primary link has any relationship to a hard link.
  1540. Arguments:
  1541. Scb - Supplies the Scb being queried
  1542. Lcb - Supplies the link the user traversed to open this file.
  1543. Buffer - Supplies a pointer to the buffer where the information is to
  1544. be returned
  1545. Length - Supplies the length of the buffer in bytes, and receives the
  1546. remaining bytes free in the buffer upon return.
  1547. Return Value:
  1548. **** We need a status code for the case where there is no alternate name
  1549. or the caller isn't allowed to see it.
  1550. NTSTATUS - STATUS_SUCCESS if the whole name would fit into the user buffer,
  1551. STATUS_OBJECT_NAME_NOT_FOUND if we can't return the name,
  1552. STATUS_BUFFER_OVERFLOW otherwise.
  1553. **** A code like STATUS_NAME_NOT_FOUND would be good.
  1554. --*/
  1555. {
  1556. ULONG BytesToCopy;
  1557. NTSTATUS Status;
  1558. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  1559. BOOLEAN MoreToGo;
  1560. UNICODE_STRING AlternateName;
  1561. ASSERT_IRP_CONTEXT( IrpContext );
  1562. ASSERT_SCB( Scb );
  1563. ASSERT_LCB( Lcb );
  1564. PAGED_CODE();
  1565. DebugTrace( +1, Dbg, ("NtfsQueryAlternateNameInfo...\n") );
  1566. //
  1567. // If the Lcb is not a primary link we can return immediately.
  1568. //
  1569. if (!FlagOn( Lcb->FileNameAttr->Flags, FILE_NAME_DOS | FILE_NAME_NTFS )) {
  1570. DebugTrace( -1, Dbg, ("NtfsQueryAlternateNameInfo: Lcb not a primary link\n") );
  1571. return STATUS_OBJECT_NAME_NOT_FOUND;
  1572. }
  1573. //
  1574. // Reduce the buffer length by the size of the fixed part of the structure.
  1575. //
  1576. if (*Length < SIZEOF_FILE_NAME_INFORMATION ) {
  1577. *Length = 0;
  1578. NtfsRaiseStatus( IrpContext, STATUS_BUFFER_OVERFLOW, NULL, NULL );
  1579. }
  1580. RtlZeroMemory( Buffer, SIZEOF_FILE_NAME_INFORMATION );
  1581. *Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]);
  1582. NtfsInitializeAttributeContext( &AttrContext );
  1583. //
  1584. // Use a try-finally to cleanup the attribut structure if we need it.
  1585. //
  1586. try {
  1587. //
  1588. // We can special case for the case where the name is in the Lcb.
  1589. //
  1590. if (FlagOn( Lcb->FileNameAttr->Flags, FILE_NAME_DOS )) {
  1591. AlternateName = Lcb->ExactCaseLink.LinkName;
  1592. } else {
  1593. //
  1594. // We will walk through the file record looking for a file name
  1595. // attribute with the 8.3 bit set. It is not guaranteed to be
  1596. // present.
  1597. //
  1598. MoreToGo = NtfsLookupAttributeByCode( IrpContext,
  1599. Scb->Fcb,
  1600. &Scb->Fcb->FileReference,
  1601. $FILE_NAME,
  1602. &AttrContext );
  1603. while (MoreToGo) {
  1604. PFILE_NAME FileName;
  1605. FileName = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  1606. //
  1607. // See if the 8.3 flag is set for this name.
  1608. //
  1609. if (FlagOn( FileName->Flags, FILE_NAME_DOS )) {
  1610. AlternateName.Length = (USHORT)(FileName->FileNameLength * sizeof( WCHAR ));
  1611. AlternateName.Buffer = (PWSTR) FileName->FileName;
  1612. break;
  1613. }
  1614. //
  1615. // The last one wasn't it. Let's try again.
  1616. //
  1617. MoreToGo = NtfsLookupNextAttributeByCode( IrpContext,
  1618. Scb->Fcb,
  1619. $FILE_NAME,
  1620. &AttrContext );
  1621. }
  1622. //
  1623. // If we didn't find a match, return to the caller.
  1624. //
  1625. if (!MoreToGo) {
  1626. DebugTrace( 0, Dbg, ("NtfsQueryAlternateNameInfo: No Dos link\n") );
  1627. try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND );
  1628. //
  1629. // **** Get a better status code.
  1630. //
  1631. }
  1632. }
  1633. //
  1634. // The name is now in alternate name.
  1635. // Figure out how many bytes we can copy.
  1636. //
  1637. if ( *Length >= (ULONG)AlternateName.Length ) {
  1638. Status = STATUS_SUCCESS;
  1639. BytesToCopy = AlternateName.Length;
  1640. } else {
  1641. Status = STATUS_BUFFER_OVERFLOW;
  1642. BytesToCopy = *Length;
  1643. }
  1644. //
  1645. // Copy over the file name
  1646. //
  1647. RtlCopyMemory( Buffer->FileName, AlternateName.Buffer, BytesToCopy);
  1648. //
  1649. // Copy the number of bytes (not characters) and update the Length
  1650. //
  1651. Buffer->FileNameLength = BytesToCopy;
  1652. *Length -= BytesToCopy;
  1653. try_exit: NOTHING;
  1654. } finally {
  1655. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  1656. //
  1657. // And return to our caller
  1658. //
  1659. DebugTrace( 0, Dbg, ("*Length = %08lx\n", *Length) );
  1660. DebugTrace( -1, Dbg, ("NtfsQueryAlternateNameInfo -> 0x%8lx\n", Status) );
  1661. }
  1662. return Status;
  1663. }
  1664. //
  1665. // Local support routine
  1666. //
  1667. NTSTATUS
  1668. NtfsQueryStreamsInfo (
  1669. IN PIRP_CONTEXT IrpContext,
  1670. IN PFCB Fcb,
  1671. IN OUT PFILE_STREAM_INFORMATION Buffer,
  1672. IN OUT PULONG Length
  1673. )
  1674. /*++
  1675. Routine Description:
  1676. This routine will return the attribute name and code name for as
  1677. many attributes in the file as will fit in the user buffer. We return
  1678. a string which can be appended to the end of the file name to
  1679. open the string.
  1680. For example, for the unnamed data stream we will return the string:
  1681. "::$DATA"
  1682. For a user data stream with the name "Authors", we return the string
  1683. ":Authors:$DATA"
  1684. Arguments:
  1685. Fcb - This is the Fcb for the file.
  1686. Length - Supplies the length of the buffer in bytes, and receives the
  1687. remaining bytes free in the buffer upon return.
  1688. Return Value:
  1689. NTSTATUS - STATUS_SUCCESS if all of the names would fit into the user buffer,
  1690. STATUS_BUFFER_OVERFLOW otherwise.
  1691. **** We need a code indicating that they didn't all fit but
  1692. some of them got in.
  1693. --*/
  1694. {
  1695. NTSTATUS Status;
  1696. BOOLEAN MoreToGo;
  1697. PUCHAR UserBuffer;
  1698. PATTRIBUTE_RECORD_HEADER Attribute;
  1699. PATTRIBUTE_DEFINITION_COLUMNS AttrDefinition;
  1700. UNICODE_STRING AttributeCodeString;
  1701. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  1702. ATTRIBUTE_TYPE_CODE TypeCode = $DATA;
  1703. ULONG NextEntry;
  1704. ULONG LastEntry;
  1705. ULONG ThisLength;
  1706. ULONG NameLength;
  1707. ULONG LastQuadAlign;
  1708. ASSERT_IRP_CONTEXT( IrpContext );
  1709. ASSERT_FCB( Fcb );
  1710. PAGED_CODE();
  1711. DebugTrace( +1, Dbg, ("NtfsQueryStreamsInfo...\n") );
  1712. Status = STATUS_SUCCESS;
  1713. LastEntry = 0;
  1714. NextEntry = 0;
  1715. LastQuadAlign = 0;
  1716. //
  1717. // Zero the entire buffer.
  1718. //
  1719. UserBuffer = (PUCHAR) Buffer;
  1720. RtlZeroMemory( UserBuffer, *Length );
  1721. //
  1722. // Use a try-finally to facilitate cleanup.
  1723. //
  1724. try {
  1725. NtfsInitializeAttributeContext( &AttrContext );
  1726. //
  1727. // There should always be at least one attribute.
  1728. //
  1729. MoreToGo = NtfsLookupAttribute( IrpContext,
  1730. Fcb,
  1731. &Fcb->FileReference,
  1732. &AttrContext );
  1733. Attribute = NtfsFoundAttribute( &AttrContext );
  1734. //
  1735. // Walk through all of the entries, checking if we can return this
  1736. // entry to the user and if it will fit in the buffer.
  1737. //
  1738. while (MoreToGo) {
  1739. //
  1740. // If we can return this entry to the user, compute it's size.
  1741. // We only return user defined attributes or data streams
  1742. // unless we are allowing access to all attributes for
  1743. // debugging.
  1744. //
  1745. if ((Attribute->TypeCode == TypeCode)
  1746. &&
  1747. (NtfsIsAttributeResident(Attribute) ||
  1748. (Attribute->Form.Nonresident.LowestVcn == 0))) {
  1749. PWCHAR StreamName;
  1750. //
  1751. // Lookup the attribute definition for this attribute code.
  1752. //
  1753. AttrDefinition = NtfsGetAttributeDefinition( Fcb->Vcb,
  1754. Attribute->TypeCode );
  1755. //
  1756. // Generate a unicode string for the attribute code name.
  1757. //
  1758. RtlInitUnicodeString( &AttributeCodeString, AttrDefinition->AttributeName );
  1759. //
  1760. //
  1761. // The size is a combination of the length of the attribute
  1762. // code name and the attribute name plus the separating
  1763. // colons plus the size of the structure. We first compute
  1764. // the name length.
  1765. //
  1766. NameLength = ((2 + Attribute->NameLength) * sizeof( WCHAR ))
  1767. + AttributeCodeString.Length;
  1768. ThisLength = FIELD_OFFSET( FILE_STREAM_INFORMATION, StreamName[0] ) + NameLength;
  1769. //
  1770. // If the entry doesn't fit, we return buffer overflow.
  1771. //
  1772. // **** This doesn't seem like a good scheme. Maybe we should
  1773. // let the user know how much buffer was needed.
  1774. //
  1775. if (ThisLength + LastQuadAlign > *Length) {
  1776. DebugTrace( 0, Dbg, ("Next entry won't fit in the buffer \n") );
  1777. Status = STATUS_BUFFER_OVERFLOW ;
  1778. leave;
  1779. }
  1780. //
  1781. // Now store the stream information into the user's buffer.
  1782. // The name starts with a colon, following by the attribute name
  1783. // and another colon, followed by the attribute code name.
  1784. //
  1785. if (NtfsIsAttributeResident( Attribute )) {
  1786. Buffer->StreamSize.QuadPart =
  1787. Attribute->Form.Resident.ValueLength;
  1788. Buffer->StreamAllocationSize.QuadPart =
  1789. QuadAlign( Attribute->Form.Resident.ValueLength );
  1790. } else {
  1791. Buffer->StreamSize.QuadPart = Attribute->Form.Nonresident.FileSize;
  1792. Buffer->StreamAllocationSize.QuadPart = Attribute->Form.Nonresident.AllocatedLength;
  1793. }
  1794. Buffer->StreamNameLength = NameLength;
  1795. StreamName = (PWCHAR) Buffer->StreamName;
  1796. *StreamName = L':';
  1797. StreamName += 1;
  1798. RtlCopyMemory( StreamName,
  1799. Add2Ptr( Attribute, Attribute->NameOffset ),
  1800. Attribute->NameLength * sizeof( WCHAR ));
  1801. StreamName += Attribute->NameLength;
  1802. *StreamName = L':';
  1803. StreamName += 1;
  1804. RtlCopyMemory( StreamName,
  1805. AttributeCodeString.Buffer,
  1806. AttributeCodeString.Length );
  1807. //
  1808. // Set up the previous next entry offset to point to this entry.
  1809. //
  1810. *((PULONG)(&UserBuffer[LastEntry])) = NextEntry - LastEntry;
  1811. //
  1812. // Subtract the number of bytes used from the number of bytes
  1813. // available in the buffer.
  1814. //
  1815. *Length -= (ThisLength + LastQuadAlign);
  1816. //
  1817. // Compute the number of bytes needed to quad-align this entry
  1818. // and the offset of the next entry.
  1819. //
  1820. LastQuadAlign = QuadAlign( ThisLength ) - ThisLength;
  1821. LastEntry = NextEntry;
  1822. NextEntry += (ThisLength + LastQuadAlign);
  1823. //
  1824. // Generate a pointer at the next entry offset.
  1825. //
  1826. Buffer = (PFILE_STREAM_INFORMATION) Add2Ptr( UserBuffer, NextEntry );
  1827. }
  1828. //
  1829. // Look for the next attribute in the file.
  1830. //
  1831. MoreToGo = NtfsLookupNextAttribute( IrpContext,
  1832. Fcb,
  1833. &AttrContext );
  1834. Attribute = NtfsFoundAttribute( &AttrContext );
  1835. }
  1836. } finally {
  1837. DebugUnwind( NtfsQueryStreamsInfo );
  1838. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  1839. //
  1840. // And return to our caller
  1841. //
  1842. DebugTrace( 0, Dbg, ("*Length = %08lx\n", *Length) );
  1843. DebugTrace( -1, Dbg, ("NtfsQueryStreamInfo -> 0x%8lx\n", Status) );
  1844. }
  1845. return Status;
  1846. }
  1847. //
  1848. // Local support routine
  1849. //
  1850. NTSTATUS
  1851. NtfsQueryCompressedFileSize (
  1852. IN PIRP_CONTEXT IrpContext,
  1853. IN PSCB Scb,
  1854. IN OUT PFILE_COMPRESSION_INFORMATION Buffer,
  1855. IN OUT PULONG Length
  1856. )
  1857. /*++
  1858. Routine Description:
  1859. Arguments:
  1860. Return Value:
  1861. --*/
  1862. {
  1863. //
  1864. // Lookup the attribute and pin it so that we can modify it.
  1865. //
  1866. //
  1867. // Reduce the buffer length by the size of the fixed part of the structure.
  1868. //
  1869. if (*Length < sizeof(FILE_COMPRESSION_INFORMATION) ) {
  1870. *Length = 0;
  1871. NtfsRaiseStatus( IrpContext, STATUS_BUFFER_OVERFLOW, NULL, NULL );
  1872. }
  1873. if ((Scb->Header.NodeTypeCode == NTFS_NTC_SCB_INDEX) ||
  1874. (Scb->Header.NodeTypeCode == NTFS_NTC_SCB_ROOT_INDEX)) {
  1875. Buffer->CompressedFileSize = Li0;
  1876. } else {
  1877. Buffer->CompressedFileSize.QuadPart = Scb->TotalAllocated;
  1878. }
  1879. //
  1880. // Do not return more than FileSize.
  1881. //
  1882. if (Buffer->CompressedFileSize.QuadPart > Scb->Header.FileSize.QuadPart) {
  1883. Buffer->CompressedFileSize = Scb->Header.FileSize;
  1884. }
  1885. //
  1886. // Start off saying that the file/directory isn't comressed
  1887. //
  1888. Buffer->CompressionFormat = 0;
  1889. //
  1890. // If this is the index allocation Scb and it has not been initialized then
  1891. // lookup the index root and perform the initialization.
  1892. //
  1893. if ((Scb->AttributeTypeCode == $INDEX_ALLOCATION) &&
  1894. (Scb->ScbType.Index.BytesPerIndexBuffer == 0)) {
  1895. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  1896. NtfsInitializeAttributeContext( &Context );
  1897. //
  1898. // Use a try-finally to perform cleanup.
  1899. //
  1900. try {
  1901. if (!NtfsLookupAttributeByName( IrpContext,
  1902. Scb->Fcb,
  1903. &Scb->Fcb->FileReference,
  1904. $INDEX_ROOT,
  1905. &Scb->AttributeName,
  1906. NULL,
  1907. FALSE,
  1908. &Context )) {
  1909. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  1910. }
  1911. NtfsUpdateIndexScbFromAttribute( IrpContext,
  1912. Scb,
  1913. NtfsFoundAttribute( &Context ),
  1914. FALSE );
  1915. } finally {
  1916. NtfsCleanupAttributeContext( IrpContext, &Context );
  1917. }
  1918. }
  1919. //
  1920. // Return the compression state and the size of the returned data.
  1921. //
  1922. Buffer->CompressionFormat = (USHORT)(Scb->AttributeFlags & ATTRIBUTE_FLAG_COMPRESSION_MASK);
  1923. if (Buffer->CompressionFormat != 0) {
  1924. Buffer->CompressionFormat += 1;
  1925. Buffer->ClusterShift = (UCHAR)Scb->Vcb->ClusterShift;
  1926. Buffer->CompressionUnitShift = (UCHAR)(Scb->CompressionUnitShift + Buffer->ClusterShift);
  1927. Buffer->ChunkShift = NTFS_CHUNK_SHIFT;
  1928. }
  1929. *Length -= sizeof(FILE_COMPRESSION_INFORMATION);
  1930. return STATUS_SUCCESS;
  1931. }
  1932. //
  1933. // Internal Support Routine
  1934. //
  1935. VOID
  1936. NtfsQueryNetworkOpenInfo (
  1937. IN PIRP_CONTEXT IrpContext,
  1938. IN PFILE_OBJECT FileObject,
  1939. IN PSCB Scb,
  1940. IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer,
  1941. IN OUT PULONG Length
  1942. )
  1943. /*++
  1944. Routine Description:
  1945. This routine performs the query network open information function.
  1946. Arguments:
  1947. FileObject - Supplies the file object being processed
  1948. Scb - Supplies the Scb being queried
  1949. Buffer - Supplies a pointer to the buffer where the information is to
  1950. be returned
  1951. Length - Supplies the length of the buffer in bytes, and receives the
  1952. remaining bytes free in the buffer upon return.
  1953. Return Value:
  1954. None
  1955. --*/
  1956. {
  1957. PFCB Fcb;
  1958. ASSERT_IRP_CONTEXT( IrpContext );
  1959. ASSERT_FILE_OBJECT( FileObject );
  1960. ASSERT_SCB( Scb );
  1961. PAGED_CODE();
  1962. DebugTrace( +1, Dbg, ("NtfsQueryNetworkOpenInfo...\n") );
  1963. Fcb = Scb->Fcb;
  1964. //
  1965. // If the Scb is uninitialized, we initialize it now.
  1966. //
  1967. if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED ) &&
  1968. (Scb->AttributeTypeCode != $INDEX_ALLOCATION)) {
  1969. DebugTrace( 0, Dbg, ("Initializing Scb -> %08lx\n", Scb) );
  1970. NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
  1971. }
  1972. //
  1973. // Update the length.
  1974. //
  1975. *Length -= sizeof( FILE_NETWORK_OPEN_INFORMATION );
  1976. //
  1977. // Copy over the data.
  1978. //
  1979. NtfsFillNetworkOpenInfo( Buffer, Scb );
  1980. //
  1981. // And return to our caller
  1982. //
  1983. DebugTrace( 0, Dbg, ("*Length = %08lx\n", *Length) );
  1984. DebugTrace( -1, Dbg, ("NtfsQueryNetworkOpenInfo -> VOID\n") );
  1985. return;
  1986. }
  1987. //
  1988. // Internal Support Routine
  1989. //
  1990. NTSTATUS
  1991. NtfsSetBasicInfo (
  1992. IN PIRP_CONTEXT IrpContext,
  1993. IN PFILE_OBJECT FileObject,
  1994. IN PIRP Irp,
  1995. IN PSCB Scb,
  1996. IN PCCB Ccb
  1997. )
  1998. /*++
  1999. Routine Description:
  2000. This routine performs the set basic information function.
  2001. Arguments:
  2002. FileObject - Supplies the file object being processed
  2003. Irp - Supplies the Irp being processed
  2004. Scb - Supplies the Scb for the file/directory being modified
  2005. Ccb - Supplies the Ccb for this operation
  2006. Return Value:
  2007. NTSTATUS - The status of the operation
  2008. --*/
  2009. {
  2010. NTSTATUS Status;
  2011. PFCB Fcb;
  2012. ULONG UsnReason = 0;
  2013. ULONG NewCcbFlags = 0;
  2014. PFILE_BASIC_INFORMATION Buffer;
  2015. ULONG PreviousFileAttributes = Scb->Fcb->Info.FileAttributes;
  2016. BOOLEAN LeaveChangeTime = BooleanFlagOn( Ccb->Flags, CCB_FLAG_USER_SET_LAST_CHANGE_TIME );
  2017. LONGLONG CurrentTime;
  2018. ASSERT_IRP_CONTEXT( IrpContext );
  2019. ASSERT_FILE_OBJECT( FileObject );
  2020. ASSERT_IRP( Irp );
  2021. ASSERT_SCB( Scb );
  2022. PAGED_CODE();
  2023. DebugTrace( +1, Dbg, ("NtfsSetBasicInfo...\n") );
  2024. Fcb = Scb->Fcb;
  2025. //
  2026. // Reference the system buffer containing the user specified basic
  2027. // information record
  2028. //
  2029. Buffer = Irp->AssociatedIrp.SystemBuffer;
  2030. //
  2031. // Remember the source info flags in the Ccb.
  2032. //
  2033. IrpContext->SourceInfo = Ccb->UsnSourceInfo;
  2034. //
  2035. // If the user is specifying -1 for a field, that means
  2036. // we should leave that field unchanged, even if we might
  2037. // have otherwise set it ourselves. We'll set the
  2038. // Ccb flag saying the user set the field so that we
  2039. // don't do our default updating.
  2040. //
  2041. // We set the field to 0 then so we know not to actually
  2042. // set the field to the user-specified (and in this case,
  2043. // illegal) value.
  2044. //
  2045. if (Buffer->ChangeTime.QuadPart == -1) {
  2046. SetFlag( NewCcbFlags, CCB_FLAG_USER_SET_LAST_CHANGE_TIME );
  2047. Buffer->ChangeTime.QuadPart = 0;
  2048. //
  2049. // This timestamp is special -- sometimes even this very
  2050. // function wants to update the ChangeTime, but if the
  2051. // user is asking us not to, we shouldn't.
  2052. //
  2053. LeaveChangeTime = TRUE;
  2054. }
  2055. if (Buffer->LastAccessTime.QuadPart == -1) {
  2056. SetFlag( NewCcbFlags, CCB_FLAG_USER_SET_LAST_ACCESS_TIME );
  2057. Buffer->LastAccessTime.QuadPart = 0;
  2058. }
  2059. if (Buffer->LastWriteTime.QuadPart == -1) {
  2060. SetFlag( NewCcbFlags, CCB_FLAG_USER_SET_LAST_MOD_TIME );
  2061. Buffer->LastWriteTime.QuadPart = 0;
  2062. }
  2063. if (Buffer->CreationTime.QuadPart == -1) {
  2064. //
  2065. // We only set the creation time at creation time anyway (how
  2066. // appropriate), so we don't need to set a Ccb flag in this
  2067. // case. In fact, there isn't even a Ccb flag to signify
  2068. // that the user set the creation time.
  2069. //
  2070. Buffer->CreationTime.QuadPart = 0;
  2071. }
  2072. //
  2073. // Do a quick check to see there are any illegal time stamps being set.
  2074. // Ntfs supports all values of Nt time as long as the uppermost bit
  2075. // isn't set.
  2076. //
  2077. if (FlagOn( Buffer->ChangeTime.HighPart, 0x80000000 ) ||
  2078. FlagOn( Buffer->CreationTime.HighPart, 0x80000000 ) ||
  2079. FlagOn( Buffer->LastAccessTime.HighPart, 0x80000000 ) ||
  2080. FlagOn( Buffer->LastWriteTime.HighPart, 0x80000000 )) {
  2081. DebugTrace( -1, Dbg, ("NtfsSetBasicInfo -> %08lx\n", STATUS_INVALID_PARAMETER) );
  2082. return STATUS_INVALID_PARAMETER;
  2083. }
  2084. NtfsGetCurrentTime( IrpContext, CurrentTime );
  2085. //
  2086. // Pick up any changes from the fast Io path now while we have the
  2087. // file exclusive.
  2088. //
  2089. NtfsUpdateScbFromFileObject( IrpContext, FileObject, Scb, TRUE );
  2090. //
  2091. // If the user specified a non-zero file attributes field then
  2092. // we need to change the file attributes. This code uses the
  2093. // I/O supplied system buffer to modify the file attributes field
  2094. // before changing its value on the disk.
  2095. //
  2096. if (Buffer->FileAttributes != 0) {
  2097. //
  2098. // Check for valid flags being passed in. We fail if this is
  2099. // a directory and the TEMPORARY bit is used. Also fail if this
  2100. // is a file and the DIRECTORY bit is used.
  2101. //
  2102. if (Scb->AttributeTypeCode == $DATA) {
  2103. if (FlagOn( Buffer->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) {
  2104. DebugTrace( -1, Dbg, ("NtfsSetBasicInfo -> %08lx\n", STATUS_INVALID_PARAMETER) );
  2105. return STATUS_INVALID_PARAMETER;
  2106. }
  2107. } else if (IsDirectory( &Fcb->Info )) {
  2108. if (FlagOn( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY )) {
  2109. DebugTrace( -1, Dbg, ("NtfsSetBasicInfo -> %08lx\n", STATUS_INVALID_PARAMETER) );
  2110. return STATUS_INVALID_PARAMETER;
  2111. }
  2112. }
  2113. //
  2114. // Clear out the normal bit and the directory bit as well as any unsupported
  2115. // bits.
  2116. //
  2117. ClearFlag( Buffer->FileAttributes,
  2118. ~FILE_ATTRIBUTE_VALID_SET_FLAGS | FILE_ATTRIBUTE_NORMAL );
  2119. //
  2120. // Update the attributes in the Fcb if this is a change to the file.
  2121. // We want to keep the flags that the user can't set.
  2122. //
  2123. Fcb->Info.FileAttributes = (Fcb->Info.FileAttributes & ~FILE_ATTRIBUTE_VALID_SET_FLAGS) |
  2124. Buffer->FileAttributes;
  2125. ASSERTMSG( "conflict with flush",
  2126. NtfsIsSharedFcb( Fcb ) ||
  2127. (Fcb->PagingIoResource != NULL &&
  2128. NtfsIsSharedFcbPagingIo( Fcb )) );
  2129. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  2130. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_FILE_ATTR );
  2131. //
  2132. // If this is the root directory then keep the hidden and system flags.
  2133. //
  2134. if (Fcb == Fcb->Vcb->RootIndexScb->Fcb) {
  2135. SetFlag( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN );
  2136. //
  2137. // Mark the file object temporary flag correctly.
  2138. //
  2139. } else if (FlagOn(Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY)) {
  2140. SetFlag( Scb->ScbState, SCB_STATE_TEMPORARY );
  2141. SetFlag( FileObject->Flags, FO_TEMPORARY_FILE );
  2142. } else {
  2143. ClearFlag( Scb->ScbState, SCB_STATE_TEMPORARY );
  2144. ClearFlag( FileObject->Flags, FO_TEMPORARY_FILE );
  2145. }
  2146. if (!LeaveChangeTime) {
  2147. Fcb->Info.LastChangeTime = CurrentTime;
  2148. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_LAST_CHANGE );
  2149. LeaveChangeTime = TRUE;
  2150. }
  2151. //
  2152. // Post a Usn change if the file attribute change.
  2153. //
  2154. if (PreviousFileAttributes != Fcb->Info.FileAttributes) {
  2155. UsnReason = USN_REASON_BASIC_INFO_CHANGE;
  2156. }
  2157. }
  2158. //
  2159. // Propagate the new Ccb flags to the Ccb now that we know we won't fail.
  2160. //
  2161. SetFlag( Ccb->Flags, NewCcbFlags );
  2162. //
  2163. // If the user specified a non-zero change time then change
  2164. // the change time on the record. Then do the exact same
  2165. // for the last acces time, last write time, and creation time
  2166. //
  2167. if (Buffer->ChangeTime.QuadPart != 0) {
  2168. if (Fcb->Info.LastChangeTime != Buffer->ChangeTime.QuadPart) {
  2169. UsnReason = USN_REASON_BASIC_INFO_CHANGE;
  2170. }
  2171. Fcb->Info.LastChangeTime = Buffer->ChangeTime.QuadPart;
  2172. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  2173. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_LAST_CHANGE );
  2174. SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_CHANGE_TIME );
  2175. LeaveChangeTime = TRUE;
  2176. }
  2177. if (Buffer->CreationTime.QuadPart != 0) {
  2178. if (Fcb->Info.CreationTime != Buffer->CreationTime.QuadPart) {
  2179. UsnReason = USN_REASON_BASIC_INFO_CHANGE;
  2180. }
  2181. Fcb->Info.CreationTime = Buffer->CreationTime.QuadPart;
  2182. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  2183. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_CREATE );
  2184. if (!LeaveChangeTime) {
  2185. Fcb->Info.LastChangeTime = CurrentTime;
  2186. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_LAST_CHANGE );
  2187. LeaveChangeTime = TRUE;
  2188. }
  2189. }
  2190. if (Buffer->LastAccessTime.QuadPart != 0) {
  2191. if (Fcb->CurrentLastAccess != Buffer->LastAccessTime.QuadPart) {
  2192. UsnReason = USN_REASON_BASIC_INFO_CHANGE;
  2193. }
  2194. Fcb->CurrentLastAccess = Fcb->Info.LastAccessTime = Buffer->LastAccessTime.QuadPart;
  2195. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  2196. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_LAST_ACCESS );
  2197. SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS_TIME );
  2198. if (!LeaveChangeTime) {
  2199. Fcb->Info.LastChangeTime = CurrentTime;
  2200. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_LAST_CHANGE );
  2201. LeaveChangeTime = TRUE;
  2202. }
  2203. }
  2204. if (Buffer->LastWriteTime.QuadPart != 0) {
  2205. if (Fcb->Info.LastModificationTime != Buffer->LastWriteTime.QuadPart) {
  2206. UsnReason = USN_REASON_BASIC_INFO_CHANGE;
  2207. }
  2208. Fcb->Info.LastModificationTime = Buffer->LastWriteTime.QuadPart;
  2209. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  2210. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_LAST_MOD );
  2211. SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_MOD_TIME );
  2212. if (!LeaveChangeTime) {
  2213. Fcb->Info.LastChangeTime = CurrentTime;
  2214. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_LAST_CHANGE );
  2215. LeaveChangeTime = TRUE;
  2216. }
  2217. }
  2218. //
  2219. // Now indicate that we should not be updating the standard information attribute anymore
  2220. // on cleanup.
  2221. //
  2222. if (FlagOn( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO )) {
  2223. //
  2224. // Check if the index bit changed.
  2225. //
  2226. if (FlagOn( PreviousFileAttributes ^ Fcb->Info.FileAttributes,
  2227. FILE_ATTRIBUTE_NOT_CONTENT_INDEXED )) {
  2228. SetFlag( UsnReason, USN_REASON_INDEXABLE_CHANGE );
  2229. }
  2230. //
  2231. // Post the change to the Usn Journal
  2232. //
  2233. if (UsnReason != 0) {
  2234. NtfsPostUsnChange( IrpContext, Scb, UsnReason );
  2235. }
  2236. NtfsUpdateStandardInformation( IrpContext, Fcb );
  2237. if (FlagOn( Scb->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE )) {
  2238. NtfsWriteFileSizes( IrpContext,
  2239. Scb,
  2240. &Scb->Header.ValidDataLength.QuadPart,
  2241. FALSE,
  2242. TRUE,
  2243. FALSE );
  2244. ClearFlag( Scb->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE );
  2245. }
  2246. ClearFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  2247. if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_FILE_ID )) {
  2248. NtfsCheckpointCurrentTransaction( IrpContext );
  2249. NtfsUpdateFileDupInfo( IrpContext, Fcb, Ccb );
  2250. }
  2251. }
  2252. Status = STATUS_SUCCESS;
  2253. //
  2254. // And return to our caller
  2255. //
  2256. DebugTrace( -1, Dbg, ("NtfsSetBasicInfo -> %08lx\n", Status) );
  2257. return Status;
  2258. }
  2259. //
  2260. // Internal Support Routine
  2261. //
  2262. NTSTATUS
  2263. NtfsSetDispositionInfo (
  2264. IN PIRP_CONTEXT IrpContext,
  2265. IN PFILE_OBJECT FileObject,
  2266. IN PIRP Irp,
  2267. IN PSCB Scb,
  2268. IN PCCB Ccb
  2269. )
  2270. /*++
  2271. Routine Description:
  2272. This routine performs the set disposition information function.
  2273. Arguments:
  2274. FileObject - Supplies the file object being processed
  2275. Irp - Supplies the Irp being processed
  2276. Scb - Supplies the Scb for the file/directory being modified
  2277. Ccb - Supplies the Ccb for this handle
  2278. Return Value:
  2279. NTSTATUS - The status of the operation
  2280. --*/
  2281. {
  2282. NTSTATUS Status;
  2283. PLCB Lcb;
  2284. BOOLEAN GenerateOnClose = FALSE;
  2285. PIO_STACK_LOCATION IrpSp;
  2286. HANDLE FileHandle = NULL;
  2287. PFILE_DISPOSITION_INFORMATION Buffer;
  2288. ASSERT_IRP_CONTEXT( IrpContext );
  2289. ASSERT_FILE_OBJECT( FileObject );
  2290. ASSERT_IRP( Irp );
  2291. ASSERT_SCB( Scb );
  2292. PAGED_CODE();
  2293. DebugTrace( +1, Dbg, ("NtfsSetDispositionInfo...\n") );
  2294. //
  2295. // First pull the file handle out of the irp
  2296. //
  2297. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  2298. FileHandle = IrpSp->Parameters.SetFile.DeleteHandle;
  2299. //
  2300. // We get the Lcb for this open. If there is no link then we can't
  2301. // set any disposition information if this is a file.
  2302. //
  2303. Lcb = Ccb->Lcb;
  2304. if ((Lcb == NULL) &&
  2305. FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE )) {
  2306. DebugTrace( -1, Dbg, ("NtfsSetDispositionInfo: Exit -> %08lx\n", STATUS_INVALID_PARAMETER) );
  2307. return STATUS_INVALID_PARAMETER;
  2308. }
  2309. //
  2310. // Reference the system buffer containing the user specified disposition
  2311. // information record
  2312. //
  2313. Buffer = Irp->AssociatedIrp.SystemBuffer;
  2314. try {
  2315. if (Buffer->DeleteFile) {
  2316. //
  2317. // Check if the file is marked read only
  2318. //
  2319. if (IsReadOnly( &Scb->Fcb->Info )) {
  2320. DebugTrace( 0, Dbg, ("File fat flags indicates read only\n") );
  2321. try_return( Status = STATUS_CANNOT_DELETE );
  2322. }
  2323. //
  2324. // Make sure there is no process mapping this file as an image
  2325. //
  2326. if (!MmFlushImageSection( &Scb->NonpagedScb->SegmentObject,
  2327. MmFlushForDelete )) {
  2328. DebugTrace( 0, Dbg, ("Failed to flush image section\n") );
  2329. try_return( Status = STATUS_CANNOT_DELETE );
  2330. }
  2331. //
  2332. // Check that we are not trying to delete one of the special
  2333. // system files.
  2334. //
  2335. if (FlagOn( Scb->Fcb->FcbState, FCB_STATE_SYSTEM_FILE )) {
  2336. DebugTrace( 0, Dbg, ("Scb is one of the special system files\n") );
  2337. try_return( Status = STATUS_CANNOT_DELETE );
  2338. }
  2339. //
  2340. // Only do the auditing if we have a user handle. We verify that the FileHandle
  2341. // is still valid and hasn't gone through close. Note we first check the CCB state
  2342. // to see if the cleanup has been issued. If the CCB state is valid then we are
  2343. // guaranteed the handle couldn't have been reused by the object manager even if
  2344. // the user close in another thread has gone through OB. This is because this request
  2345. // is serialized with Ntfs cleanup.
  2346. //
  2347. if (FileHandle != NULL) {
  2348. //
  2349. // Check for serialization with Ntfs cleanup first.
  2350. //
  2351. if (FlagOn( Ccb->Flags, CCB_FLAG_CLEANUP )) {
  2352. DebugTrace( 0, Dbg, ("This call issued after cleanup\n") );
  2353. try_return( Status = STATUS_INVALID_HANDLE );
  2354. }
  2355. Status = ObQueryObjectAuditingByHandle( FileHandle,
  2356. &GenerateOnClose );
  2357. //
  2358. // Fail the request if the object manager doesn't recognize the handle.
  2359. //
  2360. if (!NT_SUCCESS( Status )) {
  2361. DebugTrace( 0, Dbg, ("Object manager fails to recognize handle\n") );
  2362. try_return( Status );
  2363. }
  2364. }
  2365. //
  2366. // Now check that the file is really deleteable according to indexsup
  2367. //
  2368. if (FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE )) {
  2369. BOOLEAN LastLink;
  2370. BOOLEAN NonEmptyIndex = FALSE;
  2371. //
  2372. // If the link is not deleted, we check if it can be deleted.
  2373. //
  2374. if (!LcbLinkIsDeleted( Lcb )) {
  2375. if (NtfsIsLinkDeleteable( IrpContext, Scb->Fcb, &NonEmptyIndex, &LastLink )) {
  2376. //
  2377. // It is ok to get rid of this guy. All we need to do is
  2378. // mark this Lcb for delete and decrement the link count
  2379. // in the Fcb. If this is a primary link, then we
  2380. // indicate that the primary link has been deleted.
  2381. //
  2382. SetFlag( Lcb->LcbState, LCB_STATE_DELETE_ON_CLOSE );
  2383. ASSERTMSG( "Link count should not be 0\n", Scb->Fcb->LinkCount != 0 );
  2384. Scb->Fcb->LinkCount -= 1;
  2385. if (FlagOn( Lcb->FileNameAttr->Flags, FILE_NAME_DOS | FILE_NAME_NTFS )) {
  2386. SetFlag( Scb->Fcb->FcbState, FCB_STATE_PRIMARY_LINK_DELETED );
  2387. }
  2388. //
  2389. // Call into the notify package to close any handles on
  2390. // a directory being deleted.
  2391. //
  2392. if (IsDirectory( &Scb->Fcb->Info )) {
  2393. FsRtlNotifyFilterChangeDirectory( Scb->Vcb->NotifySync,
  2394. &Scb->Vcb->DirNotifyList,
  2395. FileObject->FsContext,
  2396. NULL,
  2397. FALSE,
  2398. FALSE,
  2399. 0,
  2400. NULL,
  2401. NULL,
  2402. NULL,
  2403. NULL );
  2404. }
  2405. } else if (NonEmptyIndex) {
  2406. DebugTrace( 0, Dbg, ("Index attribute has entries\n") );
  2407. try_return( Status = STATUS_DIRECTORY_NOT_EMPTY );
  2408. } else {
  2409. DebugTrace( 0, Dbg, ("File is not deleteable\n") );
  2410. try_return( Status = STATUS_CANNOT_DELETE );
  2411. }
  2412. }
  2413. //
  2414. // Otherwise we are simply removing the attribute.
  2415. //
  2416. } else {
  2417. SetFlag( Scb->ScbState, SCB_STATE_DELETE_ON_CLOSE );
  2418. }
  2419. //
  2420. // Indicate in the file object that a delete is pending
  2421. //
  2422. FileObject->DeletePending = TRUE;
  2423. //
  2424. // Now do the audit.
  2425. //
  2426. if ((FileHandle != NULL) && GenerateOnClose) {
  2427. SeDeleteObjectAuditAlarm( FileObject, FileHandle );
  2428. }
  2429. } else {
  2430. if (FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE )) {
  2431. if (LcbLinkIsDeleted( Lcb )) {
  2432. //
  2433. // The user doesn't want to delete the link so clear any delete bits
  2434. // we have laying around
  2435. //
  2436. DebugTrace( 0, Dbg, ("File is being marked as do not delete on close\n") );
  2437. ClearFlag( Lcb->LcbState, LCB_STATE_DELETE_ON_CLOSE );
  2438. Scb->Fcb->LinkCount += 1;
  2439. ASSERTMSG( "Link count should not be 0\n", Scb->Fcb->LinkCount != 0 );
  2440. if (FlagOn( Lcb->FileNameAttr->Flags, FILE_NAME_DOS | FILE_NAME_NTFS )) {
  2441. ClearFlag( Scb->Fcb->FcbState, FCB_STATE_PRIMARY_LINK_DELETED );
  2442. }
  2443. }
  2444. //
  2445. // Otherwise we are undeleting an attribute.
  2446. //
  2447. } else {
  2448. ClearFlag( Scb->ScbState, SCB_STATE_DELETE_ON_CLOSE );
  2449. }
  2450. FileObject->DeletePending = FALSE;
  2451. }
  2452. Status = STATUS_SUCCESS;
  2453. try_exit: NOTHING;
  2454. } finally {
  2455. DebugUnwind( NtfsSetDispositionInfo );
  2456. NOTHING;
  2457. }
  2458. //
  2459. // And return to our caller
  2460. //
  2461. DebugTrace( -1, Dbg, ("NtfsSetDispositionInfo -> %08lx\n", Status) );
  2462. return Status;
  2463. }
  2464. //
  2465. // Internal Support Routine
  2466. //
  2467. NTSTATUS
  2468. NtfsSetRenameInfo (
  2469. IN PIRP_CONTEXT IrpContext,
  2470. IN PFILE_OBJECT FileObject,
  2471. IN PIRP Irp,
  2472. IN PVCB Vcb,
  2473. IN PSCB Scb,
  2474. IN PCCB Ccb,
  2475. IN OUT PBOOLEAN VcbAcquired
  2476. )
  2477. /*++
  2478. Routine Description:
  2479. This routine performs the set rename function.
  2480. Arguments:
  2481. FileObject - Supplies the file object being processed
  2482. Irp - Supplies the Irp being processed
  2483. Vcb - Supplies the Vcb for the Volume
  2484. Scb - Supplies the Scb for the file/directory being modified
  2485. Ccb - Supplies the Ccb for this file object
  2486. Return Value:
  2487. NTSTATUS - The status of the operation
  2488. --*/
  2489. {
  2490. NTSTATUS Status = STATUS_SUCCESS;
  2491. PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
  2492. PLCB Lcb = Ccb->Lcb;
  2493. PFCB Fcb = Scb->Fcb;
  2494. PSCB ParentScb;
  2495. USHORT FcbLinkCountAdj = 0;
  2496. BOOLEAN AcquiredParentScb = TRUE;
  2497. BOOLEAN AcquiredObjectIdIndex = FALSE;
  2498. BOOLEAN AcquiredReparsePointIndex = FALSE;
  2499. PFCB TargetLinkFcb = NULL;
  2500. BOOLEAN ExistingTargetLinkFcb;
  2501. BOOLEAN AcquiredTargetLinkFcb = FALSE;
  2502. USHORT TargetLinkFcbCountAdj = 0;
  2503. BOOLEAN AcquiredFcbTable = FALSE;
  2504. PFCB FcbWithPagingToRelease = NULL;
  2505. PFILE_OBJECT TargetFileObject;
  2506. PSCB TargetParentScb;
  2507. UNICODE_STRING NewLinkName;
  2508. UNICODE_STRING NewFullLinkName;
  2509. PWCHAR NewFullLinkNameBuffer = NULL;
  2510. UCHAR NewLinkNameFlags;
  2511. PFILE_NAME FileNameAttr = NULL;
  2512. USHORT FileNameAttrLength = 0;
  2513. UNICODE_STRING PrevLinkName;
  2514. UNICODE_STRING PrevFullLinkName;
  2515. UCHAR PrevLinkNameFlags;
  2516. UNICODE_STRING SourceFullLinkName;
  2517. USHORT SourceLinkLastNameOffset;
  2518. BOOLEAN FoundLink;
  2519. PINDEX_ENTRY IndexEntry;
  2520. PBCB IndexEntryBcb = NULL;
  2521. PWCHAR NextChar;
  2522. BOOLEAN ReportDirNotify = FALSE;
  2523. ULONG RenameFlags = ACTIVELY_REMOVE_SOURCE_LINK | REMOVE_SOURCE_LINK | ADD_TARGET_LINK;
  2524. PLIST_ENTRY Links;
  2525. PSCB ThisScb;
  2526. PFCB_USN_RECORD SavedFcbUsnRecord = NULL;
  2527. ULONG SavedUsnReason = 0;
  2528. NAME_PAIR NamePair;
  2529. NTFS_TUNNELED_DATA TunneledData;
  2530. ULONG TunneledDataSize;
  2531. BOOLEAN HaveTunneledInformation = FALSE;
  2532. PFCB LockedFcb = NULL;
  2533. ASSERT_IRP_CONTEXT( IrpContext );
  2534. ASSERT_FILE_OBJECT( FileObject );
  2535. ASSERT_IRP( Irp );
  2536. ASSERT_SCB( Scb );
  2537. PAGED_CODE ();
  2538. DebugTrace( +1, Dbg, ("NtfsSetRenameInfo...\n") );
  2539. //
  2540. // See if we are doing a stream rename. The allowed inputs are:
  2541. // No associated file object.
  2542. // Rename Name begins with a colon
  2543. // If so, perform the rename
  2544. //
  2545. TargetFileObject = IrpSp->Parameters.SetFile.FileObject;
  2546. if (TargetFileObject == NULL) {
  2547. PFILE_RENAME_INFORMATION FileRename;
  2548. FileRename = IrpContext->OriginatingIrp->AssociatedIrp.SystemBuffer;
  2549. if (FileRename->FileNameLength >= sizeof( WCHAR ) &&
  2550. FileRename->FileName[0] == L':') {
  2551. NewLinkName.Buffer = FileRename->FileName;
  2552. NewLinkName.MaximumLength =
  2553. NewLinkName.Length = (USHORT) FileRename->FileNameLength;
  2554. Status = NtfsStreamRename( IrpContext, FileObject, Fcb, Scb, Ccb, FileRename->ReplaceIfExists, &NewLinkName );
  2555. DebugTrace( -1, Dbg, ("NtfsSetRenameInfo: Exit -> %08lx\n", Status) );
  2556. return Status;
  2557. }
  2558. }
  2559. //
  2560. // Do a quick check that the caller is allowed to do the rename.
  2561. // The opener must have opened the main data stream by name and this can't be
  2562. // a system file.
  2563. //
  2564. if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE ) ||
  2565. (Lcb == NULL) ||
  2566. FlagOn(Fcb->FcbState, FCB_STATE_SYSTEM_FILE)) {
  2567. DebugTrace( -1, Dbg, ("NtfsSetRenameInfo: Exit -> %08lx\n", STATUS_INVALID_PARAMETER) );
  2568. return STATUS_INVALID_PARAMETER;
  2569. }
  2570. //
  2571. // If this link has been deleted, then we don't allow this operation.
  2572. //
  2573. if (LcbLinkIsDeleted( Lcb )) {
  2574. DebugTrace( -1, Dbg, ("NtfsSetRenameInfo: Exit -> %08lx\n", STATUS_ACCESS_DENIED) );
  2575. return STATUS_ACCESS_DENIED;
  2576. }
  2577. //
  2578. // Verify that we can wait.
  2579. //
  2580. if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT )) {
  2581. Status = NtfsPostRequest( IrpContext, Irp );
  2582. DebugTrace( -1, Dbg, ("NtfsSetRenameInfo: Can't wait\n") );
  2583. return Status;
  2584. }
  2585. //
  2586. // Remember the source info flags in the Ccb.
  2587. //
  2588. IrpContext->SourceInfo = Ccb->UsnSourceInfo;
  2589. //
  2590. // Use a try-finally to facilitate cleanup.
  2591. //
  2592. try {
  2593. //
  2594. // Initialize the local variables.
  2595. //
  2596. ParentScb = Lcb->Scb;
  2597. NtfsInitializeNamePair( &NamePair );
  2598. if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_FILE_ID ) &&
  2599. (Vcb->NotifyCount != 0)) {
  2600. ReportDirNotify = TRUE;
  2601. }
  2602. PrevFullLinkName.Buffer = NULL;
  2603. SourceFullLinkName.Buffer = NULL;
  2604. //
  2605. // If this is a directory file, we need to examine its descendents.
  2606. // We may not remove a link which may be an ancestor path
  2607. // component of any open file.
  2608. //
  2609. if (IsDirectory( &Fcb->Info )) {
  2610. Status = NtfsCheckTreeForBatchOplocks( IrpContext, Irp, Scb );
  2611. if (Status != STATUS_SUCCESS) { leave; }
  2612. }
  2613. //
  2614. // We now assemble the names and in memory-structures for both the
  2615. // source and target links and check if the target link currently
  2616. // exists.
  2617. //
  2618. NtfsFindTargetElements( IrpContext,
  2619. TargetFileObject,
  2620. ParentScb,
  2621. &TargetParentScb,
  2622. &NewFullLinkName,
  2623. &NewLinkName );
  2624. //
  2625. // Check that the new name is not invalid.
  2626. //
  2627. if ((NewLinkName.Length > (NTFS_MAX_FILE_NAME_LENGTH * sizeof( WCHAR ))) ||
  2628. !NtfsIsFileNameValid( &NewLinkName, FALSE )) {
  2629. Status = STATUS_OBJECT_NAME_INVALID;
  2630. leave;
  2631. }
  2632. //
  2633. // Acquire the current parent in order to synchronize removing the current name.
  2634. //
  2635. NtfsAcquireExclusiveScb( IrpContext, ParentScb );
  2636. //
  2637. // If this Scb does not have a normalized name then provide it with one now.
  2638. //
  2639. if (ParentScb->ScbType.Index.NormalizedName.Length == 0) {
  2640. NtfsBuildNormalizedName( IrpContext,
  2641. ParentScb->Fcb,
  2642. ParentScb,
  2643. &ParentScb->ScbType.Index.NormalizedName );
  2644. }
  2645. //
  2646. // If this is a directory then make sure it has a normalized name.
  2647. //
  2648. if (IsDirectory( &Fcb->Info ) &&
  2649. (Scb->ScbType.Index.NormalizedName.Length == 0)) {
  2650. NtfsUpdateNormalizedName( IrpContext,
  2651. ParentScb,
  2652. Scb,
  2653. NULL,
  2654. FALSE );
  2655. }
  2656. //
  2657. // Check if we are renaming to the same directory with the exact same name.
  2658. //
  2659. if (TargetParentScb == ParentScb) {
  2660. if (NtfsAreNamesEqual( Vcb->UpcaseTable, &NewLinkName, &Lcb->ExactCaseLink.LinkName, FALSE )) {
  2661. DebugTrace( 0, Dbg, ("Renaming to same name and directory\n") );
  2662. leave;
  2663. }
  2664. //
  2665. // Otherwise we want to acquire the target directory.
  2666. //
  2667. } else {
  2668. //
  2669. // We need to do the acquisition carefully since we may only have the Vcb shared.
  2670. //
  2671. if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX )) {
  2672. if (!NtfsAcquireExclusiveFcb( IrpContext,
  2673. TargetParentScb->Fcb,
  2674. TargetParentScb,
  2675. ACQUIRE_DONT_WAIT )) {
  2676. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX );
  2677. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  2678. }
  2679. //
  2680. // Now snapshot the Scb.
  2681. //
  2682. if (FlagOn( TargetParentScb->ScbState, SCB_STATE_FILE_SIZE_LOADED )) {
  2683. NtfsSnapshotScb( IrpContext, TargetParentScb );
  2684. }
  2685. } else {
  2686. NtfsAcquireExclusiveScb( IrpContext, TargetParentScb );
  2687. }
  2688. SetFlag( RenameFlags, MOVE_TO_NEW_DIR );
  2689. }
  2690. //
  2691. // We also determine which type of link to
  2692. // create. We create a hard link only unless the source link is
  2693. // a primary link and the user is an IgnoreCase guy.
  2694. //
  2695. if (FlagOn( Lcb->FileNameAttr->Flags, FILE_NAME_DOS | FILE_NAME_NTFS ) &&
  2696. FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE )) {
  2697. SetFlag( RenameFlags, ADD_PRIMARY_LINK );
  2698. }
  2699. //
  2700. // Lookup the entry for this filename in the target directory.
  2701. // We look in the Ccb for the type of case match for the target
  2702. // name.
  2703. //
  2704. FoundLink = NtfsLookupEntry( IrpContext,
  2705. TargetParentScb,
  2706. BooleanFlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE ),
  2707. &NewLinkName,
  2708. &FileNameAttr,
  2709. &FileNameAttrLength,
  2710. NULL,
  2711. &IndexEntry,
  2712. &IndexEntryBcb,
  2713. NULL );
  2714. //
  2715. // This call to NtfsLookupEntry may decide to push the root index,
  2716. // in which case we might be holding the Mft now. If there is a
  2717. // transaction, commit it now so we will be able to free the Mft to
  2718. // eliminate a potential deadlock with the ObjectId index when we
  2719. // look up the object id for the rename source to add it to the
  2720. // tunnel cache.
  2721. //
  2722. if (IrpContext->TransactionId != 0) {
  2723. NtfsCheckpointCurrentTransaction( IrpContext );
  2724. //
  2725. // Go through and free any Scb's in the queue of shared
  2726. // Scb's for transactions.
  2727. //
  2728. if (IrpContext->SharedScb != NULL) {
  2729. NtfsReleaseSharedResources( IrpContext );
  2730. ASSERT( IrpContext->SharedScb == NULL );
  2731. }
  2732. //
  2733. // Release the mft, if we acquired it in pushing the root index.
  2734. //
  2735. NtfsReleaseExclusiveScbIfOwned( IrpContext, Vcb->MftScb );
  2736. }
  2737. //
  2738. // If we found a matching link, we need to check how we want to operate
  2739. // on the source link and the target link. This means whether we
  2740. // have any work to do, whether we need to remove the target link
  2741. // and whether we need to remove the source link.
  2742. //
  2743. if (FoundLink) {
  2744. PFILE_NAME IndexFileName;
  2745. //
  2746. // Assume we will remove this link.
  2747. //
  2748. SetFlag( RenameFlags, REMOVE_TARGET_LINK );
  2749. IndexFileName = (PFILE_NAME) NtfsFoundIndexEntry( IndexEntry );
  2750. NtfsCheckLinkForRename( Fcb,
  2751. Lcb,
  2752. IndexFileName,
  2753. IndexEntry->FileReference,
  2754. &NewLinkName,
  2755. BooleanFlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE ),
  2756. &RenameFlags );
  2757. //
  2758. // Assume we will use the existing name flags on the link found. This
  2759. // will be the case where the file was opened with the 8.3 name and
  2760. // the new name is exactly the long name for the same file.
  2761. //
  2762. PrevLinkNameFlags =
  2763. NewLinkNameFlags = IndexFileName->Flags;
  2764. //
  2765. // If we didn't have an exact match, then we need to check if we
  2766. // can remove the found link and then remove it from the disk.
  2767. //
  2768. if (FlagOn( RenameFlags, REMOVE_TARGET_LINK )) {
  2769. //
  2770. // We need to check that the user wanted to remove that link.
  2771. //
  2772. if (!FlagOn( RenameFlags, TRAVERSE_MATCH ) &&
  2773. !IrpSp->Parameters.SetFile.ReplaceIfExists) {
  2774. Status = STATUS_OBJECT_NAME_COLLISION;
  2775. leave;
  2776. }
  2777. //
  2778. // We want to preserve the case and the flags of the matching
  2779. // link found. We also want to preserve the case of the
  2780. // name being created. The following variables currently contain
  2781. // the exact case for the target to remove and the new name to
  2782. // apply.
  2783. //
  2784. // Link to remove - In 'IndexEntry'.
  2785. // The link's flags are also in 'IndexEntry'. We copy
  2786. // these flags to 'PrevLinkNameFlags'
  2787. //
  2788. // New Name - Exact case is stored in 'NewLinkName'
  2789. // - It is also in 'FileNameAttr
  2790. //
  2791. // We modify this so that we can use the FileName attribute
  2792. // structure to create the new link. We copy the linkname being
  2793. // removed into 'PrevLinkName'. The following is the
  2794. // state after the switch.
  2795. //
  2796. // 'FileNameAttr' - contains the name for the link being
  2797. // created.
  2798. //
  2799. // 'PrevLinkFileName' - Contains the link name for the link being
  2800. // removed.
  2801. //
  2802. // 'PrevLinkFileNameFlags' - Contains the name flags for the link
  2803. // being removed.
  2804. //
  2805. //
  2806. // Allocate a buffer for the name being removed. It should be
  2807. // large enough for the entire directory name.
  2808. //
  2809. PrevFullLinkName.MaximumLength = TargetParentScb->ScbType.Index.NormalizedName.Length +
  2810. sizeof( WCHAR ) +
  2811. (IndexFileName->FileNameLength * sizeof( WCHAR ));
  2812. PrevFullLinkName.Buffer = NtfsAllocatePool( PagedPool,
  2813. PrevFullLinkName.MaximumLength );
  2814. RtlCopyMemory( PrevFullLinkName.Buffer,
  2815. TargetParentScb->ScbType.Index.NormalizedName.Buffer,
  2816. TargetParentScb->ScbType.Index.NormalizedName.Length );
  2817. NextChar = Add2Ptr( PrevFullLinkName.Buffer,
  2818. TargetParentScb->ScbType.Index.NormalizedName.Length );
  2819. if (TargetParentScb != Vcb->RootIndexScb) {
  2820. *NextChar = L'\\';
  2821. NextChar += 1;
  2822. }
  2823. RtlCopyMemory( NextChar,
  2824. IndexFileName->FileName,
  2825. IndexFileName->FileNameLength * sizeof( WCHAR ));
  2826. //
  2827. // Copy the name found in the Index Entry to 'PrevLinkName'
  2828. //
  2829. PrevLinkName.Buffer = NextChar;
  2830. PrevLinkName.MaximumLength =
  2831. PrevLinkName.Length = IndexFileName->FileNameLength * sizeof( WCHAR );
  2832. //
  2833. // Update the full name length with the final component.
  2834. //
  2835. PrevFullLinkName.Length = (USHORT) PtrOffset( PrevFullLinkName.Buffer, NextChar ) + PrevLinkName.Length;
  2836. //
  2837. // We only need this check if the link is for a different file.
  2838. //
  2839. if (!FlagOn( RenameFlags, TRAVERSE_MATCH )) {
  2840. //
  2841. // We check if there is an existing Fcb for the target link.
  2842. // If there is, the unclean count better be 0.
  2843. //
  2844. NtfsAcquireFcbTable( IrpContext, Vcb );
  2845. AcquiredFcbTable = TRUE;
  2846. TargetLinkFcb = NtfsCreateFcb( IrpContext,
  2847. Vcb,
  2848. IndexEntry->FileReference,
  2849. FALSE,
  2850. BooleanFlagOn( Fcb->FcbState, FCB_STATE_COMPOUND_INDEX ),
  2851. &ExistingTargetLinkFcb );
  2852. //
  2853. // Before we go on, make sure we aren't about to rename over a system file.
  2854. //
  2855. if (FlagOn( TargetLinkFcb->FcbState, FCB_STATE_SYSTEM_FILE )) {
  2856. Status = STATUS_ACCESS_DENIED;
  2857. leave;
  2858. }
  2859. //
  2860. // Add a paging resource to the target - this is not supplied if its created
  2861. // from scratch. We need this (acquired in the proper order) for the delete
  2862. // to work correctly if there are any data streams. It's not going to harm a
  2863. // directory del and because of the teardown in the finally clause its difficult
  2864. // to retry again without looping.
  2865. //
  2866. NtfsLockFcb( IrpContext, TargetLinkFcb );
  2867. LockedFcb = TargetLinkFcb;
  2868. if (TargetLinkFcb->PagingIoResource == NULL) {
  2869. TargetLinkFcb->PagingIoResource = NtfsAllocateEresource();
  2870. }
  2871. NtfsUnlockFcb( IrpContext, LockedFcb );
  2872. LockedFcb = NULL;
  2873. //
  2874. // We need to acquire this file carefully in the event that we don't hold
  2875. // the Vcb exclusively.
  2876. //
  2877. if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX )) {
  2878. if (!ExAcquireResourceExclusiveLite( TargetLinkFcb->PagingIoResource, FALSE )) {
  2879. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX );
  2880. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  2881. }
  2882. FcbWithPagingToRelease = TargetLinkFcb;
  2883. if (!NtfsAcquireExclusiveFcb( IrpContext, TargetLinkFcb, NULL, ACQUIRE_DONT_WAIT )) {
  2884. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX );
  2885. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  2886. }
  2887. NtfsReleaseFcbTable( IrpContext, Vcb );
  2888. AcquiredFcbTable = FALSE;
  2889. } else {
  2890. NtfsReleaseFcbTable( IrpContext, Vcb );
  2891. AcquiredFcbTable = FALSE;
  2892. //
  2893. // Acquire the paging Io resource for this file before the main
  2894. // resource in case we need to delete.
  2895. //
  2896. FcbWithPagingToRelease = TargetLinkFcb;
  2897. ExAcquireResourceExclusiveLite( FcbWithPagingToRelease->PagingIoResource, TRUE );
  2898. NtfsAcquireExclusiveFcb( IrpContext, TargetLinkFcb, NULL, 0 );
  2899. }
  2900. AcquiredTargetLinkFcb = TRUE;
  2901. //
  2902. // If the Fcb Info field needs to be initialized, we do so now.
  2903. // We read this information from the disk as the duplicate information
  2904. // in the index entry is not guaranteed to be correct.
  2905. //
  2906. if (!FlagOn( TargetLinkFcb->FcbState, FCB_STATE_DUP_INITIALIZED )) {
  2907. NtfsUpdateFcbInfoFromDisk( IrpContext,
  2908. TRUE,
  2909. TargetLinkFcb,
  2910. NULL );
  2911. NtfsConditionallyFixupQuota( IrpContext, TargetLinkFcb );
  2912. if (IrpContext->TransactionId != 0) {
  2913. NtfsCheckpointCurrentTransaction( IrpContext );
  2914. ASSERTMSG( "Ntfs: we should not own the mftscb\n", !NtfsIsSharedScb( Vcb->MftScb ) );
  2915. }
  2916. }
  2917. //
  2918. // We are adding a link to the source file which already
  2919. // exists as a link to a different file in the target directory.
  2920. //
  2921. // We need to check whether we permitted to delete this
  2922. // link. If not then it is possible that the problem is
  2923. // an existing batch oplock on the file. In that case
  2924. // we want to delete the batch oplock and try this again.
  2925. //
  2926. Status = NtfsCheckFileForDelete( IrpContext,
  2927. TargetParentScb,
  2928. TargetLinkFcb,
  2929. ExistingTargetLinkFcb,
  2930. IndexEntry );
  2931. if (!NT_SUCCESS( Status )) {
  2932. PSCB NextScb = NULL;
  2933. //
  2934. // We are going to either fail this request or pass
  2935. // this on to the oplock package. Test if there is
  2936. // a batch oplock on any streams on this file.
  2937. //
  2938. while ((NextScb = NtfsGetNextChildScb( TargetLinkFcb,
  2939. NextScb )) != NULL) {
  2940. if ((NextScb->AttributeTypeCode == $DATA) &&
  2941. (NextScb->Header.NodeTypeCode == NTFS_NTC_SCB_DATA) &&
  2942. FsRtlCurrentBatchOplock( &NextScb->ScbType.Data.Oplock )) {
  2943. if (*VcbAcquired) {
  2944. NtfsReleaseVcb( IrpContext, Vcb );
  2945. *VcbAcquired = FALSE;
  2946. }
  2947. Status = FsRtlCheckOplock( &NextScb->ScbType.Data.Oplock,
  2948. Irp,
  2949. IrpContext,
  2950. NtfsOplockComplete,
  2951. NtfsPrePostIrp );
  2952. break;
  2953. }
  2954. }
  2955. leave;
  2956. }
  2957. NtfsCleanupLinkForRemoval( TargetLinkFcb, TargetParentScb, ExistingTargetLinkFcb );
  2958. //
  2959. // DeleteFile might need to get the reparse index to remove a reparse
  2960. // point. We may need the object id index later to deal with the
  2961. // tunnel cache. Let's acquire them in the right order now.
  2962. //
  2963. if (HasReparsePoint( &TargetLinkFcb->Info ) &&
  2964. (Vcb->ReparsePointTableScb != NULL)) {
  2965. NtfsAcquireExclusiveScb( IrpContext, Vcb->ReparsePointTableScb );
  2966. AcquiredReparsePointIndex = TRUE;
  2967. }
  2968. if (Vcb->ObjectIdTableScb != NULL) {
  2969. NtfsAcquireExclusiveScb( IrpContext, Vcb->ObjectIdTableScb );
  2970. AcquiredObjectIdIndex = TRUE;
  2971. }
  2972. if (TargetLinkFcb->LinkCount == 1) {
  2973. PFCB TempFcb;
  2974. //
  2975. // Fixup the IrpContext CleanupStructure so deletefile logs correctly
  2976. //
  2977. TempFcb = (PFCB) IrpContext->CleanupStructure;
  2978. IrpContext->CleanupStructure = FcbWithPagingToRelease;
  2979. ASSERT( (NULL == TempFcb) || (NTFS_NTC_FCB == SafeNodeType( TempFcb )) );
  2980. FcbWithPagingToRelease = TempFcb;
  2981. NtfsDeleteFile( IrpContext,
  2982. TargetLinkFcb,
  2983. TargetParentScb,
  2984. &AcquiredParentScb,
  2985. NULL,
  2986. NULL );
  2987. FcbWithPagingToRelease = IrpContext->CleanupStructure;
  2988. IrpContext->CleanupStructure = TempFcb;
  2989. //
  2990. // Make sure to force the close record out to disk.
  2991. //
  2992. TargetLinkFcbCountAdj += 1;
  2993. } else {
  2994. NtfsPostUsnChange( IrpContext, TargetLinkFcb, USN_REASON_HARD_LINK_CHANGE | USN_REASON_CLOSE );
  2995. NtfsRemoveLink( IrpContext,
  2996. TargetLinkFcb,
  2997. TargetParentScb,
  2998. PrevLinkName,
  2999. NULL,
  3000. NULL );
  3001. ClearFlag( TargetLinkFcb->FcbState, FCB_STATE_VALID_USN_NAME );
  3002. TargetLinkFcbCountAdj += 1;
  3003. NtfsUpdateFcb( TargetLinkFcb, FCB_INFO_CHANGED_LAST_CHANGE );
  3004. }
  3005. //
  3006. // The target link is for the same file as the source link. No security
  3007. // checks need to be done. Go ahead and remove it.
  3008. //
  3009. } else {
  3010. NtfsPostUsnChange( IrpContext, Scb, USN_REASON_RENAME_OLD_NAME );
  3011. TargetLinkFcb = Fcb;
  3012. NtfsRemoveLink( IrpContext,
  3013. Fcb,
  3014. TargetParentScb,
  3015. PrevLinkName,
  3016. NULL,
  3017. NULL );
  3018. FcbLinkCountAdj += 1;
  3019. }
  3020. }
  3021. }
  3022. NtfsUnpinBcb( IrpContext, &IndexEntryBcb );
  3023. //
  3024. // Post the Usn record for the old name. Don't write it until after
  3025. // we check if we need to remove an object ID due to tunnelling.
  3026. // Otherwise we might deadlock between the journal/mft resources
  3027. // and the object id resources.
  3028. //
  3029. NtfsPostUsnChange( IrpContext, Scb, USN_REASON_RENAME_OLD_NAME );
  3030. //
  3031. // See if we need to remove the current link.
  3032. //
  3033. if (FlagOn( RenameFlags, REMOVE_SOURCE_LINK )) {
  3034. //
  3035. // Now we want to remove the source link from the file. We need to
  3036. // remember if we deleted a two part primary link.
  3037. //
  3038. if (FlagOn( RenameFlags, ACTIVELY_REMOVE_SOURCE_LINK )) {
  3039. TunneledData.HasObjectId = FALSE;
  3040. NtfsRemoveLink( IrpContext,
  3041. Fcb,
  3042. ParentScb,
  3043. Lcb->ExactCaseLink.LinkName,
  3044. &NamePair,
  3045. &TunneledData );
  3046. //
  3047. // Remember the full name for the original filename and some
  3048. // other information to pass to the dirnotify package.
  3049. //
  3050. if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_FILE_ID )) {
  3051. if (!IsDirectory( &Fcb->Info ) &&
  3052. !FlagOn( FileObject->Flags, FO_OPENED_CASE_SENSITIVE )) {
  3053. //
  3054. // Tunnel property information for file links
  3055. //
  3056. NtfsGetTunneledData( IrpContext,
  3057. Fcb,
  3058. &TunneledData );
  3059. FsRtlAddToTunnelCache( &Vcb->Tunnel,
  3060. *(PULONGLONG)&ParentScb->Fcb->FileReference,
  3061. &NamePair.Short,
  3062. &NamePair.Long,
  3063. BooleanFlagOn( Lcb->FileNameAttr->Flags, FILE_NAME_DOS ),
  3064. sizeof( NTFS_TUNNELED_DATA ),
  3065. &TunneledData );
  3066. }
  3067. }
  3068. FcbLinkCountAdj += 1;
  3069. }
  3070. if (ReportDirNotify) {
  3071. SourceFullLinkName.Buffer = NtfsAllocatePool( PagedPool, Ccb->FullFileName.Length );
  3072. RtlCopyMemory( SourceFullLinkName.Buffer,
  3073. Ccb->FullFileName.Buffer,
  3074. Ccb->FullFileName.Length );
  3075. SourceFullLinkName.MaximumLength = SourceFullLinkName.Length = Ccb->FullFileName.Length;
  3076. SourceLinkLastNameOffset = Ccb->LastFileNameOffset;
  3077. }
  3078. }
  3079. //
  3080. // See if we need to add the target link.
  3081. //
  3082. if (FlagOn( RenameFlags, ADD_TARGET_LINK )) {
  3083. //
  3084. // Check that we have permission to add a file to this directory.
  3085. //
  3086. NtfsCheckIndexForAddOrDelete( IrpContext,
  3087. TargetParentScb->Fcb,
  3088. (IsDirectory( &Fcb->Info ) ?
  3089. FILE_ADD_SUBDIRECTORY :
  3090. FILE_ADD_FILE),
  3091. Ccb->AccessFlags >> 2 );
  3092. //
  3093. // Grunge the tunnel cache for property restoration
  3094. //
  3095. if (!IsDirectory( &Fcb->Info ) &&
  3096. !FlagOn( FileObject->Flags, FO_OPENED_CASE_SENSITIVE )) {
  3097. NtfsResetNamePair( &NamePair );
  3098. TunneledDataSize = sizeof( NTFS_TUNNELED_DATA );
  3099. if (FsRtlFindInTunnelCache( &Vcb->Tunnel,
  3100. *(PULONGLONG)&TargetParentScb->Fcb->FileReference,
  3101. &NewLinkName,
  3102. &NamePair.Short,
  3103. &NamePair.Long,
  3104. &TunneledDataSize,
  3105. &TunneledData)) {
  3106. ASSERT( TunneledDataSize == sizeof( NTFS_TUNNELED_DATA ));
  3107. HaveTunneledInformation = TRUE;
  3108. }
  3109. }
  3110. //
  3111. // We now want to add the new link into the target directory.
  3112. // We create a hard link only if the source name was a hard link
  3113. // or this is a case-sensitive open. This means that we can
  3114. // replace a primary link pair with a hard link only.
  3115. //
  3116. NtfsAddLink( IrpContext,
  3117. BooleanFlagOn( RenameFlags, ADD_PRIMARY_LINK ),
  3118. TargetParentScb,
  3119. Fcb,
  3120. FileNameAttr,
  3121. NULL,
  3122. &NewLinkNameFlags,
  3123. NULL,
  3124. HaveTunneledInformation ? &NamePair : NULL,
  3125. NULL );
  3126. //
  3127. // Restore timestamps on tunneled files
  3128. //
  3129. if (HaveTunneledInformation) {
  3130. NtfsSetTunneledData( IrpContext,
  3131. Fcb,
  3132. &TunneledData );
  3133. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_CREATE );
  3134. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  3135. //
  3136. // If we have tunneled information then copy the correct case of the
  3137. // name into the new link pointer.
  3138. //
  3139. if (NewLinkNameFlags == FILE_NAME_DOS) {
  3140. RtlCopyMemory( NewLinkName.Buffer,
  3141. NamePair.Short.Buffer,
  3142. NewLinkName.Length );
  3143. }
  3144. }
  3145. //
  3146. // Update the flags field in the target file name. We will use this
  3147. // below if we are updating the normalized name.
  3148. //
  3149. FileNameAttr->Flags = NewLinkNameFlags;
  3150. if (ParentScb != TargetParentScb) {
  3151. NtfsUpdateFcb( TargetParentScb->Fcb,
  3152. (FCB_INFO_CHANGED_LAST_CHANGE |
  3153. FCB_INFO_CHANGED_LAST_MOD |
  3154. FCB_INFO_UPDATE_LAST_ACCESS) );
  3155. }
  3156. //
  3157. // If we need a full buffer for the new name for notify and don't already
  3158. // have one then construct the full name now. This will only happen if
  3159. // we are renaming within the same directory.
  3160. //
  3161. if (ReportDirNotify &&
  3162. (NewFullLinkName.Buffer == NULL)) {
  3163. NewFullLinkName.MaximumLength = Ccb->LastFileNameOffset + NewLinkName.Length;
  3164. NewFullLinkNameBuffer = NtfsAllocatePool( PagedPool,
  3165. NewFullLinkName.MaximumLength );
  3166. RtlCopyMemory( NewFullLinkNameBuffer,
  3167. Ccb->FullFileName.Buffer,
  3168. Ccb->LastFileNameOffset );
  3169. RtlCopyMemory( Add2Ptr( NewFullLinkNameBuffer, Ccb->LastFileNameOffset ),
  3170. NewLinkName.Buffer,
  3171. NewLinkName.Length );
  3172. NewFullLinkName.Buffer = NewFullLinkNameBuffer;
  3173. NewFullLinkName.Length = NewFullLinkName.MaximumLength;
  3174. }
  3175. FcbLinkCountAdj -= 1;
  3176. }
  3177. //
  3178. // Now write the Usn record for the old name if it exists. Since this call
  3179. // needs to acquire the usn journal and/or mft, we need to do this after the
  3180. // NtfsSetTunneledData call, since that may acquire the object id index.
  3181. //
  3182. if (IrpContext->Usn.CurrentUsnFcb != NULL) {
  3183. NtfsWriteUsnJournalChanges( IrpContext );
  3184. }
  3185. //
  3186. // We need to update the names in the Lcb for this file as well as any subdirectories
  3187. // or files. We will do this in two passes. The first pass is just to reserve enough
  3188. // space in all of the file objects and Lcb's. We update the names in the second pass.
  3189. //
  3190. if (FlagOn( RenameFlags, TRAVERSE_MATCH )) {
  3191. if (FlagOn( RenameFlags, REMOVE_TARGET_LINK )) {
  3192. SetFlag( RenameFlags, REMOVE_TRAVERSE_LINK );
  3193. } else {
  3194. SetFlag( RenameFlags, REUSE_TRAVERSE_LINK );
  3195. }
  3196. }
  3197. //
  3198. // If this is a directory and we added a target link it means that the
  3199. // normalized name has changed. Make sure the buffer in the Scb will hold
  3200. // the larger name.
  3201. //
  3202. if (IsDirectory( &Fcb->Info ) && FlagOn( RenameFlags, ADD_TARGET_LINK )) {
  3203. NtfsUpdateNormalizedName( IrpContext,
  3204. TargetParentScb,
  3205. Scb,
  3206. FileNameAttr,
  3207. TRUE );
  3208. }
  3209. //
  3210. // Now post a rename change on the Fcb. We delete the old Usn record first,
  3211. // since it has the wrong name. No need to get the mutex since we have the
  3212. // file exclusive.
  3213. //
  3214. if (Fcb->FcbUsnRecord != NULL) {
  3215. SavedFcbUsnRecord = Fcb->FcbUsnRecord;
  3216. SavedUsnReason = SavedFcbUsnRecord->UsnRecord.Reason;
  3217. if (SavedFcbUsnRecord->ModifiedOpenFilesLinks.Flink != NULL) {
  3218. NtfsLockFcb( IrpContext, Vcb->UsnJournal->Fcb );
  3219. RemoveEntryList( &SavedFcbUsnRecord->ModifiedOpenFilesLinks );
  3220. if (SavedFcbUsnRecord->TimeOutLinks.Flink != NULL) {
  3221. RemoveEntryList( &SavedFcbUsnRecord->TimeOutLinks );
  3222. SavedFcbUsnRecord->ModifiedOpenFilesLinks.Flink = NULL;
  3223. }
  3224. NtfsUnlockFcb( IrpContext, Vcb->UsnJournal->Fcb );
  3225. }
  3226. Fcb->FcbUsnRecord = NULL;
  3227. //
  3228. // Note - Fcb is unlocked immediately below in the finally clause.
  3229. //
  3230. }
  3231. //
  3232. // Post the rename to the Usn Journal. We wait until the end, in order to
  3233. // reduce resource contention on the UsnJournal, in the event that we already
  3234. // posted a change when we deleted the target file.
  3235. //
  3236. NtfsPostUsnChange( IrpContext,
  3237. Scb,
  3238. (SavedUsnReason & ~USN_REASON_RENAME_OLD_NAME) | USN_REASON_RENAME_NEW_NAME );
  3239. //
  3240. // Now, if anything at all is posted to the Usn Journal, we must write it now
  3241. // so that we do not get a log file full later. But do not checkpoint until
  3242. // any failure cases are behind us.
  3243. //
  3244. if (IrpContext->Usn.CurrentUsnFcb != NULL) {
  3245. NtfsWriteUsnJournalChanges( IrpContext );
  3246. }
  3247. //
  3248. // We have now modified the on-disk structures. We now need to
  3249. // modify the in-memory structures. This includes the Fcb and Lcb's
  3250. // for any links we superseded, and the source Fcb and it's Lcb's.
  3251. //
  3252. // We will do this in two passes. The first pass will guarantee that all of the
  3253. // name buffers will be large enough for the names. The second pass will store the
  3254. // names into the buffers.
  3255. //
  3256. if (FlagOn( RenameFlags, MOVE_TO_NEW_DIR )) {
  3257. NtfsMoveLinkToNewDir( IrpContext,
  3258. &NewFullLinkName,
  3259. &NewLinkName,
  3260. NewLinkNameFlags,
  3261. TargetParentScb,
  3262. Fcb,
  3263. Lcb,
  3264. RenameFlags,
  3265. &PrevLinkName,
  3266. PrevLinkNameFlags );
  3267. //
  3268. // Otherwise we will rename in the current directory. We need to remember
  3269. // if we have merged with an existing link on this file.
  3270. //
  3271. } else {
  3272. NtfsRenameLinkInDir( IrpContext,
  3273. ParentScb,
  3274. Fcb,
  3275. Lcb,
  3276. &NewLinkName,
  3277. NewLinkNameFlags,
  3278. RenameFlags,
  3279. &PrevLinkName,
  3280. PrevLinkNameFlags );
  3281. }
  3282. //
  3283. // Now, checkpoint the transaction to free resources if we are holding on
  3284. // to the Usn Journal. No more failures can occur.
  3285. //
  3286. if (IrpContext->Usn.CurrentUsnFcb != NULL) {
  3287. NtfsCheckpointCurrentTransaction( IrpContext );
  3288. }
  3289. //
  3290. // Nothing should fail from this point forward.
  3291. //
  3292. // Now make the change to the normalized name. The buffer should be
  3293. // large enough.
  3294. //
  3295. if (IsDirectory( &Fcb->Info ) && FlagOn( RenameFlags, ADD_TARGET_LINK )) {
  3296. NtfsUpdateNormalizedName( IrpContext,
  3297. TargetParentScb,
  3298. Scb,
  3299. FileNameAttr,
  3300. FALSE );
  3301. }
  3302. //
  3303. // Now look at the link we superseded. If we deleted the file then go through and
  3304. // mark everything as deleted.
  3305. //
  3306. if (FlagOn( RenameFlags, REMOVE_TARGET_LINK | TRAVERSE_MATCH ) == REMOVE_TARGET_LINK) {
  3307. NtfsUpdateFcbFromLinkRemoval( IrpContext,
  3308. TargetParentScb,
  3309. TargetLinkFcb,
  3310. PrevLinkName,
  3311. PrevLinkNameFlags );
  3312. //
  3313. // If the link count is going to 0, we need to perform the work of
  3314. // removing the file.
  3315. //
  3316. if (TargetLinkFcb->LinkCount == 1) {
  3317. SetFlag( TargetLinkFcb->FcbState, FCB_STATE_FILE_DELETED );
  3318. //
  3319. // We need to mark all of the Scbs as gone.
  3320. //
  3321. for (Links = TargetLinkFcb->ScbQueue.Flink;
  3322. Links != &TargetLinkFcb->ScbQueue;
  3323. Links = Links->Flink) {
  3324. ThisScb = CONTAINING_RECORD( Links,
  3325. SCB,
  3326. FcbLinks );
  3327. SetFlag( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
  3328. }
  3329. }
  3330. }
  3331. //
  3332. // Change the time stamps in the parent if we modified the links in this directory.
  3333. //
  3334. if (FlagOn( RenameFlags, REMOVE_SOURCE_LINK )) {
  3335. NtfsUpdateFcb( ParentScb->Fcb,
  3336. (FCB_INFO_CHANGED_LAST_CHANGE |
  3337. FCB_INFO_CHANGED_LAST_MOD |
  3338. FCB_INFO_UPDATE_LAST_ACCESS) );
  3339. }
  3340. //
  3341. // We always set the last change time on the file we renamed unless
  3342. // the caller explicitly set this.
  3343. //
  3344. SetFlag( Ccb->Flags, CCB_FLAG_UPDATE_LAST_CHANGE );
  3345. //
  3346. // Don't set the archive bit on a directory. Otherwise we break existing
  3347. // apps that don't expect to see this flag.
  3348. //
  3349. if (!IsDirectory( &Fcb->Info )) {
  3350. SetFlag( Ccb->Flags, CCB_FLAG_SET_ARCHIVE );
  3351. }
  3352. //
  3353. // Report the changes to the affected directories. We defer reporting
  3354. // until now so that all of the on disk changes have been made.
  3355. // We have already preserved the original file name for any changes
  3356. // associated with it.
  3357. //
  3358. // Note that we may have to make a call to notify that we are removing
  3359. // a target if there is only a case change. This could make for
  3360. // a third notify call.
  3361. //
  3362. // Now that we have the new name we need to decide whether to report
  3363. // this as a change in the file or adding a file to a new directory.
  3364. //
  3365. if (ReportDirNotify) {
  3366. ULONG FilterMatch = 0;
  3367. ULONG Action;
  3368. //
  3369. // If we are deleting a target link in order to make a case change then
  3370. // report that.
  3371. //
  3372. if ((PrevFullLinkName.Buffer != NULL) &&
  3373. FlagOn( RenameFlags,
  3374. OVERWRITE_SOURCE_LINK | REMOVE_TARGET_LINK | EXACT_CASE_MATCH ) == REMOVE_TARGET_LINK) {
  3375. NtfsReportDirNotify( IrpContext,
  3376. Vcb,
  3377. &PrevFullLinkName,
  3378. PrevFullLinkName.Length - PrevLinkName.Length,
  3379. NULL,
  3380. ((TargetParentScb->ScbType.Index.NormalizedName.Length != 0) ?
  3381. &TargetParentScb->ScbType.Index.NormalizedName :
  3382. NULL),
  3383. (IsDirectory( &TargetLinkFcb->Info ) ?
  3384. FILE_NOTIFY_CHANGE_DIR_NAME :
  3385. FILE_NOTIFY_CHANGE_FILE_NAME),
  3386. FILE_ACTION_REMOVED,
  3387. TargetParentScb->Fcb );
  3388. }
  3389. //
  3390. // If we stored the original name then we report the changes
  3391. // associated with it.
  3392. //
  3393. if (FlagOn( RenameFlags, REMOVE_SOURCE_LINK )) {
  3394. NtfsReportDirNotify( IrpContext,
  3395. Vcb,
  3396. &SourceFullLinkName,
  3397. SourceLinkLastNameOffset,
  3398. NULL,
  3399. ((ParentScb->ScbType.Index.NormalizedName.Length != 0) ?
  3400. &ParentScb->ScbType.Index.NormalizedName :
  3401. NULL),
  3402. (IsDirectory( &Fcb->Info ) ?
  3403. FILE_NOTIFY_CHANGE_DIR_NAME :
  3404. FILE_NOTIFY_CHANGE_FILE_NAME),
  3405. ((FlagOn( RenameFlags, MOVE_TO_NEW_DIR ) ||
  3406. !FlagOn( RenameFlags, ADD_TARGET_LINK ) ||
  3407. (FlagOn( RenameFlags, REMOVE_TARGET_LINK | EXACT_CASE_MATCH ) == (REMOVE_TARGET_LINK | EXACT_CASE_MATCH))) ?
  3408. FILE_ACTION_REMOVED :
  3409. FILE_ACTION_RENAMED_OLD_NAME),
  3410. ParentScb->Fcb );
  3411. }
  3412. //
  3413. // Check if a new name will appear in the directory.
  3414. //
  3415. if (!FoundLink ||
  3416. (FlagOn( RenameFlags, OVERWRITE_SOURCE_LINK | EXACT_CASE_MATCH) == OVERWRITE_SOURCE_LINK) ||
  3417. (FlagOn( RenameFlags, REMOVE_TARGET_LINK | EXACT_CASE_MATCH ) == REMOVE_TARGET_LINK)) {
  3418. FilterMatch = IsDirectory( &Fcb->Info)
  3419. ? FILE_NOTIFY_CHANGE_DIR_NAME
  3420. : FILE_NOTIFY_CHANGE_FILE_NAME;
  3421. //
  3422. // If we moved to a new directory, remember the
  3423. // action was a create operation.
  3424. //
  3425. if (FlagOn( RenameFlags, MOVE_TO_NEW_DIR )) {
  3426. Action = FILE_ACTION_ADDED;
  3427. } else {
  3428. Action = FILE_ACTION_RENAMED_NEW_NAME;
  3429. }
  3430. //
  3431. // There was an entry with the same case. If this isn't the
  3432. // same file then we report a change to all the file attributes.
  3433. //
  3434. } else if (FlagOn( RenameFlags, REMOVE_TARGET_LINK | TRAVERSE_MATCH ) == REMOVE_TARGET_LINK) {
  3435. FilterMatch = (FILE_NOTIFY_CHANGE_ATTRIBUTES |
  3436. FILE_NOTIFY_CHANGE_SIZE |
  3437. FILE_NOTIFY_CHANGE_LAST_WRITE |
  3438. FILE_NOTIFY_CHANGE_LAST_ACCESS |
  3439. FILE_NOTIFY_CHANGE_CREATION |
  3440. FILE_NOTIFY_CHANGE_SECURITY |
  3441. FILE_NOTIFY_CHANGE_EA);
  3442. //
  3443. // The file name isn't changing, only the properties of the
  3444. // file.
  3445. //
  3446. Action = FILE_ACTION_MODIFIED;
  3447. }
  3448. if (FilterMatch != 0) {
  3449. NtfsReportDirNotify( IrpContext,
  3450. Vcb,
  3451. &NewFullLinkName,
  3452. NewFullLinkName.Length - NewLinkName.Length,
  3453. NULL,
  3454. ((TargetParentScb->ScbType.Index.NormalizedName.Length != 0) ?
  3455. &TargetParentScb->ScbType.Index.NormalizedName :
  3456. NULL),
  3457. FilterMatch,
  3458. Action,
  3459. TargetParentScb->Fcb );
  3460. }
  3461. }
  3462. //
  3463. // Now adjust the link counts on the different files.
  3464. //
  3465. if (TargetLinkFcb != NULL) {
  3466. TargetLinkFcb->LinkCount -= TargetLinkFcbCountAdj;
  3467. TargetLinkFcb->TotalLinks -= TargetLinkFcbCountAdj;
  3468. //
  3469. // Now go through and mark everything as deleted.
  3470. //
  3471. if (TargetLinkFcb->LinkCount == 0) {
  3472. SetFlag( TargetLinkFcb->FcbState, FCB_STATE_FILE_DELETED );
  3473. //
  3474. // We need to mark all of the Scbs as gone.
  3475. //
  3476. for (Links = TargetLinkFcb->ScbQueue.Flink;
  3477. Links != &TargetLinkFcb->ScbQueue;
  3478. Links = Links->Flink) {
  3479. ThisScb = CONTAINING_RECORD( Links, SCB, FcbLinks );
  3480. if (!FlagOn( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )) {
  3481. NtfsSnapshotScb( IrpContext, ThisScb );
  3482. ThisScb->ValidDataToDisk =
  3483. ThisScb->Header.AllocationSize.QuadPart =
  3484. ThisScb->Header.FileSize.QuadPart =
  3485. ThisScb->Header.ValidDataLength.QuadPart = 0;
  3486. SetFlag( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
  3487. }
  3488. }
  3489. }
  3490. }
  3491. Fcb->TotalLinks -= FcbLinkCountAdj;
  3492. Fcb->LinkCount -= FcbLinkCountAdj;
  3493. } finally {
  3494. DebugUnwind( NtfsSetRenameInfo );
  3495. if (LockedFcb != NULL) {
  3496. NtfsUnlockFcb( IrpContext, LockedFcb );
  3497. }
  3498. //
  3499. // See if we have a SavedFcbUsnRecord.
  3500. //
  3501. if (SavedFcbUsnRecord != NULL) {
  3502. //
  3503. // Conceivably we failed to reallcoate the record when we tried to post
  3504. // the rename. If so, we will simply restore it here. (Note the rename
  3505. // back to the old name will occur anyway.)
  3506. //
  3507. if (Fcb->FcbUsnRecord == NULL) {
  3508. Fcb->FcbUsnRecord = SavedFcbUsnRecord;
  3509. //
  3510. // Else just free the pool.
  3511. //
  3512. } else {
  3513. NtfsFreePool( SavedFcbUsnRecord );
  3514. }
  3515. }
  3516. //
  3517. // release objectid and reparse explicitly so we can call Teardown structures and wait to go up chain
  3518. //
  3519. if (AcquiredObjectIdIndex) { NtfsReleaseScb( IrpContext, Vcb->ObjectIdTableScb ); }
  3520. if (AcquiredReparsePointIndex) { NtfsReleaseScb( IrpContext, Vcb->ReparsePointTableScb ); }
  3521. if (AcquiredFcbTable) { NtfsReleaseFcbTable( IrpContext, Vcb ); }
  3522. if (FcbWithPagingToRelease != NULL) { ExReleaseResourceLite( FcbWithPagingToRelease->PagingIoResource ); }
  3523. NtfsUnpinBcb( IrpContext, &IndexEntryBcb );
  3524. //
  3525. // If we allocated any buffers for the notify operations deallocate them now.
  3526. //
  3527. if (NewFullLinkNameBuffer != NULL) { NtfsFreePool( NewFullLinkNameBuffer ); }
  3528. if (PrevFullLinkName.Buffer != NULL) { NtfsFreePool( PrevFullLinkName.Buffer ); }
  3529. if (SourceFullLinkName.Buffer != NULL) {
  3530. NtfsFreePool( SourceFullLinkName.Buffer );
  3531. }
  3532. //
  3533. // If we allocated a file name attribute, we deallocate it now.
  3534. //
  3535. if (FileNameAttr != NULL) { NtfsFreePool( FileNameAttr ); }
  3536. //
  3537. // If we allocated a buffer for the tunneled names, deallocate them now.
  3538. //
  3539. if (NamePair.Long.Buffer != NamePair.LongBuffer) {
  3540. NtfsFreePool( NamePair.Long.Buffer );
  3541. }
  3542. //
  3543. // Some cleanup only occurs if this request has not been posted to
  3544. // the oplock package
  3545. if (Status != STATUS_PENDING) {
  3546. if (AcquiredTargetLinkFcb) {
  3547. NtfsTeardownStructures( IrpContext,
  3548. TargetLinkFcb,
  3549. NULL,
  3550. FALSE,
  3551. 0,
  3552. NULL );
  3553. }
  3554. }
  3555. DebugTrace( -1, Dbg, ("NtfsSetRenameInfo: Exit -> %08lx\n", Status) );
  3556. }
  3557. return Status;
  3558. }
  3559. //
  3560. // Internal Support Routine
  3561. //
  3562. NTSTATUS
  3563. NtfsSetLinkInfo (
  3564. IN PIRP_CONTEXT IrpContext,
  3565. IN PIRP Irp,
  3566. IN PVCB Vcb,
  3567. IN PSCB Scb,
  3568. IN PCCB Ccb,
  3569. IN OUT PBOOLEAN VcbAcquired
  3570. )
  3571. /*++
  3572. Routine Description:
  3573. This routine performs the set link function. It will create a new link for a
  3574. file.
  3575. Arguments:
  3576. Irp - Supplies the Irp being processed
  3577. Vcb - Supplies the Vcb for the Volume
  3578. Scb - Supplies the Scb for the file/directory being modified
  3579. Ccb - Supplies the Ccb for this file object
  3580. Return Value:
  3581. NTSTATUS - The status of the operation
  3582. --*/
  3583. {
  3584. NTSTATUS Status = STATUS_SUCCESS;
  3585. PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
  3586. PLCB Lcb = Ccb->Lcb;
  3587. PFCB Fcb = Scb->Fcb;
  3588. PSCB ParentScb = NULL;
  3589. SHORT LinkCountAdj = 0;
  3590. BOOLEAN AcquiredParentScb = TRUE;
  3591. BOOLEAN AcquiredObjectIdIndex = FALSE;
  3592. PFCB LockedFcb = NULL;
  3593. UNICODE_STRING NewLinkName;
  3594. UNICODE_STRING NewFullLinkName;
  3595. PWCHAR NewFullLinkNameBuffer = NULL;
  3596. PFILE_NAME NewLinkNameAttr = NULL;
  3597. USHORT NewLinkNameAttrLength = 0;
  3598. UCHAR NewLinkNameFlags;
  3599. PSCB TargetParentScb;
  3600. PFILE_OBJECT TargetFileObject = IrpSp->Parameters.SetFile.FileObject;
  3601. BOOLEAN FoundPrevLink;
  3602. UNICODE_STRING PrevLinkName;
  3603. UNICODE_STRING PrevFullLinkName;
  3604. UCHAR PrevLinkNameFlags;
  3605. USHORT PrevFcbLinkCountAdj = 0;
  3606. BOOLEAN ExistingPrevFcb = FALSE;
  3607. PFCB PreviousFcb = NULL;
  3608. ULONG RenameFlags = 0;
  3609. BOOLEAN AcquiredFcbTable = FALSE;
  3610. PFCB FcbWithPagingResourceToRelease = NULL;
  3611. BOOLEAN ReportDirNotify = FALSE;
  3612. PWCHAR NextChar;
  3613. PINDEX_ENTRY IndexEntry;
  3614. PBCB IndexEntryBcb = NULL;
  3615. PLIST_ENTRY Links;
  3616. PSCB ThisScb;
  3617. PISECURITY_DESCRIPTOR SecurityDescriptor;
  3618. PAGED_CODE();
  3619. DebugTrace( +1, Dbg, ("NtfsSetLinkInfo...\n") );
  3620. PrevFullLinkName.Buffer = NULL;
  3621. //
  3622. // If we are not opening the entire file, we can't set link info.
  3623. //
  3624. if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE )) {
  3625. DebugTrace( -1, Dbg, ("NtfsSetLinkInfo: Exit -> %08lx\n", STATUS_INVALID_PARAMETER) );
  3626. return STATUS_INVALID_PARAMETER;
  3627. }
  3628. //
  3629. // We also fail this if we are attempting to create a link on a directory.
  3630. // This will prevent cycles from being created.
  3631. //
  3632. if (FlagOn( Fcb->Info.FileAttributes, DUP_FILE_NAME_INDEX_PRESENT)) {
  3633. DebugTrace( -1, Dbg, ("NtfsSetLinkInfo: Exit -> %08lx\n", STATUS_FILE_IS_A_DIRECTORY) );
  3634. return STATUS_FILE_IS_A_DIRECTORY;
  3635. }
  3636. //
  3637. // We can't add a link without having a parent directory. Either we want to use the same
  3638. // parent or our caller supplied a parent.
  3639. //
  3640. if (Lcb == NULL) {
  3641. //
  3642. // If the current file has been opened by FileId and there are no
  3643. // remaining links not marked for delete then don't allow this
  3644. // operation. This is because we defer the delete of the last link
  3645. // until all of the OpenByID handles are closed. We don't have any
  3646. // easy way to remember that there is a link to delete after
  3647. // the open through the link is closed.
  3648. //
  3649. // The OPEN_BY_FILE_ID flag indicates that we used an open by Id somewhere
  3650. // in the open path. This operation is OK if this user opened through
  3651. // a link, that's why we will only do this test if there is no Lcb.
  3652. //
  3653. if (FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_FILE_ID ) &&
  3654. (Fcb->LinkCount == 0)) {
  3655. Status = STATUS_ACCESS_DENIED;
  3656. DebugTrace( -1, Dbg, ("NtfsSetLinkInfo: Exit -> %08lx\n", Status) );
  3657. return Status;
  3658. }
  3659. //
  3660. // If there is no target file object, then we can't add a link.
  3661. //
  3662. if (TargetFileObject == NULL) {
  3663. DebugTrace( -1, Dbg, ("NtfsSetLinkInfo: No target file object -> %08lx\n", STATUS_INVALID_PARAMETER) );
  3664. return STATUS_INVALID_PARAMETER;
  3665. }
  3666. } else {
  3667. ParentScb = Lcb->Scb;
  3668. //
  3669. // If this link has been deleted, then we don't allow this operation.
  3670. //
  3671. if (LcbLinkIsDeleted( Lcb )) {
  3672. Status = STATUS_ACCESS_DENIED;
  3673. DebugTrace( -1, Dbg, ("NtfsSetLinkInfo: Exit -> %08lx\n", Status) );
  3674. return Status;
  3675. }
  3676. }
  3677. //
  3678. // Check if we are allowed to perform this link operation. We can't if this
  3679. // is a system file or the user hasn't opened the entire file. We
  3680. // don't need to check for the root explicitly since it is one of
  3681. // the system files.
  3682. //
  3683. if (FlagOn( Fcb->FcbState, FCB_STATE_SYSTEM_FILE )) {
  3684. Status = STATUS_INVALID_PARAMETER;
  3685. DebugTrace( -1, Dbg, ("NtfsSetLinkInfo: Exit -> %08lx\n", Status) );
  3686. return Status;
  3687. }
  3688. //
  3689. // Verify that we can wait.
  3690. //
  3691. if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT )) {
  3692. Status = NtfsPostRequest( IrpContext, Irp );
  3693. DebugTrace( -1, Dbg, ("NtfsSetLinkInfo: Can't wait\n") );
  3694. return Status;
  3695. }
  3696. //
  3697. // Check if we will want to report this via the dir notify package.
  3698. //
  3699. if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_FILE_ID ) &&
  3700. (Ccb->FullFileName.Buffer[0] == L'\\') &&
  3701. (Vcb->NotifyCount != 0)) {
  3702. ReportDirNotify = TRUE;
  3703. }
  3704. //
  3705. // Remember the source info flags in the Ccb.
  3706. //
  3707. IrpContext->SourceInfo = Ccb->UsnSourceInfo;
  3708. //
  3709. // Use a try-finally to facilitate cleanup.
  3710. //
  3711. try {
  3712. //
  3713. // Post the change to the Usn Journal (on errors change is backed out)
  3714. //
  3715. NtfsPostUsnChange( IrpContext, Scb, USN_REASON_HARD_LINK_CHANGE );
  3716. //
  3717. // We now assemble the names and in memory-structures for both the
  3718. // source and target links and check if the target link currently
  3719. // exists.
  3720. //
  3721. NtfsFindTargetElements( IrpContext,
  3722. TargetFileObject,
  3723. ParentScb,
  3724. &TargetParentScb,
  3725. &NewFullLinkName,
  3726. &NewLinkName );
  3727. //
  3728. // Check that the new name is not invalid.
  3729. //
  3730. if ((NewLinkName.Length > (NTFS_MAX_FILE_NAME_LENGTH * sizeof( WCHAR ))) ||
  3731. !NtfsIsFileNameValid( &NewLinkName, FALSE )) {
  3732. Status = STATUS_OBJECT_NAME_INVALID;
  3733. leave;
  3734. }
  3735. if (TargetParentScb == ParentScb) {
  3736. //
  3737. // Acquire the target parent in order to synchronize adding a link.
  3738. //
  3739. NtfsAcquireExclusiveScb( IrpContext, ParentScb );
  3740. //
  3741. // Check if we are creating a link to the same directory with the
  3742. // exact same name.
  3743. //
  3744. if (NtfsAreNamesEqual( Vcb->UpcaseTable,
  3745. &NewLinkName,
  3746. &Lcb->ExactCaseLink.LinkName,
  3747. FALSE )) {
  3748. DebugTrace( 0, Dbg, ("Creating link to same name and directory\n") );
  3749. Status = STATUS_SUCCESS;
  3750. leave;
  3751. }
  3752. //
  3753. // Make sure the normalized name is in this Scb.
  3754. //
  3755. if (ParentScb->ScbType.Index.NormalizedName.Length == 0) {
  3756. NtfsBuildNormalizedName( IrpContext,
  3757. ParentScb->Fcb,
  3758. ParentScb,
  3759. &ParentScb->ScbType.Index.NormalizedName );
  3760. }
  3761. //
  3762. // Otherwise we remember that we are creating this link in a new directory.
  3763. //
  3764. } else {
  3765. SetFlag( RenameFlags, CREATE_IN_NEW_DIR );
  3766. //
  3767. // We know that we need to acquire the target directory so we can
  3768. // add and remove links. We want to carefully acquire the Scb in the
  3769. // event we only have the Vcb shared.
  3770. //
  3771. if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX )) {
  3772. if (!NtfsAcquireExclusiveFcb( IrpContext,
  3773. TargetParentScb->Fcb,
  3774. TargetParentScb,
  3775. ACQUIRE_DONT_WAIT )) {
  3776. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX );
  3777. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  3778. }
  3779. //
  3780. // Now snapshot the Scb.
  3781. //
  3782. if (FlagOn( TargetParentScb->ScbState, SCB_STATE_FILE_SIZE_LOADED )) {
  3783. NtfsSnapshotScb( IrpContext, TargetParentScb );
  3784. }
  3785. } else {
  3786. NtfsAcquireExclusiveScb( IrpContext, TargetParentScb );
  3787. }
  3788. }
  3789. //
  3790. // If we are exceeding the maximum link count on this file then return
  3791. // an error. There isn't a descriptive error code to use at this time.
  3792. //
  3793. if (Fcb->TotalLinks >= NTFS_MAX_LINK_COUNT) {
  3794. Status = STATUS_TOO_MANY_LINKS;
  3795. leave;
  3796. }
  3797. //
  3798. // Lookup the entry for this filename in the target directory.
  3799. // We look in the Ccb for the type of case match for the target
  3800. // name.
  3801. //
  3802. FoundPrevLink = NtfsLookupEntry( IrpContext,
  3803. TargetParentScb,
  3804. BooleanFlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE ),
  3805. &NewLinkName,
  3806. &NewLinkNameAttr,
  3807. &NewLinkNameAttrLength,
  3808. NULL,
  3809. &IndexEntry,
  3810. &IndexEntryBcb,
  3811. NULL );
  3812. //
  3813. // If we found a matching link, we need to check how we want to operate
  3814. // on the source link and the target link. This means whether we
  3815. // have any work to do, whether we need to remove the target link
  3816. // and whether we need to remove the source link.
  3817. //
  3818. if (FoundPrevLink) {
  3819. PFILE_NAME IndexFileName;
  3820. IndexFileName = (PFILE_NAME) NtfsFoundIndexEntry( IndexEntry );
  3821. //
  3822. // If the file references match, we are trying to create a
  3823. // link where one already exists.
  3824. //
  3825. if (NtfsCheckLinkForNewLink( Fcb,
  3826. IndexFileName,
  3827. IndexEntry->FileReference,
  3828. &NewLinkName,
  3829. &RenameFlags )) {
  3830. //
  3831. // There is no work to do.
  3832. //
  3833. Status = STATUS_SUCCESS;
  3834. leave;
  3835. }
  3836. //
  3837. // We need to check that the user wanted to remove that link.
  3838. //
  3839. if (!IrpSp->Parameters.SetFile.ReplaceIfExists) {
  3840. Status = STATUS_OBJECT_NAME_COLLISION;
  3841. leave;
  3842. }
  3843. //
  3844. // We want to preserve the case and the flags of the matching
  3845. // target link. We also want to preserve the case of the
  3846. // name being created. The following variables currently contain
  3847. // the exact case for the target to remove and the new name to
  3848. // apply.
  3849. //
  3850. // Link to remove - In 'IndexEntry'.
  3851. // The links flags are also in 'IndexEntry'. We copy
  3852. // these flags to 'PrevLinkNameFlags'
  3853. //
  3854. // New Name - Exact case is stored in 'NewLinkName'
  3855. // - Exact case is also stored in 'NewLinkNameAttr'
  3856. //
  3857. // We modify this so that we can use the FileName attribute
  3858. // structure to create the new link. We copy the linkname being
  3859. // removed into 'PrevLinkName'. The following is the
  3860. // state after the switch.
  3861. //
  3862. // 'NewLinkNameAttr' - contains the name for the link being
  3863. // created.
  3864. //
  3865. // 'PrevLinkName' - Contains the link name for the link being
  3866. // removed.
  3867. //
  3868. // 'PrevLinkNameFlags' - Contains the name flags for the link
  3869. // being removed.
  3870. //
  3871. //
  3872. // Remember the file name flags for the match being made.
  3873. //
  3874. PrevLinkNameFlags = IndexFileName->Flags;
  3875. //
  3876. // If we are report this via dir notify then build the full name.
  3877. // Otherwise just remember the last name.
  3878. //
  3879. if (ReportDirNotify) {
  3880. PrevFullLinkName.MaximumLength =
  3881. PrevFullLinkName.Length = (ParentScb->ScbType.Index.NormalizedName.Length +
  3882. sizeof( WCHAR ) +
  3883. NewLinkName.Length);
  3884. PrevFullLinkName.Buffer = NtfsAllocatePool( PagedPool,
  3885. PrevFullLinkName.MaximumLength );
  3886. RtlCopyMemory( PrevFullLinkName.Buffer,
  3887. ParentScb->ScbType.Index.NormalizedName.Buffer,
  3888. ParentScb->ScbType.Index.NormalizedName.Length );
  3889. NextChar = Add2Ptr( PrevFullLinkName.Buffer,
  3890. ParentScb->ScbType.Index.NormalizedName.Length );
  3891. if (ParentScb->ScbType.Index.NormalizedName.Length != sizeof( WCHAR )) {
  3892. *NextChar = L'\\';
  3893. NextChar += 1;
  3894. } else {
  3895. PrevFullLinkName.Length -= sizeof( WCHAR );
  3896. }
  3897. PrevLinkName.Buffer = NextChar;
  3898. } else {
  3899. PrevFullLinkName.Buffer =
  3900. PrevLinkName.Buffer = NtfsAllocatePool( PagedPool, NewLinkName.Length );
  3901. PrevFullLinkName.Length = PrevLinkName.MaximumLength = NewLinkName.Length;
  3902. }
  3903. //
  3904. // Copy the name found in the Index Entry to 'PrevLinkName'
  3905. //
  3906. PrevLinkName.Length =
  3907. PrevLinkName.MaximumLength = NewLinkName.Length;
  3908. RtlCopyMemory( PrevLinkName.Buffer,
  3909. IndexFileName->FileName,
  3910. NewLinkName.Length );
  3911. //
  3912. // We only need this check if the existing link is for a different file.
  3913. //
  3914. if (!FlagOn( RenameFlags, TRAVERSE_MATCH )) {
  3915. //
  3916. // We check if there is an existing Fcb for the target link.
  3917. // If there is, the unclean count better be 0.
  3918. //
  3919. NtfsAcquireFcbTable( IrpContext, Vcb );
  3920. AcquiredFcbTable = TRUE;
  3921. PreviousFcb = NtfsCreateFcb( IrpContext,
  3922. Vcb,
  3923. IndexEntry->FileReference,
  3924. FALSE,
  3925. BooleanFlagOn( Fcb->FcbState, FCB_STATE_COMPOUND_INDEX ),
  3926. &ExistingPrevFcb );
  3927. //
  3928. // Before we go on, make sure we aren't about to rename over a system file.
  3929. //
  3930. if (FlagOn( PreviousFcb->FcbState, FCB_STATE_SYSTEM_FILE )) {
  3931. Status = STATUS_ACCESS_DENIED;
  3932. leave;
  3933. }
  3934. //
  3935. // Add a paging resource to the target - this is not supplied if its created
  3936. // from scratch. We need this (acquired in the proper order) for the delete
  3937. // to work correctly if there are any data streams. It's not going to harm a
  3938. // directory del and because of the teardown in the finally clause its difficult
  3939. // to retry again without looping
  3940. //
  3941. NtfsLockFcb( IrpContext, PreviousFcb );
  3942. LockedFcb = PreviousFcb;
  3943. if (PreviousFcb->PagingIoResource == NULL) {
  3944. PreviousFcb->PagingIoResource = NtfsAllocateEresource();
  3945. }
  3946. NtfsUnlockFcb( IrpContext, LockedFcb );
  3947. LockedFcb = NULL;
  3948. //
  3949. // We need to acquire this file carefully in the event that we don't hold
  3950. // the Vcb exclusively.
  3951. //
  3952. if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX )) {
  3953. if (!ExAcquireResourceExclusiveLite( PreviousFcb->PagingIoResource, FALSE )) {
  3954. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX );
  3955. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  3956. }
  3957. FcbWithPagingResourceToRelease = PreviousFcb;
  3958. if (!NtfsAcquireExclusiveFcb( IrpContext, PreviousFcb, NULL, ACQUIRE_DONT_WAIT )) {
  3959. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX );
  3960. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  3961. }
  3962. NtfsReleaseFcbTable( IrpContext, Vcb );
  3963. AcquiredFcbTable = FALSE;
  3964. } else {
  3965. NtfsReleaseFcbTable( IrpContext, Vcb );
  3966. AcquiredFcbTable = FALSE;
  3967. //
  3968. // Acquire the paging Io resource for this file before the main
  3969. // resource in case we need to delete.
  3970. //
  3971. FcbWithPagingResourceToRelease = PreviousFcb;
  3972. ExAcquireResourceExclusiveLite( PreviousFcb->PagingIoResource, TRUE );
  3973. NtfsAcquireExclusiveFcb( IrpContext, PreviousFcb, NULL, 0 );
  3974. }
  3975. //
  3976. // If the Fcb Info field needs to be initialized, we do so now.
  3977. // We read this information from the disk as the duplicate information
  3978. // in the index entry is not guaranteed to be correct.
  3979. //
  3980. if (!FlagOn( PreviousFcb->FcbState, FCB_STATE_DUP_INITIALIZED )) {
  3981. NtfsUpdateFcbInfoFromDisk( IrpContext,
  3982. TRUE,
  3983. PreviousFcb,
  3984. NULL );
  3985. //
  3986. // If we need to acquire the object id index later in order
  3987. // to set or lookup information for the tunnel cache, we
  3988. // risk a deadlock if the quota index is still held. Given
  3989. // that superceding renames where the target Fcb isn't
  3990. // already open are a fairly rare case, we can tolerate the
  3991. // potential inefficiency of preacquiring the object id
  3992. // index now.
  3993. //
  3994. if (Vcb->ObjectIdTableScb != NULL) {
  3995. NtfsAcquireExclusiveScb( IrpContext, Vcb->ObjectIdTableScb );
  3996. AcquiredObjectIdIndex = TRUE;
  3997. }
  3998. NtfsConditionallyFixupQuota( IrpContext, PreviousFcb );
  3999. }
  4000. //
  4001. // We are adding a link to the source file which already
  4002. // exists as a link to a different file in the target directory.
  4003. //
  4004. // We need to check whether we permitted to delete this
  4005. // link. If not then it is possible that the problem is
  4006. // an existing batch oplock on the file. In that case
  4007. // we want to delete the batch oplock and try this again.
  4008. //
  4009. Status = NtfsCheckFileForDelete( IrpContext,
  4010. TargetParentScb,
  4011. PreviousFcb,
  4012. ExistingPrevFcb,
  4013. IndexEntry );
  4014. if (!NT_SUCCESS( Status )) {
  4015. PSCB NextScb = NULL;
  4016. //
  4017. // We are going to either fail this request or pass
  4018. // this on to the oplock package. Test if there is
  4019. // a batch oplock on any streams on this file.
  4020. //
  4021. while ((NextScb = NtfsGetNextChildScb( PreviousFcb,
  4022. NextScb )) != NULL) {
  4023. if ((NextScb->AttributeTypeCode == $DATA) &&
  4024. (NextScb->Header.NodeTypeCode == NTFS_NTC_SCB_DATA) &&
  4025. FsRtlCurrentBatchOplock( &NextScb->ScbType.Data.Oplock )) {
  4026. //
  4027. // Go ahead and perform any necessary cleanup now.
  4028. // Once we call the oplock package below we lose
  4029. // control of the IrpContext.
  4030. //
  4031. if (*VcbAcquired) {
  4032. NtfsReleaseVcb( IrpContext, Vcb );
  4033. *VcbAcquired = FALSE;
  4034. }
  4035. Status = FsRtlCheckOplock( &NextScb->ScbType.Data.Oplock,
  4036. Irp,
  4037. IrpContext,
  4038. NtfsOplockComplete,
  4039. NtfsPrePostIrp );
  4040. break;
  4041. }
  4042. }
  4043. leave;
  4044. }
  4045. //
  4046. // We are adding a link to the source file which already
  4047. // exists as a link to a different file in the target directory.
  4048. //
  4049. NtfsCleanupLinkForRemoval( PreviousFcb,
  4050. TargetParentScb,
  4051. ExistingPrevFcb );
  4052. //
  4053. // If the link count on this file is 1, then delete the file. Otherwise just
  4054. // delete the link.
  4055. //
  4056. if (PreviousFcb->LinkCount == 1) {
  4057. PVOID TempFcb;
  4058. TempFcb = (PFCB)IrpContext->CleanupStructure;
  4059. IrpContext->CleanupStructure = FcbWithPagingResourceToRelease;
  4060. FcbWithPagingResourceToRelease = TempFcb;
  4061. ASSERT( (NULL == TempFcb) || (NTFS_NTC_FCB == SafeNodeType( TempFcb )) );
  4062. NtfsDeleteFile( IrpContext,
  4063. PreviousFcb,
  4064. TargetParentScb,
  4065. &AcquiredParentScb,
  4066. NULL,
  4067. NULL );
  4068. FcbWithPagingResourceToRelease = IrpContext->CleanupStructure;
  4069. IrpContext->CleanupStructure = TempFcb;
  4070. //
  4071. // Make sure to force the close record out to disk.
  4072. //
  4073. PrevFcbLinkCountAdj += 1;
  4074. } else {
  4075. NtfsPostUsnChange( IrpContext, PreviousFcb, USN_REASON_HARD_LINK_CHANGE | USN_REASON_CLOSE );
  4076. NtfsRemoveLink( IrpContext,
  4077. PreviousFcb,
  4078. TargetParentScb,
  4079. PrevLinkName,
  4080. NULL,
  4081. NULL );
  4082. ClearFlag( PreviousFcb->FcbState, FCB_STATE_VALID_USN_NAME );
  4083. PrevFcbLinkCountAdj += 1;
  4084. NtfsUpdateFcb( PreviousFcb, FCB_INFO_CHANGED_LAST_CHANGE );
  4085. }
  4086. //
  4087. // Otherwise we need to remove this link as our caller wants to replace it
  4088. // with a different case.
  4089. //
  4090. } else {
  4091. NtfsRemoveLink( IrpContext,
  4092. Fcb,
  4093. TargetParentScb,
  4094. PrevLinkName,
  4095. NULL,
  4096. NULL );
  4097. //
  4098. // Make sure we find the name again when posting another change.
  4099. //
  4100. ClearFlag( Fcb->FcbState, FCB_STATE_VALID_USN_NAME );
  4101. PreviousFcb = Fcb;
  4102. LinkCountAdj += 1;
  4103. }
  4104. }
  4105. //
  4106. // Make sure we have the full name of the target if we will be reporting
  4107. // this.
  4108. //
  4109. if (ReportDirNotify && (NewFullLinkName.Buffer == NULL)) {
  4110. NewFullLinkName.MaximumLength =
  4111. NewFullLinkName.Length = (ParentScb->ScbType.Index.NormalizedName.Length +
  4112. sizeof( WCHAR ) +
  4113. NewLinkName.Length);
  4114. NewFullLinkNameBuffer =
  4115. NewFullLinkName.Buffer = NtfsAllocatePool( PagedPool,
  4116. NewFullLinkName.MaximumLength );
  4117. RtlCopyMemory( NewFullLinkName.Buffer,
  4118. ParentScb->ScbType.Index.NormalizedName.Buffer,
  4119. ParentScb->ScbType.Index.NormalizedName.Length );
  4120. NextChar = Add2Ptr( NewFullLinkName.Buffer,
  4121. ParentScb->ScbType.Index.NormalizedName.Length );
  4122. if (ParentScb->ScbType.Index.NormalizedName.Length != sizeof( WCHAR )) {
  4123. *NextChar = L'\\';
  4124. NextChar += 1;
  4125. } else {
  4126. NewFullLinkName.Length -= sizeof( WCHAR );
  4127. }
  4128. RtlCopyMemory( NextChar,
  4129. NewLinkName.Buffer,
  4130. NewLinkName.Length );
  4131. }
  4132. NtfsUnpinBcb( IrpContext, &IndexEntryBcb );
  4133. //
  4134. // Check that we have permission to add a file to this directory.
  4135. //
  4136. NtfsCheckIndexForAddOrDelete( IrpContext,
  4137. TargetParentScb->Fcb,
  4138. FILE_ADD_FILE,
  4139. Ccb->AccessFlags >> 2 );
  4140. //
  4141. // We always set the last change time on the file we renamed unless
  4142. // the caller explicitly set this.
  4143. //
  4144. SetFlag( Ccb->Flags, CCB_FLAG_UPDATE_LAST_CHANGE | CCB_FLAG_SET_ARCHIVE );
  4145. //
  4146. // We now want to add the new link into the target directory.
  4147. // We never create a primary link through the link operation although
  4148. // we can remove one.
  4149. //
  4150. NtfsAddLink( IrpContext,
  4151. FALSE,
  4152. TargetParentScb,
  4153. Fcb,
  4154. NewLinkNameAttr,
  4155. NULL,
  4156. &NewLinkNameFlags,
  4157. NULL,
  4158. NULL,
  4159. NULL );
  4160. LinkCountAdj -= 1;
  4161. NtfsUpdateFcb( TargetParentScb->Fcb,
  4162. (FCB_INFO_CHANGED_LAST_CHANGE |
  4163. FCB_INFO_CHANGED_LAST_MOD |
  4164. FCB_INFO_UPDATE_LAST_ACCESS) );
  4165. //
  4166. // Now we want to update the Fcb for the link we renamed. If we moved it
  4167. // to a new directory we need to move all the Lcb's associated with
  4168. // the previous link.
  4169. //
  4170. if (FlagOn( RenameFlags, TRAVERSE_MATCH )) {
  4171. NtfsReplaceLinkInDir( IrpContext,
  4172. TargetParentScb,
  4173. Fcb,
  4174. &NewLinkName,
  4175. NewLinkNameFlags,
  4176. &PrevLinkName,
  4177. PrevLinkNameFlags );
  4178. }
  4179. //
  4180. // We have now modified the on-disk structures. We now need to
  4181. // modify the in-memory structures. This includes the Fcb and Lcb's
  4182. // for any links we superseded, and the source Fcb and it's Lcb's.
  4183. //
  4184. // We start by looking at the link we superseded. We know the
  4185. // the target directory, link name and flags, and the file the
  4186. // link was connected to.
  4187. //
  4188. if (FoundPrevLink && !FlagOn( RenameFlags, TRAVERSE_MATCH )) {
  4189. NtfsUpdateFcbFromLinkRemoval( IrpContext,
  4190. TargetParentScb,
  4191. PreviousFcb,
  4192. PrevLinkName,
  4193. PrevLinkNameFlags );
  4194. }
  4195. //
  4196. // We have three cases to report for changes in the target directory..
  4197. //
  4198. // 1. If we overwrote an existing link to a different file, we
  4199. // report this as a modified file.
  4200. //
  4201. // 2. If we moved a link to a new directory, then we added a file.
  4202. //
  4203. // 3. If we renamed a link in in the same directory, then we report
  4204. // that there is a new name.
  4205. //
  4206. // We currently combine cases 2 and 3.
  4207. //
  4208. if (ReportDirNotify) {
  4209. ULONG FilterMatch = 0;
  4210. ULONG FileAction;
  4211. //
  4212. // If we removed an entry and it wasn't an exact case match, then
  4213. // report the entry which was removed.
  4214. //
  4215. if (!FlagOn( RenameFlags, EXACT_CASE_MATCH )) {
  4216. if (FoundPrevLink) {
  4217. NtfsReportDirNotify( IrpContext,
  4218. Vcb,
  4219. &PrevFullLinkName,
  4220. PrevFullLinkName.Length - PrevLinkName.Length,
  4221. NULL,
  4222. &TargetParentScb->ScbType.Index.NormalizedName,
  4223. (IsDirectory( &PreviousFcb->Info ) ?
  4224. FILE_NOTIFY_CHANGE_DIR_NAME :
  4225. FILE_NOTIFY_CHANGE_FILE_NAME),
  4226. FILE_ACTION_REMOVED,
  4227. TargetParentScb->Fcb );
  4228. }
  4229. //
  4230. // We will be adding an entry.
  4231. //
  4232. FilterMatch = FILE_NOTIFY_CHANGE_FILE_NAME;
  4233. FileAction = FILE_ACTION_ADDED;
  4234. //
  4235. // If this was not a traverse match then report that all the file
  4236. // properties changed.
  4237. //
  4238. } else if (!FlagOn( RenameFlags, TRAVERSE_MATCH )) {
  4239. FilterMatch |= (FILE_NOTIFY_CHANGE_ATTRIBUTES |
  4240. FILE_NOTIFY_CHANGE_SIZE |
  4241. FILE_NOTIFY_CHANGE_LAST_WRITE |
  4242. FILE_NOTIFY_CHANGE_LAST_ACCESS |
  4243. FILE_NOTIFY_CHANGE_CREATION |
  4244. FILE_NOTIFY_CHANGE_SECURITY |
  4245. FILE_NOTIFY_CHANGE_EA);
  4246. FileAction = FILE_ACTION_MODIFIED;
  4247. }
  4248. if (FilterMatch != 0) {
  4249. NtfsReportDirNotify( IrpContext,
  4250. Vcb,
  4251. &NewFullLinkName,
  4252. NewFullLinkName.Length - NewLinkName.Length,
  4253. NULL,
  4254. &TargetParentScb->ScbType.Index.NormalizedName,
  4255. FilterMatch,
  4256. FileAction,
  4257. TargetParentScb->Fcb );
  4258. }
  4259. }
  4260. //
  4261. // Checkpoint the transaction before we make the changes below. If there are Usn
  4262. // records then writing them could raise.
  4263. //
  4264. if (IrpContext->Usn.CurrentUsnFcb != NULL) {
  4265. NtfsCheckpointCurrentTransaction( IrpContext );
  4266. }
  4267. //
  4268. // Adjust the link counts on the files.
  4269. //
  4270. Fcb->TotalLinks = (SHORT) Fcb->TotalLinks - LinkCountAdj;
  4271. Fcb->LinkCount = (SHORT) Fcb->LinkCount - LinkCountAdj;
  4272. //
  4273. // We can now adjust the total link count on the previous Fcb.
  4274. //
  4275. if (PreviousFcb != NULL) {
  4276. PreviousFcb->TotalLinks -= PrevFcbLinkCountAdj;
  4277. PreviousFcb->LinkCount -= PrevFcbLinkCountAdj;
  4278. //
  4279. // Now go through and mark everything as deleted.
  4280. //
  4281. if (PreviousFcb->LinkCount == 0) {
  4282. SetFlag( PreviousFcb->FcbState, FCB_STATE_FILE_DELETED );
  4283. //
  4284. // Release the quota control block. This does not have to be done
  4285. // here however, it allows us to free up the quota control block
  4286. // before the fcb is removed from the table. This keeps the assert
  4287. // about quota table empty from triggering in
  4288. // NtfsClearAndVerifyQuotaIndex.
  4289. //
  4290. if (NtfsPerformQuotaOperation(PreviousFcb)) {
  4291. NtfsDereferenceQuotaControlBlock( Vcb,
  4292. &PreviousFcb->QuotaControl );
  4293. }
  4294. //
  4295. // We need to mark all of the Scbs as gone.
  4296. //
  4297. for (Links = PreviousFcb->ScbQueue.Flink;
  4298. Links != &PreviousFcb->ScbQueue;
  4299. Links = Links->Flink) {
  4300. ThisScb = CONTAINING_RECORD( Links, SCB, FcbLinks );
  4301. if (!FlagOn( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )) {
  4302. NtfsSnapshotScb( IrpContext, ThisScb );
  4303. ThisScb->ValidDataToDisk =
  4304. ThisScb->Header.AllocationSize.QuadPart =
  4305. ThisScb->Header.FileSize.QuadPart =
  4306. ThisScb->Header.ValidDataLength.QuadPart = 0;
  4307. SetFlag( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
  4308. }
  4309. }
  4310. }
  4311. }
  4312. //
  4313. // Do an audit record for the link creation if necc.
  4314. // Check if we need to load the security descriptor for the file
  4315. //
  4316. if (Fcb->SharedSecurity == NULL) {
  4317. NtfsLoadSecurityDescriptor( IrpContext, Fcb );
  4318. }
  4319. SecurityDescriptor = (PISECURITY_DESCRIPTOR) Fcb->SharedSecurity->SecurityDescriptor;
  4320. if (SeAuditingHardLinkEvents( TRUE, SecurityDescriptor )) {
  4321. UNICODE_STRING GeneratedName;
  4322. PUNICODE_STRING OldFullLinkName;
  4323. UNICODE_STRING DeviceAndOldLinkName;
  4324. UNICODE_STRING DeviceAndNewLinkName;
  4325. USHORT Length;
  4326. GeneratedName.Buffer = NULL;
  4327. DeviceAndOldLinkName.Buffer = NULL;
  4328. DeviceAndNewLinkName.Buffer = NULL;
  4329. try {
  4330. //
  4331. // Generate current filename
  4332. //
  4333. if (Ccb->FullFileName.Length != 0 ) {
  4334. OldFullLinkName = &Ccb->FullFileName;
  4335. } else {
  4336. NtfsBuildNormalizedName( IrpContext, Scb->Fcb, FALSE, &GeneratedName );
  4337. OldFullLinkName = &GeneratedName;
  4338. }
  4339. //
  4340. // Create the full device and file name strings
  4341. //
  4342. Length = Vcb->DeviceName.Length + OldFullLinkName->Length;
  4343. DeviceAndOldLinkName.Buffer = NtfsAllocatePool( PagedPool, Length );
  4344. DeviceAndOldLinkName.Length = DeviceAndOldLinkName.MaximumLength = Length;
  4345. RtlCopyMemory( DeviceAndOldLinkName.Buffer, Vcb->DeviceName.Buffer, Vcb->DeviceName.Length );
  4346. RtlCopyMemory( Add2Ptr( DeviceAndOldLinkName.Buffer, Vcb->DeviceName.Length ), OldFullLinkName->Buffer, OldFullLinkName->Length );
  4347. Length = Vcb->DeviceName.Length + TargetParentScb->ScbType.Index.NormalizedName.Length + sizeof( WCHAR ) + NewLinkName.Length;
  4348. DeviceAndNewLinkName.Buffer = NtfsAllocatePool( PagedPool, Length );
  4349. DeviceAndNewLinkName.Length = DeviceAndNewLinkName.MaximumLength = Length;
  4350. RtlCopyMemory( DeviceAndNewLinkName.Buffer, Vcb->DeviceName.Buffer, Vcb->DeviceName.Length );
  4351. RtlCopyMemory( Add2Ptr( DeviceAndNewLinkName.Buffer, Vcb->DeviceName.Length ),
  4352. TargetParentScb->ScbType.Index.NormalizedName.Buffer,
  4353. TargetParentScb->ScbType.Index.NormalizedName.Length );
  4354. NextChar = Add2Ptr( DeviceAndNewLinkName.Buffer,
  4355. Vcb->DeviceName.Length + TargetParentScb->ScbType.Index.NormalizedName.Length );
  4356. if (TargetParentScb->ScbType.Index.NormalizedName.Length != sizeof( WCHAR )) {
  4357. *NextChar = L'\\';
  4358. NextChar += 1;
  4359. } else {
  4360. DeviceAndNewLinkName.Length -= sizeof( WCHAR );
  4361. }
  4362. RtlCopyMemory( NextChar, NewLinkName.Buffer, NewLinkName.Length );
  4363. SeAuditHardLinkCreation( &DeviceAndOldLinkName,
  4364. &DeviceAndNewLinkName,
  4365. TRUE );
  4366. } finally {
  4367. if (GeneratedName.Buffer != NULL) {
  4368. NtfsFreePool( GeneratedName.Buffer );
  4369. }
  4370. if (DeviceAndNewLinkName.Buffer != NULL) {
  4371. NtfsFreePool( DeviceAndNewLinkName.Buffer );
  4372. }
  4373. if (DeviceAndOldLinkName.Buffer != NULL) {
  4374. NtfsFreePool( DeviceAndOldLinkName.Buffer );
  4375. }
  4376. }
  4377. }
  4378. } finally {
  4379. DebugUnwind( NtfsSetLinkInfo );
  4380. if (LockedFcb != NULL) {
  4381. NtfsUnlockFcb( IrpContext, LockedFcb );
  4382. }
  4383. //
  4384. // release objectid and reparse explicitly so we can call Teardown structures and wait to go up chain
  4385. //
  4386. if (AcquiredObjectIdIndex) { NtfsReleaseScb( IrpContext, Vcb->ObjectIdTableScb ); }
  4387. if (AcquiredFcbTable) { NtfsReleaseFcbTable( IrpContext, Vcb ); }
  4388. //
  4389. // If we allocated any buffers for name storage then deallocate them now.
  4390. //
  4391. if (PrevFullLinkName.Buffer != NULL) { NtfsFreePool( PrevFullLinkName.Buffer ); }
  4392. if (NewFullLinkNameBuffer != NULL) { NtfsFreePool( NewFullLinkNameBuffer ); }
  4393. //
  4394. // Release any paging io resource acquired.
  4395. //
  4396. if (FcbWithPagingResourceToRelease != NULL) { ExReleaseResourceLite( FcbWithPagingResourceToRelease->PagingIoResource ); }
  4397. //
  4398. // If we allocated a file name attribute, we deallocate it now.
  4399. //
  4400. if (NewLinkNameAttr != NULL) { NtfsFreePool( NewLinkNameAttr ); }
  4401. //
  4402. // If we have the Fcb for a removed link and it didn't previously
  4403. // exist, call our teardown routine.
  4404. //
  4405. if (Status != STATUS_PENDING) {
  4406. if ((PreviousFcb != NULL) &&
  4407. (PreviousFcb->CleanupCount == 0)) {
  4408. NtfsTeardownStructures( IrpContext,
  4409. PreviousFcb,
  4410. NULL,
  4411. FALSE,
  4412. 0,
  4413. NULL );
  4414. }
  4415. }
  4416. NtfsUnpinBcb( IrpContext, &IndexEntryBcb );
  4417. DebugTrace( -1, Dbg, ("NtfsSetLinkInfo: Exit -> %08lx\n", Status) );
  4418. }
  4419. return Status;
  4420. }
  4421. //
  4422. // Internal Support Routine
  4423. //
  4424. NTSTATUS
  4425. NtfsSetShortNameInfo (
  4426. IN PIRP_CONTEXT IrpContext,
  4427. IN PFILE_OBJECT FileObject,
  4428. IN PIRP Irp,
  4429. IN PVCB Vcb,
  4430. IN PSCB Scb,
  4431. IN PCCB Ccb
  4432. )
  4433. /*++
  4434. Routine Description:
  4435. This routine performs the set shortname function. We first check that the short name
  4436. passed to us is valid for the context established by the system, i.e. check length and
  4437. whether extended characters are allowed. We will use the same test Ntfs uses in the
  4438. create path to determine whether to generate a short name. If the name is valid then
  4439. check whether it is legal to put this short name on the link used to open the file.
  4440. It is legal if the existing link is either a long, long/short or a short name. It is
  4441. also legal if this is any link AND there isn't a specialized link (long, long/short, short)
  4442. on this file. The final check is that this new link can't be a case insensitive match with
  4443. any other link in the directory except for the existing short name on the file.
  4444. Arguments:
  4445. FileObject - Supplies the file object being processed
  4446. Irp - Supplies the Irp being processed
  4447. Vcb - Vcb for the volume
  4448. Scb - Supplies the Scb for the file/directory being modified
  4449. Ccb - Supplies the Ccb for this file object
  4450. Return Value:
  4451. NTSTATUS - The status of the operation
  4452. --*/
  4453. {
  4454. PLCB Lcb = Ccb->Lcb;
  4455. PFCB Fcb = Scb->Fcb;
  4456. PSCB ParentScb;
  4457. PLCB ShortNameLcb = NULL;
  4458. PLCB LongNameLcb = NULL;
  4459. UNICODE_STRING FullShortName;
  4460. UNICODE_STRING ShortName;
  4461. NTSTATUS Status = STATUS_SUCCESS;
  4462. UNICODE_STRING OldShortName;
  4463. UNICODE_STRING FullOldShortName;
  4464. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  4465. BOOLEAN CleanupAttrContext = FALSE;
  4466. BOOLEAN ExistingShortName = FALSE;
  4467. PFILE_NAME FoundFileName;
  4468. BOOLEAN FoundLink;
  4469. PFILE_NAME ShortNameAttr = NULL;
  4470. USHORT ShortNameAttrLength = 0;
  4471. LOGICAL ReportDirNotify = FALSE;
  4472. PINDEX_ENTRY IndexEntry;
  4473. PBCB IndexEntryBcb = NULL;
  4474. ASSERT_IRP_CONTEXT( IrpContext );
  4475. ASSERT_FILE_OBJECT( FileObject );
  4476. ASSERT_IRP( Irp );
  4477. ASSERT_SCB( Scb );
  4478. PAGED_CODE ();
  4479. DebugTrace( +1, Dbg, ("NtfsSetShortNameInfo...\n") );
  4480. OldShortName.Buffer = NULL;
  4481. FullOldShortName.Buffer = NULL;
  4482. FullShortName.Buffer = NULL;
  4483. //
  4484. // Do a quick check that the caller is allowed to do the rename.
  4485. // The opener must have opened the main data stream by name and this can't be
  4486. // a system file.
  4487. //
  4488. if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE ) ||
  4489. (Lcb == NULL) ||
  4490. FlagOn( Fcb->FcbState, FCB_STATE_SYSTEM_FILE )) {
  4491. DebugTrace( -1, Dbg, ("NtfsSetShortNameInfo: Exit -> %08lx\n", STATUS_INVALID_PARAMETER) );
  4492. return STATUS_INVALID_PARAMETER;
  4493. }
  4494. //
  4495. // The caller also must have restore privilege + plus some kind of write access to set the short name
  4496. //
  4497. if (!FlagOn( Ccb->AccessFlags, RESTORE_ACCESS ) ||
  4498. !FlagOn( Ccb->AccessFlags, WRITE_DATA_ACCESS | WRITE_ATTRIBUTES_ACCESS )) {
  4499. return STATUS_PRIVILEGE_NOT_HELD;
  4500. }
  4501. //
  4502. // This operation only applies to case-insensitive handles.
  4503. //
  4504. if (FlagOn( FileObject->Flags, FO_OPENED_CASE_SENSITIVE )) {
  4505. DebugTrace( -1, Dbg, ("NtfsSetShortNameInfo: Case sensitive handle\n") );
  4506. return STATUS_INVALID_PARAMETER;
  4507. }
  4508. //
  4509. // Validate the new short name. It must be a valid Ntfs name and satisfy
  4510. // the current requirement for a short name. The short name must be a full number of
  4511. // unicode characters and a valid short name for the current system.
  4512. //
  4513. ShortName.MaximumLength =
  4514. ShortName.Length = (USHORT) ((PFILE_NAME_INFORMATION) IrpContext->OriginatingIrp->AssociatedIrp.SystemBuffer)->FileNameLength;
  4515. ShortName.Buffer = (PWSTR) &((PFILE_NAME_INFORMATION) IrpContext->OriginatingIrp->AssociatedIrp.SystemBuffer)->FileName;
  4516. if ((ShortName.Length == 0) ||
  4517. FlagOn( ShortName.Length, 1 ) ||
  4518. !NtfsIsFatNameValid( &ShortName, FALSE )) {
  4519. DebugTrace( -1, Dbg, ("NtfsSetShortNameInfo: Invalid name\n") );
  4520. return STATUS_INVALID_PARAMETER;
  4521. }
  4522. //
  4523. // Make sure the name is upcased.
  4524. //
  4525. NtfsUpcaseName( Vcb->UpcaseTable, Vcb->UpcaseTableSize, &ShortName );
  4526. //
  4527. // If this link has been deleted, then we don't allow this operation.
  4528. //
  4529. if (LcbLinkIsDeleted( Lcb )) {
  4530. DebugTrace( -1, Dbg, ("NtfsSetShortNameInfo: Exit -> %08lx\n", STATUS_ACCESS_DENIED) );
  4531. return STATUS_ACCESS_DENIED;
  4532. }
  4533. //
  4534. // Verify that we can wait.
  4535. //
  4536. if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT )) {
  4537. Status = NtfsPostRequest( IrpContext, Irp );
  4538. DebugTrace( -1, Dbg, ("NtfsSetShortNameInfo: Can't wait\n") );
  4539. return Status;
  4540. }
  4541. //
  4542. // Use a try-finally to facilitate cleanup.
  4543. //
  4544. try {
  4545. //
  4546. // If this is a directory file, we need to examine its descendents.
  4547. // We may not remove a link which may be an ancestor path
  4548. // component of any open file.
  4549. //
  4550. if (IsDirectory( &Fcb->Info )) {
  4551. Status = NtfsCheckTreeForBatchOplocks( IrpContext, Irp, Scb );
  4552. //
  4553. // Get out if there are any blocking batch oplocks.
  4554. //
  4555. if (Status != STATUS_SUCCESS) {
  4556. leave;
  4557. }
  4558. }
  4559. //
  4560. // Find the Parent Scb.
  4561. //
  4562. ParentScb = Lcb->Scb;
  4563. //
  4564. // Acquire the parent and make sure it has a normalized name. Also make sure the current
  4565. // Fcb has a normalized name if it is a directory.
  4566. //
  4567. NtfsAcquireExclusiveScb( IrpContext, ParentScb );
  4568. if (ParentScb->ScbType.Index.NormalizedName.Length == 0) {
  4569. NtfsBuildNormalizedName( IrpContext,
  4570. ParentScb->Fcb,
  4571. ParentScb,
  4572. &ParentScb->ScbType.Index.NormalizedName );
  4573. }
  4574. if (IsDirectory( &Fcb->Info ) &&
  4575. (Scb->ScbType.Index.NormalizedName.Length == 0)) {
  4576. NtfsUpdateNormalizedName( IrpContext,
  4577. ParentScb,
  4578. Scb,
  4579. NULL,
  4580. FALSE );
  4581. }
  4582. if (Vcb->NotifyCount != 0) {
  4583. ReportDirNotify = TRUE;
  4584. }
  4585. //
  4586. // Check if the current Lcb is either part of or all of Ntfs/Dos name pair, if so then
  4587. // our life is much easier. Otherwise look through the filename attributes to verify
  4588. // there isn't already an Ntfs/Dos name.
  4589. //
  4590. if (!FlagOn( Lcb->FileNameAttr->Flags, FILE_NAME_NTFS | FILE_NAME_DOS )) {
  4591. //
  4592. // Initialize the attribute enumeration context.
  4593. //
  4594. NtfsInitializeAttributeContext( &AttrContext );
  4595. CleanupAttrContext = TRUE;
  4596. if (!NtfsLookupAttributeByCode( IrpContext,
  4597. Fcb,
  4598. &Fcb->FileReference,
  4599. $FILE_NAME,
  4600. &AttrContext )) {
  4601. DebugTrace( 0, Dbg, ("Can't find filename attribute Fcb @ %08lx\n", Fcb) );
  4602. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  4603. }
  4604. //
  4605. // Now keep looking until we find a match.
  4606. //
  4607. while (TRUE) {
  4608. FoundFileName = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  4609. //
  4610. // If we find any with the Ntfs/Dos flags set then get out.
  4611. //
  4612. if (FlagOn( FoundFileName->Flags, FILE_NAME_NTFS | FILE_NAME_DOS )) {
  4613. NtfsRaiseStatus( IrpContext, STATUS_OBJECT_NAME_COLLISION, NULL, NULL );
  4614. }
  4615. //
  4616. // Get the next filename attribute.
  4617. //
  4618. if (!NtfsLookupNextAttributeByCode( IrpContext,
  4619. Fcb,
  4620. $FILE_NAME,
  4621. &AttrContext )) {
  4622. break;
  4623. }
  4624. }
  4625. //
  4626. // We know the link in our hand will become the long name Lcb.
  4627. //
  4628. LongNameLcb = Lcb;
  4629. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  4630. CleanupAttrContext = FALSE;
  4631. //
  4632. // Find the appropriate long and short name Lcbs if they are present. We
  4633. // need to update them if present.
  4634. //
  4635. } else {
  4636. //
  4637. // The Lcb has at least one flag set. If both aren't set then there
  4638. // is a separate short name.
  4639. //
  4640. if (Lcb->FileNameAttr->Flags != (FILE_NAME_NTFS | FILE_NAME_DOS)) {
  4641. ExistingShortName = TRUE;
  4642. }
  4643. //
  4644. // If a long name flag is set then we have the long name Lcb.
  4645. //
  4646. if (FlagOn( Lcb->FileNameAttr->Flags, FILE_NAME_NTFS )) {
  4647. LongNameLcb = Lcb;
  4648. }
  4649. //
  4650. // Find out if there are any Lcb's for these links in memory.
  4651. // If not there then we don't need to update them.
  4652. //
  4653. ShortNameLcb = NtfsLookupLcbByFlags( Fcb, FILE_NAME_DOS );
  4654. if (LongNameLcb == NULL) {
  4655. LongNameLcb = NtfsLookupLcbByFlags( Fcb, FILE_NAME_NTFS );
  4656. }
  4657. }
  4658. //
  4659. // Verify that we don't have a case insensitive match in the directory UNLESS it is for
  4660. // the short name on the link we are adding this entry to. Our checks above already
  4661. // verified that any short name we find will match some component of the link we
  4662. // were called with.
  4663. //
  4664. FoundLink = NtfsLookupEntry( IrpContext,
  4665. ParentScb,
  4666. TRUE,
  4667. &ShortName,
  4668. &ShortNameAttr,
  4669. &ShortNameAttrLength,
  4670. NULL,
  4671. &IndexEntry,
  4672. &IndexEntryBcb,
  4673. NULL );
  4674. //
  4675. // If we found a link then there is nothing to do. Either its the same file in
  4676. // which case we noop or we have a name collision
  4677. //
  4678. if (FoundLink) {
  4679. if (NtfsEqualMftRef( &IndexEntry->FileReference, &Scb->Fcb->FileReference )) {
  4680. leave;
  4681. } else {
  4682. NtfsRaiseStatus( IrpContext, STATUS_OBJECT_NAME_COLLISION, NULL, NULL );
  4683. }
  4684. }
  4685. //
  4686. // Make sure the short name DOS bit is set.
  4687. //
  4688. ShortNameAttr->Flags = FILE_NAME_DOS;
  4689. //
  4690. // Grow the short name Lcb buffers if present.
  4691. //
  4692. if (ShortNameLcb != NULL) {
  4693. NtfsRenameLcb( IrpContext,
  4694. ShortNameLcb,
  4695. &ShortName,
  4696. FILE_NAME_DOS,
  4697. TRUE );
  4698. }
  4699. //
  4700. // We now have the appropriate Lcb's for the name switch and know that their buffers
  4701. // are the appropriate size. Proceed now to make the changes on disk and update the
  4702. // appropriate in-memory structures. Start with any on-disk changes which may need to
  4703. // be rolled back.
  4704. //
  4705. //
  4706. // Convert the corresponding long name to an Ntfs-only long name if necessary.
  4707. //
  4708. if (LongNameLcb != NULL) {
  4709. //
  4710. // It's possible that we don't need to update the flags.
  4711. //
  4712. if (LongNameLcb->FileNameAttr->Flags != FILE_NAME_NTFS) {
  4713. NtfsUpdateFileNameFlags( IrpContext,
  4714. Fcb,
  4715. ParentScb,
  4716. FILE_NAME_NTFS,
  4717. LongNameLcb->FileNameAttr );
  4718. }
  4719. } else {
  4720. //
  4721. // If the LongNameLcb is NULL then our caller must have opened
  4722. // through the short name Lcb. Since there must be a corresponding
  4723. // NTFS only name we don't need to update the flags.
  4724. //
  4725. ASSERT( Lcb->FileNameAttr->Flags == FILE_NAME_DOS );
  4726. ExistingShortName = TRUE;
  4727. }
  4728. //
  4729. // Remove the existing short name if necessary.
  4730. //
  4731. if (ExistingShortName) {
  4732. NtfsRemoveLinkViaFlags( IrpContext,
  4733. Fcb,
  4734. ParentScb,
  4735. FILE_NAME_DOS,
  4736. NULL,
  4737. &OldShortName );
  4738. //
  4739. // Now allocate a full name for the dir notify.
  4740. //
  4741. if (ReportDirNotify) {
  4742. //
  4743. // Figure out the length of the name.
  4744. //
  4745. FullOldShortName.Length = OldShortName.Length + ParentScb->ScbType.Index.NormalizedName.Length;
  4746. if (ParentScb != Vcb->RootIndexScb) {
  4747. FullOldShortName.Length += sizeof( WCHAR );
  4748. }
  4749. FullOldShortName.MaximumLength = FullOldShortName.Length;
  4750. FullOldShortName.Buffer = NtfsAllocatePool( PagedPool, FullOldShortName.Length );
  4751. //
  4752. // Copy in the full name. Note we always copy in the '\' separator but will automatically
  4753. // overwrite in the case where it wasn't needed.
  4754. //
  4755. RtlCopyMemory( FullOldShortName.Buffer,
  4756. ParentScb->ScbType.Index.NormalizedName.Buffer,
  4757. ParentScb->ScbType.Index.NormalizedName.Length );
  4758. *(FullOldShortName.Buffer + (ParentScb->ScbType.Index.NormalizedName.Length / sizeof( WCHAR ))) = L'\\';
  4759. RtlCopyMemory( Add2Ptr( FullOldShortName.Buffer, FullOldShortName.Length - OldShortName.Length ),
  4760. OldShortName.Buffer,
  4761. OldShortName.Length );
  4762. }
  4763. }
  4764. //
  4765. // Copy the correct dup info into the attribute.
  4766. //
  4767. RtlCopyMemory( &ShortNameAttr->Info,
  4768. &Fcb->Info,
  4769. sizeof( DUPLICATED_INFORMATION ));
  4770. //
  4771. // Put it in the file record.
  4772. //
  4773. NtfsInitializeAttributeContext( &AttrContext );
  4774. CleanupAttrContext = TRUE;
  4775. NtfsCreateAttributeWithValue( IrpContext,
  4776. Fcb,
  4777. $FILE_NAME,
  4778. NULL,
  4779. ShortNameAttr,
  4780. NtfsFileNameSize( ShortNameAttr ),
  4781. 0,
  4782. &ParentScb->Fcb->FileReference,
  4783. TRUE,
  4784. &AttrContext );
  4785. //
  4786. // Now put it in the index entry.
  4787. //
  4788. NtfsAddIndexEntry( IrpContext,
  4789. ParentScb,
  4790. ShortNameAttr,
  4791. NtfsFileNameSize( ShortNameAttr ),
  4792. &Fcb->FileReference,
  4793. NULL,
  4794. NULL );
  4795. //
  4796. // Now allocate a full name for the dir notify.
  4797. //
  4798. if (ReportDirNotify) {
  4799. //
  4800. // Figure out the length of the name.
  4801. //
  4802. FullShortName.Length = ShortName.Length + ParentScb->ScbType.Index.NormalizedName.Length;
  4803. if (ParentScb != Vcb->RootIndexScb) {
  4804. FullShortName.Length += sizeof( WCHAR );
  4805. }
  4806. FullShortName.MaximumLength = FullShortName.Length;
  4807. FullShortName.Buffer = NtfsAllocatePool( PagedPool, FullShortName.Length );
  4808. //
  4809. // Copy in the full name. Note we always copy in the '\' separator but will automatically
  4810. // overwrite in the case where it wasn't needed.
  4811. //
  4812. RtlCopyMemory( FullShortName.Buffer,
  4813. ParentScb->ScbType.Index.NormalizedName.Buffer,
  4814. ParentScb->ScbType.Index.NormalizedName.Length );
  4815. *(FullShortName.Buffer + (ParentScb->ScbType.Index.NormalizedName.Length / sizeof( WCHAR ))) = L'\\';
  4816. RtlCopyMemory( Add2Ptr( FullShortName.Buffer, FullShortName.Length - ShortName.Length ),
  4817. ShortName.Buffer,
  4818. ShortName.Length );
  4819. }
  4820. //
  4821. // Write the usn journal entry now if active. We want to write this log record
  4822. // before updating the in-memory data structures.
  4823. //
  4824. NtfsPostUsnChange( IrpContext,
  4825. Fcb,
  4826. USN_REASON_HARD_LINK_CHANGE );
  4827. //
  4828. // The on-disk changes are complete. Checkpoint the transaction now so we don't have to
  4829. // roll back any in-memory structures if we get a LOG_FILE_FULL when writing to the Usn journal.
  4830. //
  4831. NtfsCheckpointCurrentTransaction( IrpContext );
  4832. //
  4833. // Update the existing long and short names Lcb with the new names and flags if necessary.
  4834. //
  4835. if (LongNameLcb != NULL) {
  4836. LongNameLcb->FileNameAttr->Flags = FILE_NAME_NTFS;
  4837. }
  4838. if (ShortNameLcb != NULL) {
  4839. NtfsRenameLcb( IrpContext,
  4840. ShortNameLcb,
  4841. &ShortName,
  4842. FILE_NAME_DOS,
  4843. FALSE );
  4844. }
  4845. if (ReportDirNotify) {
  4846. //
  4847. // Generate the DirNotify event for the old short name if necessary.
  4848. //
  4849. if (ExistingShortName) {
  4850. NtfsReportDirNotify( IrpContext,
  4851. Vcb,
  4852. &FullOldShortName,
  4853. FullOldShortName.Length - OldShortName.Length,
  4854. NULL,
  4855. NULL,
  4856. IsDirectory( &Fcb->Info ) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
  4857. FILE_ACTION_RENAMED_OLD_NAME,
  4858. ParentScb->Fcb );
  4859. }
  4860. //
  4861. // Generate the DirNotify event for the new short name.
  4862. //
  4863. NtfsReportDirNotify( IrpContext,
  4864. Vcb,
  4865. &FullShortName,
  4866. FullShortName.Length - ShortName.Length,
  4867. NULL,
  4868. NULL,
  4869. IsDirectory( &Fcb->Info ) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
  4870. FILE_ACTION_RENAMED_NEW_NAME,
  4871. ParentScb->Fcb );
  4872. }
  4873. //
  4874. // Change the time stamps in the parent if we modified the links in this directory.
  4875. //
  4876. NtfsUpdateFcb( ParentScb->Fcb,
  4877. (FCB_INFO_CHANGED_LAST_CHANGE |
  4878. FCB_INFO_CHANGED_LAST_MOD |
  4879. FCB_INFO_UPDATE_LAST_ACCESS) );
  4880. //
  4881. // Update the last change time and archive bit. No archive bit change for
  4882. // directories.
  4883. //
  4884. SetFlag( Ccb->Flags, CCB_FLAG_UPDATE_LAST_CHANGE );
  4885. //
  4886. // Don't set the archive bit on a directory. Otherwise we break existing
  4887. // apps that don't expect to see this flag.
  4888. //
  4889. if (!IsDirectory( &Fcb->Info )) {
  4890. SetFlag( Ccb->Flags, CCB_FLAG_SET_ARCHIVE );
  4891. }
  4892. } finally {
  4893. DebugUnwind( NtfsSetShortNameInfo );
  4894. //
  4895. // Cleanup the allocations and contexts used.
  4896. //
  4897. if (CleanupAttrContext) {
  4898. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  4899. }
  4900. if (ShortNameAttr != NULL) {
  4901. NtfsFreePool( ShortNameAttr );
  4902. }
  4903. if (OldShortName.Buffer != NULL) {
  4904. NtfsFreePool( OldShortName.Buffer );
  4905. }
  4906. if (FullOldShortName.Buffer != NULL) {
  4907. NtfsFreePool( FullOldShortName.Buffer );
  4908. }
  4909. if (FullShortName.Buffer != NULL) {
  4910. NtfsFreePool( FullShortName.Buffer );
  4911. }
  4912. NtfsUnpinBcb( IrpContext, &IndexEntryBcb );
  4913. DebugTrace( -1, Dbg, ("NtfsSetShortNameInfo: Exit -> %08lx\n", Status) );
  4914. }
  4915. return Status;
  4916. }
  4917. //
  4918. // Internal Support Routine
  4919. //
  4920. NTSTATUS
  4921. NtfsSetPositionInfo (
  4922. IN PIRP_CONTEXT IrpContext,
  4923. IN PFILE_OBJECT FileObject,
  4924. IN PIRP Irp,
  4925. IN PSCB Scb
  4926. )
  4927. /*++
  4928. Routine Description:
  4929. This routine performs the set position information function.
  4930. Arguments:
  4931. FileObject - Supplies the file object being processed
  4932. Irp - Supplies the Irp being processed
  4933. Scb - Supplies the Scb for the file/directory being modified
  4934. Return Value:
  4935. NTSTATUS - The status of the operation
  4936. --*/
  4937. {
  4938. NTSTATUS Status;
  4939. PFILE_POSITION_INFORMATION Buffer;
  4940. ASSERT_IRP_CONTEXT( IrpContext );
  4941. ASSERT_FILE_OBJECT( FileObject );
  4942. ASSERT_IRP( Irp );
  4943. ASSERT_SCB( Scb );
  4944. PAGED_CODE();
  4945. DebugTrace( +1, Dbg, ("NtfsSetPositionInfo...\n") );
  4946. //
  4947. // Reference the system buffer containing the user specified position
  4948. // information record
  4949. //
  4950. Buffer = Irp->AssociatedIrp.SystemBuffer;
  4951. try {
  4952. //
  4953. // Check if the file does not use intermediate buffering. If it does
  4954. // not use intermediate buffering then the new position we're supplied
  4955. // must be aligned properly for the device
  4956. //
  4957. if (FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING )) {
  4958. PDEVICE_OBJECT DeviceObject;
  4959. DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject;
  4960. if ((Buffer->CurrentByteOffset.LowPart & DeviceObject->AlignmentRequirement) != 0) {
  4961. DebugTrace( 0, Dbg, ("Offset missaligned %08lx %08lx\n", Buffer->CurrentByteOffset.LowPart, Buffer->CurrentByteOffset.HighPart) );
  4962. try_return( Status = STATUS_INVALID_PARAMETER );
  4963. }
  4964. }
  4965. //
  4966. // Set the new current byte offset in the file object
  4967. //
  4968. FileObject->CurrentByteOffset = Buffer->CurrentByteOffset;
  4969. Status = STATUS_SUCCESS;
  4970. try_exit: NOTHING;
  4971. } finally {
  4972. DebugUnwind( NtfsSetPositionInfo );
  4973. NOTHING;
  4974. }
  4975. //
  4976. // And return to our caller
  4977. //
  4978. DebugTrace( -1, Dbg, ("NtfsSetPositionInfo -> %08lx\n", Status) );
  4979. return Status;
  4980. }
  4981. //
  4982. // Internal Support Routine
  4983. //
  4984. NTSTATUS
  4985. NtfsPrepareToShrinkFileSize (
  4986. IN PIRP_CONTEXT IrpContext,
  4987. IN PFILE_OBJECT FileObject,
  4988. IN PSCB Scb,
  4989. LONGLONG NewFileSize
  4990. )
  4991. /*++
  4992. Routine Description:
  4993. Page in the last page of the file so we don't deadlock behind another thread
  4994. trying to access it. (CcSetFileSizes will do a purge that will try to zero
  4995. the cachemap directly when we shrink a file)
  4996. Note: this requires droping and regaining the main resource to not deadlock
  4997. and must be done before a transaction has started
  4998. Arguments:
  4999. FileObject - Supplies the file object being processed
  5000. Scb - Supplies the Scb for the file/directory being modified
  5001. NewFileSize - The new size the file will shrink to
  5002. Return Value:
  5003. NTSTATUS - The status of the operation
  5004. --*/
  5005. {
  5006. IO_STATUS_BLOCK Iosb;
  5007. ULONG Buffer;
  5008. if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer,
  5009. (PLARGE_INTEGER)&NewFileSize )) {
  5010. return STATUS_USER_MAPPED_FILE;
  5011. }
  5012. if ((Scb->NonpagedScb->SegmentObject.DataSectionObject != NULL) &&
  5013. ((NewFileSize % PAGE_SIZE) != 0)) {
  5014. if (NULL == Scb->FileObject) {
  5015. NtfsCreateInternalAttributeStream( IrpContext,
  5016. Scb,
  5017. FALSE,
  5018. &NtfsInternalUseFile[PREPARETOSHRINKFILESIZE_FILE_NUMBER] );
  5019. }
  5020. ASSERT( NtfsIsExclusiveScb( Scb ) );
  5021. NtfsReleaseScb( IrpContext, Scb );
  5022. NewFileSize = NewFileSize & ~(PAGE_SIZE - 1);
  5023. if (!CcCopyRead( Scb->FileObject,
  5024. (PLARGE_INTEGER)&NewFileSize,
  5025. 1,
  5026. BooleanFlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ),
  5027. &Buffer,
  5028. &Iosb )) {
  5029. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  5030. }
  5031. NtfsAcquireExclusiveScb( IrpContext, Scb );
  5032. }
  5033. return STATUS_SUCCESS;
  5034. }
  5035. //
  5036. // Internal Support Routine
  5037. //
  5038. NTSTATUS
  5039. NtfsSetAllocationInfo (
  5040. IN PIRP_CONTEXT IrpContext,
  5041. IN PFILE_OBJECT FileObject,
  5042. IN PIRP Irp,
  5043. IN PSCB Scb,
  5044. IN PCCB Ccb
  5045. )
  5046. /*++
  5047. Routine Description:
  5048. This routine performs the set allocation information function.
  5049. Arguments:
  5050. FileObject - Supplies the file object being processed
  5051. Irp - Supplies the Irp being processed
  5052. Scb - Supplies the Scb for the file/directory being modified
  5053. Ccb - This is the Scb for the open operation. May not be present if
  5054. this is a Mm call.
  5055. Return Value:
  5056. NTSTATUS - The status of the operation
  5057. --*/
  5058. {
  5059. NTSTATUS Status;
  5060. PFCB Fcb = Scb->Fcb;
  5061. BOOLEAN NonResidentPath = FALSE;
  5062. BOOLEAN FileIsCached = FALSE;
  5063. BOOLEAN ClearCheckSizeFlag = FALSE;
  5064. LONGLONG NewAllocationSize;
  5065. LONGLONG PrevAllocationSize;
  5066. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  5067. BOOLEAN CleanupAttrContext = FALSE;
  5068. ASSERT_IRP_CONTEXT( IrpContext );
  5069. ASSERT_FILE_OBJECT( FileObject );
  5070. ASSERT_IRP( Irp );
  5071. ASSERT_SCB( Scb );
  5072. PAGED_CODE();
  5073. DebugTrace( +1, Dbg, ("NtfsSetAllocationInfo...\n") );
  5074. //
  5075. // Are we serialized correctly? In NtfsCommonSetInformation above, we get
  5076. // paging shared for a lazy writer callback, but we should never end up in
  5077. // here from a lazy writer callback.
  5078. //
  5079. ASSERT( NtfsIsExclusiveScbPagingIo( Scb ) );
  5080. //
  5081. // If this attribute has been 'deleted' then we we can return immediately
  5082. //
  5083. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )) {
  5084. Status = STATUS_SUCCESS;
  5085. DebugTrace( -1, Dbg, ("NtfsSetAllocationInfo: Attribute is already deleted\n") );
  5086. return Status;
  5087. }
  5088. if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
  5089. NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
  5090. }
  5091. if (Ccb != NULL) {
  5092. //
  5093. // Remember the source info flags in the Ccb.
  5094. //
  5095. IrpContext->SourceInfo = Ccb->UsnSourceInfo;
  5096. }
  5097. //
  5098. // Save the current state of the Scb.
  5099. //
  5100. NtfsSnapshotScb( IrpContext, Scb );
  5101. //
  5102. // Get the new allocation size.
  5103. //
  5104. NewAllocationSize = ((PFILE_ALLOCATION_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->AllocationSize.QuadPart;
  5105. PrevAllocationSize = Scb->Header.AllocationSize.QuadPart;
  5106. //
  5107. // Check for a valid input value for the file size.
  5108. //
  5109. ASSERT( NewAllocationSize >= 0 );
  5110. if ((ULONGLONG)NewAllocationSize > MAXFILESIZE) {
  5111. Status = STATUS_INVALID_PARAMETER;
  5112. DebugTrace( -1, Dbg, ("NtfsSetAllocationInfo: Invalid allocation size\n") );
  5113. return Status;
  5114. }
  5115. //
  5116. // Do work to prepare for shrinking file if necc.
  5117. //
  5118. if (NewAllocationSize < Scb->Header.FileSize.QuadPart) {
  5119. //
  5120. // Paging IO should never shrink the file.
  5121. //
  5122. ASSERT( !FlagOn( Irp->Flags, IRP_PAGING_IO ) );
  5123. Status = NtfsPrepareToShrinkFileSize( IrpContext, FileObject, Scb, NewAllocationSize );
  5124. if (Status != STATUS_SUCCESS) {
  5125. DebugTrace( -1, Dbg, ("NtfsSetAllocationInfo -> %08lx\n", Status) );
  5126. return Status;
  5127. }
  5128. }
  5129. //
  5130. // Use a try-finally so we can update the on disk time-stamps.
  5131. //
  5132. try {
  5133. #ifdef SYSCACHE
  5134. //
  5135. // Let's remember this.
  5136. //
  5137. if (FlagOn( Scb->ScbState, SCB_STATE_SYSCACHE_FILE )) {
  5138. PSYSCACHE_EVENT SyscacheEvent;
  5139. SyscacheEvent = NtfsAllocatePool( PagedPool, sizeof( SYSCACHE_EVENT ) );
  5140. SyscacheEvent->EventTypeCode = SYSCACHE_SET_ALLOCATION_SIZE;
  5141. SyscacheEvent->Data1 = NewAllocationSize;
  5142. SyscacheEvent->Data2 = 0L;
  5143. InsertTailList( &Scb->ScbType.Data.SyscacheEventList, &SyscacheEvent->EventList );
  5144. }
  5145. #endif
  5146. //
  5147. // It is extremely expensive to make this call on a file that is not
  5148. // cached, and Ntfs has suffered stack overflows in addition to massive
  5149. // time and disk I/O expense (CcZero data on user mapped files!). Therefore,
  5150. // if no one has the file cached, we cache it here to make this call cheaper.
  5151. //
  5152. // Don't create the stream file if called from FsRtlSetFileSize (which sets
  5153. // IRP_PAGING_IO) because mm is in the process of creating a section.
  5154. //
  5155. if ((NewAllocationSize != Scb->Header.AllocationSize.QuadPart) &&
  5156. (Scb->NonpagedScb->SegmentObject.DataSectionObject != NULL)) {
  5157. FileIsCached = CcIsFileCached( FileObject );
  5158. if (!FileIsCached &&
  5159. !FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) &&
  5160. !FlagOn( Irp->Flags, IRP_PAGING_IO )) {
  5161. NtfsCreateInternalAttributeStream( IrpContext,
  5162. Scb,
  5163. FALSE,
  5164. &NtfsInternalUseFile[SETALLOCATIONINFO_FILE_NUMBER] );
  5165. FileIsCached = TRUE;
  5166. }
  5167. }
  5168. //
  5169. // If the caller is extending the allocation of resident attribute then
  5170. // we will force it to become non-resident. This solves the problem of
  5171. // trying to keep the allocation and file sizes in sync with only one
  5172. // number to use in the attribute header.
  5173. //
  5174. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  5175. NtfsInitializeAttributeContext( &AttrContext );
  5176. CleanupAttrContext = TRUE;
  5177. NtfsLookupAttributeForScb( IrpContext,
  5178. Scb,
  5179. NULL,
  5180. &AttrContext );
  5181. //
  5182. // Convert if extending.
  5183. //
  5184. if (NewAllocationSize > Scb->Header.AllocationSize.QuadPart) {
  5185. NtfsConvertToNonresident( IrpContext,
  5186. Fcb,
  5187. NtfsFoundAttribute( &AttrContext ),
  5188. (BOOLEAN) (!FileIsCached),
  5189. &AttrContext );
  5190. NonResidentPath = TRUE;
  5191. //
  5192. // Otherwise the allocation is shrinking or staying the same.
  5193. //
  5194. } else {
  5195. NewAllocationSize = QuadAlign( (ULONG) NewAllocationSize );
  5196. //
  5197. // If the allocation size doesn't change, we are done.
  5198. //
  5199. if ((ULONG) NewAllocationSize == Scb->Header.AllocationSize.LowPart) {
  5200. try_return( NOTHING );
  5201. }
  5202. //
  5203. // We are sometimes called by MM during a create section, so
  5204. // for right now the best way we have of detecting a create
  5205. // section is IRP_PAGING_IO being set, as in FsRtlSetFileSizes.
  5206. //
  5207. NtfsChangeAttributeValue( IrpContext,
  5208. Fcb,
  5209. (ULONG) NewAllocationSize,
  5210. NULL,
  5211. 0,
  5212. TRUE,
  5213. FALSE,
  5214. (BOOLEAN) (!FileIsCached),
  5215. FALSE,
  5216. &AttrContext );
  5217. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  5218. CleanupAttrContext = FALSE;
  5219. //
  5220. // Post this to the Usn journal if we are shrinking the data.
  5221. //
  5222. if (NewAllocationSize < Scb->Header.FileSize.QuadPart) {
  5223. NtfsPostUsnChange( IrpContext, Scb, USN_REASON_DATA_TRUNCATION );
  5224. }
  5225. //
  5226. // Now update the sizes in the Scb.
  5227. //
  5228. Scb->Header.AllocationSize.LowPart =
  5229. Scb->Header.FileSize.LowPart =
  5230. Scb->Header.ValidDataLength.LowPart = (ULONG) NewAllocationSize;
  5231. Scb->TotalAllocated = NewAllocationSize;
  5232. #ifdef SYSCACHE_DEBUG
  5233. if (ScbIsBeingLogged( Scb )) {
  5234. FsRtlLogSyscacheEvent( Scb, SCE_VDL_CHANGE, SCE_FLAG_SET_ALLOC, 0, 0, NewAllocationSize );
  5235. }
  5236. #endif
  5237. }
  5238. } else {
  5239. NonResidentPath = TRUE;
  5240. }
  5241. //
  5242. // We now test if we need to modify the non-resident allocation. We will
  5243. // do this in two cases. Either we're converting from resident in
  5244. // two steps or the attribute was initially non-resident.
  5245. //
  5246. if (NonResidentPath) {
  5247. NewAllocationSize = LlClustersFromBytes( Scb->Vcb, NewAllocationSize );
  5248. NewAllocationSize = LlBytesFromClusters( Scb->Vcb, NewAllocationSize );
  5249. DebugTrace( 0, Dbg, ("NewAllocationSize -> %016I64x\n", NewAllocationSize) );
  5250. //
  5251. // Now if the file allocation is being increased then we need to only add allocation
  5252. // to the attribute
  5253. //
  5254. if (Scb->Header.AllocationSize.QuadPart < NewAllocationSize) {
  5255. //
  5256. // Add either the true disk allocation or add a hole for a sparse
  5257. // file.
  5258. //
  5259. if (!FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE )) {
  5260. //
  5261. // If there is a compression unit then we could be in the process of
  5262. // decompressing. Allocate precisely in this case because we don't
  5263. // want to leave any holes. Specifically the user may have truncated
  5264. // the file and is now regenerating it yet the clear compression operation
  5265. // has already passed this point in the file (and dropped all resources).
  5266. // No one will go back to cleanup the allocation if we leave a hole now.
  5267. //
  5268. if (!FlagOn( Scb->ScbState, SCB_STATE_WRITE_COMPRESSED ) &&
  5269. (Scb->CompressionUnit != 0)) {
  5270. ASSERT( FlagOn( Scb->ScbState, SCB_STATE_REALLOCATE_ON_WRITE ));
  5271. NewAllocationSize += Scb->CompressionUnit - 1;
  5272. ((PLARGE_INTEGER) &NewAllocationSize)->LowPart &= ~(Scb->CompressionUnit - 1);
  5273. }
  5274. NtfsAddAllocation( IrpContext,
  5275. FileObject,
  5276. Scb,
  5277. LlClustersFromBytes( Scb->Vcb, Scb->Header.AllocationSize.QuadPart ),
  5278. LlClustersFromBytes( Scb->Vcb, NewAllocationSize - Scb->Header.AllocationSize.QuadPart ),
  5279. FALSE,
  5280. NULL );
  5281. } else {
  5282. NtfsAddSparseAllocation( IrpContext,
  5283. FileObject,
  5284. Scb,
  5285. Scb->Header.AllocationSize.QuadPart,
  5286. NewAllocationSize - Scb->Header.AllocationSize.QuadPart );
  5287. }
  5288. //
  5289. // Set the truncate on close flag.
  5290. //
  5291. SetFlag( Scb->ScbState, SCB_STATE_TRUNCATE_ON_CLOSE );
  5292. //
  5293. // Otherwise delete the allocation as requested.
  5294. //
  5295. } else if (Scb->Header.AllocationSize.QuadPart > NewAllocationSize) {
  5296. //
  5297. // Check on possible cleanup if the file will shrink.
  5298. //
  5299. if (NewAllocationSize < Scb->Header.FileSize.QuadPart) {
  5300. //
  5301. // If we will shrink FileSize, then write the UsnJournal.
  5302. //
  5303. NtfsPostUsnChange( IrpContext, Scb, USN_REASON_DATA_TRUNCATION );
  5304. Scb->Header.FileSize.QuadPart = NewAllocationSize;
  5305. //
  5306. // Do we need to shrink any of the valid data length values.
  5307. //
  5308. if (NewAllocationSize < Scb->Header.ValidDataLength.QuadPart) {
  5309. Scb->Header.ValidDataLength.QuadPart = NewAllocationSize;
  5310. #ifdef SYSCACHE_DEBUG
  5311. if (ScbIsBeingLogged( Scb )) {
  5312. FsRtlLogSyscacheEvent( Scb, SCE_VDL_CHANGE, SCE_FLAG_SET_ALLOC, 0, 0, NewAllocationSize );
  5313. }
  5314. #endif
  5315. }
  5316. if (NewAllocationSize < Scb->ValidDataToDisk) {
  5317. Scb->ValidDataToDisk = NewAllocationSize;
  5318. #ifdef SYSCACHE_DEBUG
  5319. if (ScbIsBeingLogged( Scb )) {
  5320. FsRtlLogSyscacheEvent( Scb, SCE_VDD_CHANGE, SCE_FLAG_SET_ALLOC, 0, 0, NewAllocationSize );
  5321. }
  5322. #endif
  5323. }
  5324. }
  5325. NtfsDeleteAllocation( IrpContext,
  5326. FileObject,
  5327. Scb,
  5328. LlClustersFromBytes( Scb->Vcb, NewAllocationSize ),
  5329. MAXLONGLONG,
  5330. TRUE,
  5331. TRUE );
  5332. }
  5333. //
  5334. // If this is the paging file then guarantee that the Mcb is fully loaded.
  5335. //
  5336. if (FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
  5337. NtfsPreloadAllocation( IrpContext,
  5338. Scb,
  5339. 0,
  5340. LlClustersFromBytes( Scb->Vcb, Scb->Header.AllocationSize.QuadPart ));
  5341. }
  5342. }
  5343. try_exit:
  5344. if (PrevAllocationSize != Scb->Header.AllocationSize.QuadPart) {
  5345. //
  5346. // Mark this file object as modified and with a size change in order to capture
  5347. // all of the changes to the Fcb.
  5348. //
  5349. SetFlag( FileObject->Flags, FO_FILE_SIZE_CHANGED );
  5350. ClearCheckSizeFlag = TRUE;
  5351. }
  5352. //
  5353. // Always set the file as modified to force a time stamp change.
  5354. //
  5355. if (ARGUMENT_PRESENT( Ccb )) {
  5356. SetFlag( Ccb->Flags,
  5357. (CCB_FLAG_UPDATE_LAST_MODIFY |
  5358. CCB_FLAG_UPDATE_LAST_CHANGE |
  5359. CCB_FLAG_SET_ARCHIVE) );
  5360. } else {
  5361. SetFlag( FileObject->Flags, FO_FILE_MODIFIED );
  5362. }
  5363. //
  5364. // Now capture any file size changes in this file object back to the Fcb.
  5365. //
  5366. NtfsUpdateScbFromFileObject( IrpContext, FileObject, Scb, TRUE );
  5367. //
  5368. // Update the standard information if required.
  5369. //
  5370. if (FlagOn( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO )) {
  5371. NtfsUpdateStandardInformation( IrpContext, Fcb );
  5372. }
  5373. ClearFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  5374. //
  5375. // We know we wrote out any changes to the file size above so clear the
  5376. // flag in the Scb to check the attribute size. This will save us from doing
  5377. // this unnecessarily at cleanup.
  5378. //
  5379. if (ClearCheckSizeFlag) {
  5380. ClearFlag( Scb->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE );
  5381. }
  5382. NtfsCheckpointCurrentTransaction( IrpContext );
  5383. //
  5384. // Update duplicated information.
  5385. //
  5386. NtfsUpdateFileDupInfo( IrpContext, Fcb, Ccb );
  5387. //
  5388. // Update the cache manager if needed.
  5389. //
  5390. if (CcIsFileCached( FileObject )) {
  5391. //
  5392. // We want to checkpoint the transaction if there is one active.
  5393. //
  5394. if (IrpContext->TransactionId != 0) {
  5395. NtfsCheckpointCurrentTransaction( IrpContext );
  5396. }
  5397. #ifdef SYSCACHE_DEBUG
  5398. if (ScbIsBeingLogged( Scb )) {
  5399. FsRtlLogSyscacheEvent( Scb, SCE_CC_SET_SIZE, SCE_FLAG_SET_ALLOC, 0, Scb->Header.ValidDataLength.QuadPart, Scb->Header.FileSize.QuadPart );
  5400. }
  5401. #endif
  5402. //
  5403. // Truncate either stream that is cached.
  5404. // Cachemap better exist or we will skip notifying cc and not potentially.
  5405. // purge the data section
  5406. //
  5407. ASSERT( FileObject->SectionObjectPointer->SharedCacheMap != NULL );
  5408. NtfsSetBothCacheSizes( FileObject,
  5409. (PCC_FILE_SIZES)&Scb->Header.AllocationSize,
  5410. Scb );
  5411. //
  5412. // Clear out the write mask on truncates to zero.
  5413. //
  5414. #ifdef SYSCACHE
  5415. if ((Scb->Header.FileSize.QuadPart == 0) && FlagOn(Scb->ScbState, SCB_STATE_SYSCACHE_FILE) &&
  5416. (Scb->ScbType.Data.WriteMask != NULL)) {
  5417. RtlZeroMemory(Scb->ScbType.Data.WriteMask, (((0x2000000) / PAGE_SIZE) / 8));
  5418. }
  5419. #endif
  5420. //
  5421. // Now cleanup the stream we created if there are no more user
  5422. // handles.
  5423. //
  5424. if ((Scb->CleanupCount == 0) && (Scb->FileObject != NULL)) {
  5425. NtfsDeleteInternalAttributeStream( Scb, FALSE, FALSE );
  5426. }
  5427. }
  5428. Status = STATUS_SUCCESS;
  5429. } finally {
  5430. DebugUnwind( NtfsSetAllocation );
  5431. if (CleanupAttrContext) {
  5432. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  5433. }
  5434. //
  5435. // And return to our caller
  5436. //
  5437. DebugTrace( -1, Dbg, ("NtfsSetAllocationInfo -> %08lx\n", Status) );
  5438. }
  5439. return Status;
  5440. }
  5441. //
  5442. // Internal Support Routine
  5443. //
  5444. NTSTATUS
  5445. NtfsSetEndOfFileInfo (
  5446. IN PIRP_CONTEXT IrpContext,
  5447. IN PFILE_OBJECT FileObject,
  5448. IN PIRP Irp,
  5449. IN PSCB Scb,
  5450. IN PCCB Ccb OPTIONAL,
  5451. IN BOOLEAN VcbAcquired
  5452. )
  5453. /*++
  5454. Routine Description:
  5455. This routine performs the set end of file information function.
  5456. Arguments:
  5457. FileObject - Supplies the file object being processed
  5458. Irp - Supplies the Irp being processed
  5459. Scb - Supplies the Scb for the file/directory being modified
  5460. Ccb - Supplies the Ccb for this operation. Will always be present if the
  5461. Vcb is acquired. Otherwise we must test for it.
  5462. AcquiredVcb - Indicates if this request has acquired the Vcb, meaning
  5463. do we have duplicate information to update.
  5464. Return Value:
  5465. NTSTATUS - The status of the operation
  5466. --*/
  5467. {
  5468. NTSTATUS Status;
  5469. PFCB Fcb = Scb->Fcb;
  5470. BOOLEAN NonResidentPath = TRUE;
  5471. BOOLEAN FileSizeChanged = FALSE;
  5472. BOOLEAN FileIsCached = FALSE;
  5473. LONGLONG NewFileSize;
  5474. LONGLONG NewValidDataLength;
  5475. ASSERT_IRP_CONTEXT( IrpContext );
  5476. ASSERT_FILE_OBJECT( FileObject );
  5477. ASSERT_IRP( Irp );
  5478. ASSERT_SCB( Scb );
  5479. PAGED_CODE();
  5480. DebugTrace( +1, Dbg, ("NtfsSetEndOfFileInfo...\n") );
  5481. if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
  5482. NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
  5483. }
  5484. //
  5485. // Get the new file size and whether this is coming from the lazy writer.
  5486. //
  5487. NewFileSize = ((PFILE_END_OF_FILE_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->EndOfFile.QuadPart;
  5488. //
  5489. // If this attribute has been 'deleted' then return immediately.
  5490. //
  5491. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )) {
  5492. DebugTrace( -1, Dbg, ("NtfsEndOfFileInfo: No work to do\n") );
  5493. return STATUS_SUCCESS;
  5494. }
  5495. //
  5496. // Save the current state of the Scb.
  5497. //
  5498. NtfsSnapshotScb( IrpContext, Scb );
  5499. //
  5500. // If we are called from the cache manager then we want to update the valid data
  5501. // length if necessary and also perform an update duplicate call if the Vcb
  5502. // is held.
  5503. //
  5504. if (IoGetCurrentIrpStackLocation(Irp)->Parameters.SetFile.AdvanceOnly) {
  5505. #ifdef SYSCACHE_DEBUG
  5506. if (ScbIsBeingLogged( Scb ) && (Scb->CleanupCount == 0)) {
  5507. FsRtlLogSyscacheEvent( Scb, SCE_WRITE, SCE_FLAG_SET_EOF, Scb->Header.ValidDataLength.QuadPart, Scb->ValidDataToDisk, NewFileSize );
  5508. }
  5509. #endif
  5510. //
  5511. // We only have work to do if the file is nonresident.
  5512. //
  5513. if (!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  5514. //
  5515. // Assume this is the lazy writer and set NewValidDataLength to
  5516. // NewFileSize (NtfsWriteFileSizes never goes beyond what's in the
  5517. // Fcb).
  5518. //
  5519. NewValidDataLength = NewFileSize;
  5520. NewFileSize = Scb->Header.FileSize.QuadPart;
  5521. //
  5522. // If this file has a compressed stream, then we have to possibly
  5523. // reduce NewValidDataLength according to dirty data in the opposite
  5524. // stream (compressed or uncompressed) from which we were called.
  5525. //
  5526. #ifdef COMPRESS_ON_WIRE
  5527. if (Scb->NonpagedScb->SegmentObjectC.SharedCacheMap != NULL) {
  5528. LARGE_INTEGER FlushedValidData;
  5529. PSECTION_OBJECT_POINTERS SegmentObject = &Scb->NonpagedScb->SegmentObject;
  5530. //
  5531. // Assume the other stream is not cached.
  5532. //
  5533. FlushedValidData.QuadPart = NewValidDataLength;
  5534. //
  5535. // If we were called for the compressed stream, then get flushed number
  5536. // for the normal stream.
  5537. //
  5538. if (FileObject->SectionObjectPointer != SegmentObject) {
  5539. if (SegmentObject->SharedCacheMap != NULL) {
  5540. FlushedValidData = CcGetFlushedValidData( SegmentObject, FALSE );
  5541. }
  5542. //
  5543. // Else if we were called for the normal stream, get the flushed number
  5544. // for the compressed stream.
  5545. //
  5546. } else {
  5547. FlushedValidData = CcGetFlushedValidData( &Scb->NonpagedScb->SegmentObjectC, FALSE );
  5548. }
  5549. if (NewValidDataLength > FlushedValidData.QuadPart) {
  5550. NewValidDataLength = FlushedValidData.QuadPart;
  5551. }
  5552. }
  5553. #endif
  5554. //
  5555. // NtfsWriteFileSizes will trim the new vdl down to filesize if necc. for on disk updates
  5556. // so we only need to explicitly trim it ourselfs for cases when its really growing
  5557. // but cc thinks its gone farther than it really has
  5558. // E.g in the activevacb case when its replaced cc considers the whole page dirty and
  5559. // advances valid data goal to the end of the page
  5560. //
  5561. // 3 pts protect us here - cc always trims valid data goal when we shrink so any
  5562. // callbacks indicate real data from this size file
  5563. // We inform cc of the new vdl on all unbuffered writes so eventually he will
  5564. // call us back to update for new disk sizes
  5565. // if mm and cc are active in a file we will let mm
  5566. // flush all pages beyond vdl. For the boundary page
  5567. // cc can flush it but we will move vdl fwd at that time as well
  5568. //
  5569. if ((Scb->Header.ValidDataLength.QuadPart < NewFileSize) &&
  5570. (NewValidDataLength > Scb->Header.ValidDataLength.QuadPart)) {
  5571. #ifdef SYSCACHE_DEBUG
  5572. if (ScbIsBeingLogged( Scb )) {
  5573. FsRtlLogSyscacheEvent( Scb, SCE_VDL_CHANGE, SCE_FLAG_SET_EOF, NewValidDataLength, 0, Scb->Header.ValidDataLength.QuadPart );
  5574. }
  5575. #endif
  5576. NewValidDataLength = Scb->Header.ValidDataLength.QuadPart;
  5577. } // endif advancing VDL
  5578. //
  5579. // Always call writefilesizes in case on disk VDL is less than the
  5580. // in memory one
  5581. //
  5582. NtfsWriteFileSizes( IrpContext,
  5583. Scb,
  5584. &NewValidDataLength,
  5585. TRUE,
  5586. TRUE,
  5587. TRUE );
  5588. }
  5589. //
  5590. // If we acquired the Vcb then do the update duplicate if necessary.
  5591. //
  5592. if (VcbAcquired) {
  5593. //
  5594. // Now capture any file size changes in this file object back to the Fcb.
  5595. //
  5596. NtfsUpdateScbFromFileObject( IrpContext, FileObject, Scb, TRUE );
  5597. //
  5598. // Update the standard information if required.
  5599. //
  5600. if (FlagOn( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO )) {
  5601. NtfsUpdateStandardInformation( IrpContext, Fcb );
  5602. ClearFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  5603. }
  5604. NtfsCheckpointCurrentTransaction( IrpContext );
  5605. //
  5606. // Update duplicated information.
  5607. //
  5608. NtfsUpdateFileDupInfo( IrpContext, Fcb, Ccb );
  5609. }
  5610. //
  5611. // We know the file size for this Scb is now correct on disk.
  5612. //
  5613. NtfsAcquireFsrtlHeader( Scb );
  5614. ClearFlag( Scb->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE );
  5615. NtfsReleaseFsrtlHeader( Scb );
  5616. } else {
  5617. if (Ccb != NULL) {
  5618. //
  5619. // Remember the source info flags in the Ccb.
  5620. //
  5621. IrpContext->SourceInfo = Ccb->UsnSourceInfo;
  5622. }
  5623. //
  5624. // Check for a valid input value for the file size.
  5625. //
  5626. if ((ULONGLONG)NewFileSize > MAXFILESIZE) {
  5627. Status = STATUS_INVALID_PARAMETER;
  5628. DebugTrace( -1, Dbg, ("NtfsSetEndOfFileInfo: Invalid file size -> %08lx\n", Status) );
  5629. return Status;
  5630. }
  5631. //
  5632. // Do work to prepare for shrinking file if necc.
  5633. //
  5634. if (NewFileSize < Scb->Header.FileSize.QuadPart) {
  5635. Status = NtfsPrepareToShrinkFileSize( IrpContext, FileObject, Scb, NewFileSize );
  5636. if (Status != STATUS_SUCCESS) {
  5637. DebugTrace( -1, Dbg, ("NtfsSetEndOfFileInfo -> %08lx\n", Status) );
  5638. return Status;
  5639. }
  5640. }
  5641. //
  5642. // Check if we really are changing the file size.
  5643. //
  5644. if (Scb->Header.FileSize.QuadPart != NewFileSize) {
  5645. FileSizeChanged = TRUE;
  5646. //
  5647. // Post the FileSize change to the Usn Journal
  5648. //
  5649. NtfsPostUsnChange( IrpContext,
  5650. Scb,
  5651. ((NewFileSize > Scb->Header.FileSize.QuadPart) ?
  5652. USN_REASON_DATA_EXTEND :
  5653. USN_REASON_DATA_TRUNCATION) );
  5654. }
  5655. //
  5656. // It is extremely expensive to make this call on a file that is not
  5657. // cached, and Ntfs has suffered stack overflows in addition to massive
  5658. // time and disk I/O expense (CcZero data on user mapped files!). Therefore,
  5659. // if no one has the file cached, we cache it here to make this call cheaper.
  5660. //
  5661. // Don't create the stream file if called from FsRtlSetFileSize (which sets
  5662. // IRP_PAGING_IO) because mm is in the process of creating a section.
  5663. //
  5664. if (FileSizeChanged &&
  5665. (Scb->NonpagedScb->SegmentObject.DataSectionObject != NULL)) {
  5666. FileIsCached = CcIsFileCached( FileObject );
  5667. if (!FileIsCached &&
  5668. !FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) &&
  5669. !FlagOn( Irp->Flags, IRP_PAGING_IO )) {
  5670. NtfsCreateInternalAttributeStream( IrpContext,
  5671. Scb,
  5672. FALSE,
  5673. &NtfsInternalUseFile[SETENDOFFILEINFO_FILE_NUMBER] );
  5674. FileIsCached = TRUE;
  5675. }
  5676. }
  5677. //
  5678. // If this is a resident attribute we will try to keep it resident.
  5679. //
  5680. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  5681. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  5682. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  5683. if (FileSizeChanged) {
  5684. //
  5685. // If the new file size is larger than a file record then convert
  5686. // to non-resident and use the non-resident code below. Otherwise
  5687. // call ChangeAttributeValue which may also convert to nonresident.
  5688. //
  5689. NtfsInitializeAttributeContext( &AttrContext );
  5690. try {
  5691. NtfsLookupAttributeForScb( IrpContext,
  5692. Scb,
  5693. NULL,
  5694. &AttrContext );
  5695. //
  5696. // If we are growing out of the file record then force the non-resident
  5697. // path. We especially need this for sparse files to make sure it
  5698. // stays either fully allocated or fully deallocated. QuadAlign the new
  5699. // size to handle the close boundary cases.
  5700. //
  5701. FileRecord = NtfsContainingFileRecord( &AttrContext );
  5702. ASSERT( FileRecord->FirstFreeByte > Scb->Header.FileSize.LowPart );
  5703. if ((FileRecord->FirstFreeByte - Scb->Header.FileSize.QuadPart + QuadAlign( NewFileSize )) >=
  5704. Scb->Vcb->BytesPerFileRecordSegment) {
  5705. NtfsConvertToNonresident( IrpContext,
  5706. Fcb,
  5707. NtfsFoundAttribute( &AttrContext ),
  5708. (BOOLEAN) (!FileIsCached),
  5709. &AttrContext );
  5710. } else {
  5711. ULONG AttributeOffset;
  5712. //
  5713. // We are sometimes called by MM during a create section, so
  5714. // for right now the best way we have of detecting a create
  5715. // section is IRP_PAGING_IO being set, as in FsRtlSetFileSizes.
  5716. //
  5717. if ((ULONG) NewFileSize > Scb->Header.FileSize.LowPart) {
  5718. AttributeOffset = Scb->Header.ValidDataLength.LowPart;
  5719. } else {
  5720. AttributeOffset = (ULONG) NewFileSize;
  5721. }
  5722. NtfsChangeAttributeValue( IrpContext,
  5723. Fcb,
  5724. AttributeOffset,
  5725. NULL,
  5726. (ULONG) NewFileSize - AttributeOffset,
  5727. TRUE,
  5728. FALSE,
  5729. (BOOLEAN) (!FileIsCached),
  5730. FALSE,
  5731. &AttrContext );
  5732. Scb->Header.FileSize.QuadPart = NewFileSize;
  5733. //
  5734. // If the file went non-resident, then the allocation size in
  5735. // the Scb is correct. Otherwise we quad-align the new file size.
  5736. //
  5737. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  5738. Scb->Header.AllocationSize.LowPart = QuadAlign( Scb->Header.FileSize.LowPart );
  5739. Scb->Header.ValidDataLength.QuadPart = NewFileSize;
  5740. Scb->TotalAllocated = Scb->Header.AllocationSize.QuadPart;
  5741. #ifdef SYSCACHE_DEBUG
  5742. if (ScbIsBeingLogged( Scb )) {
  5743. FsRtlLogSyscacheEvent( Scb, SCE_VDL_CHANGE, SCE_FLAG_SET_EOF, 0, 0, NewFileSize );
  5744. }
  5745. #endif
  5746. }
  5747. NonResidentPath = FALSE;
  5748. }
  5749. } finally {
  5750. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  5751. }
  5752. } else {
  5753. NonResidentPath = FALSE;
  5754. }
  5755. }
  5756. //
  5757. // We now test if we need to modify the non-resident Eof. We will
  5758. // do this in two cases. Either we're converting from resident in
  5759. // two steps or the attribute was initially non-resident. We can ignore
  5760. // this step if not changing the file size.
  5761. //
  5762. if (NonResidentPath) {
  5763. //
  5764. // Now determine where the new file size lines up with the
  5765. // current file layout. The two cases we need to consider are
  5766. // where the new file size is less than the current file size and
  5767. // valid data length, in which case we need to shrink them.
  5768. // Or we new file size is greater than the current allocation,
  5769. // in which case we need to extend the allocation to match the
  5770. // new file size.
  5771. //
  5772. if (NewFileSize > Scb->Header.AllocationSize.QuadPart) {
  5773. DebugTrace( 0, Dbg, ("Adding allocation to file\n") );
  5774. //
  5775. // Add either the true disk allocation or add a hole for a sparse
  5776. // file.
  5777. //
  5778. if (!FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE )) {
  5779. LONGLONG NewAllocationSize = NewFileSize;
  5780. //
  5781. // If there is a compression unit then we could be in the process of
  5782. // decompressing. Allocate precisely in this case because we don't
  5783. // want to leave any holes. Specifically the user may have truncated
  5784. // the file and is now regenerating it yet the clear compression operation
  5785. // has already passed this point in the file (and dropped all resources).
  5786. // No one will go back to cleanup the allocation if we leave a hole now.
  5787. //
  5788. if (!FlagOn( Scb->ScbState, SCB_STATE_WRITE_COMPRESSED ) &&
  5789. (Scb->CompressionUnit != 0)) {
  5790. ASSERT( FlagOn( Scb->ScbState, SCB_STATE_REALLOCATE_ON_WRITE ));
  5791. NewAllocationSize += Scb->CompressionUnit - 1;
  5792. ((PLARGE_INTEGER) &NewAllocationSize)->LowPart &= ~(Scb->CompressionUnit - 1);
  5793. }
  5794. NtfsAddAllocation( IrpContext,
  5795. FileObject,
  5796. Scb,
  5797. LlClustersFromBytes( Scb->Vcb, Scb->Header.AllocationSize.QuadPart ),
  5798. LlClustersFromBytes(Scb->Vcb, (NewAllocationSize - Scb->Header.AllocationSize.QuadPart)),
  5799. FALSE,
  5800. NULL );
  5801. } else {
  5802. NtfsAddSparseAllocation( IrpContext,
  5803. FileObject,
  5804. Scb,
  5805. Scb->Header.AllocationSize.QuadPart,
  5806. NewFileSize - Scb->Header.AllocationSize.QuadPart );
  5807. }
  5808. } else {
  5809. LONGLONG DeletePoint;
  5810. //
  5811. // If this is a sparse file we actually want to leave a hole between
  5812. // the end of the file and the allocation size.
  5813. //
  5814. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE ) &&
  5815. (NewFileSize < Scb->Header.FileSize.QuadPart) &&
  5816. ((DeletePoint = NewFileSize + Scb->CompressionUnit - 1) < Scb->Header.AllocationSize.QuadPart)) {
  5817. ((PLARGE_INTEGER) &DeletePoint)->LowPart &= ~(Scb->CompressionUnit - 1);
  5818. ASSERT( DeletePoint < Scb->Header.AllocationSize.QuadPart );
  5819. NtfsDeleteAllocation( IrpContext,
  5820. FileObject,
  5821. Scb,
  5822. LlClustersFromBytesTruncate( Scb->Vcb, DeletePoint ),
  5823. LlClustersFromBytesTruncate( Scb->Vcb, Scb->Header.AllocationSize.QuadPart ) - 1,
  5824. TRUE,
  5825. TRUE );
  5826. }
  5827. SetFlag( Scb->ScbState, SCB_STATE_TRUNCATE_ON_CLOSE );
  5828. }
  5829. NewValidDataLength = Scb->Header.ValidDataLength.QuadPart;
  5830. //
  5831. // If this is a paging file, let the whole thing be valid
  5832. // so that we don't end up zeroing pages! Also, make sure
  5833. // we really write this into the file.
  5834. //
  5835. if (FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
  5836. VCN AllocatedVcns;
  5837. AllocatedVcns = Int64ShraMod32(Scb->Header.AllocationSize.QuadPart, Scb->Vcb->ClusterShift);
  5838. Scb->Header.ValidDataLength.QuadPart =
  5839. NewValidDataLength = NewFileSize;
  5840. //
  5841. // If this is the paging file then guarantee that the Mcb is fully loaded.
  5842. //
  5843. NtfsPreloadAllocation( IrpContext, Scb, 0, AllocatedVcns );
  5844. }
  5845. if (NewFileSize < NewValidDataLength) {
  5846. Scb->Header.ValidDataLength.QuadPart =
  5847. NewValidDataLength = NewFileSize;
  5848. #ifdef SYSCACHE_DEBUG
  5849. if (ScbIsBeingLogged( Scb )) {
  5850. FsRtlLogSyscacheEvent( Scb, SCE_VDL_CHANGE, SCE_FLAG_SET_EOF, 0, 0, NewFileSize );
  5851. }
  5852. #endif
  5853. }
  5854. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK ) &&
  5855. (NewFileSize < Scb->ValidDataToDisk)) {
  5856. Scb->ValidDataToDisk = NewFileSize;
  5857. #ifdef SYSCACHE_DEBUG
  5858. if (ScbIsBeingLogged( Scb )) {
  5859. FsRtlLogSyscacheEvent( Scb, SCE_VDD_CHANGE, SCE_FLAG_SET_EOF, 0, 0, NewFileSize );
  5860. }
  5861. #endif
  5862. }
  5863. Scb->Header.FileSize.QuadPart = NewFileSize;
  5864. //
  5865. // Call our common routine to modify the file sizes. We are now
  5866. // done with NewFileSize and NewValidDataLength, and we have
  5867. // PagingIo + main exclusive (so no one can be working on this Scb).
  5868. // NtfsWriteFileSizes uses the sizes in the Scb, and this is the
  5869. // one place where in Ntfs where we wish to use a different value
  5870. // for ValidDataLength. Therefore, we save the current ValidData
  5871. // and plug it with our desired value and restore on return.
  5872. //
  5873. ASSERT( NewFileSize == Scb->Header.FileSize.QuadPart );
  5874. ASSERT( NewValidDataLength == Scb->Header.ValidDataLength.QuadPart );
  5875. NtfsVerifySizes( &Scb->Header );
  5876. NtfsWriteFileSizes( IrpContext,
  5877. Scb,
  5878. &Scb->Header.ValidDataLength.QuadPart,
  5879. BooleanFlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ),
  5880. TRUE,
  5881. TRUE );
  5882. }
  5883. //
  5884. // If the file size changed then mark this file object as having changed the size.
  5885. //
  5886. if (FileSizeChanged) {
  5887. SetFlag( FileObject->Flags, FO_FILE_SIZE_CHANGED );
  5888. }
  5889. //
  5890. // Always mark the data stream as modified.
  5891. //
  5892. if (ARGUMENT_PRESENT( Ccb )) {
  5893. SetFlag( Ccb->Flags,
  5894. (CCB_FLAG_UPDATE_LAST_MODIFY |
  5895. CCB_FLAG_UPDATE_LAST_CHANGE |
  5896. CCB_FLAG_SET_ARCHIVE) );
  5897. } else {
  5898. SetFlag( FileObject->Flags, FO_FILE_MODIFIED );
  5899. }
  5900. //
  5901. // Now capture any file size changes in this file object back to the Fcb.
  5902. //
  5903. NtfsUpdateScbFromFileObject( IrpContext, FileObject, Scb, VcbAcquired );
  5904. //
  5905. // Update the standard information if required.
  5906. //
  5907. if (FlagOn( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO )) {
  5908. NtfsUpdateStandardInformation( IrpContext, Fcb );
  5909. ClearFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  5910. }
  5911. //
  5912. // We know we wrote out any changes to the file size above so clear the
  5913. // flag in the Scb to check the attribute size. This will save us from doing
  5914. // this unnecessarily at cleanup.
  5915. //
  5916. if (FileSizeChanged) {
  5917. ClearFlag( Scb->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE );
  5918. }
  5919. NtfsCheckpointCurrentTransaction( IrpContext );
  5920. //
  5921. // Update duplicated information.
  5922. //
  5923. if (VcbAcquired) {
  5924. NtfsUpdateFileDupInfo( IrpContext, Fcb, Ccb );
  5925. }
  5926. if (CcIsFileCached( FileObject )) {
  5927. //
  5928. // We want to checkpoint the transaction if there is one active.
  5929. //
  5930. if (IrpContext->TransactionId != 0) {
  5931. NtfsCheckpointCurrentTransaction( IrpContext );
  5932. }
  5933. #ifdef SYSCACHE_DEBUG
  5934. if (ScbIsBeingLogged( Scb )) {
  5935. FsRtlLogSyscacheEvent( Scb, SCE_CC_SET_SIZE, SCE_FLAG_SET_EOF, 0, Scb->Header.ValidDataLength.QuadPart, Scb->Header.FileSize.QuadPart );
  5936. }
  5937. #endif
  5938. //
  5939. // Cache map should still exist or we won't purge the data section
  5940. //
  5941. ASSERT( FileObject->SectionObjectPointer->SharedCacheMap != NULL );
  5942. NtfsSetBothCacheSizes( FileObject,
  5943. (PCC_FILE_SIZES)&Scb->Header.AllocationSize,
  5944. Scb );
  5945. //
  5946. // Clear out the write mask on truncates to zero.
  5947. //
  5948. #ifdef SYSCACHE
  5949. if ((Scb->Header.FileSize.QuadPart == 0) && FlagOn(Scb->ScbState, SCB_STATE_SYSCACHE_FILE) &&
  5950. (Scb->ScbType.Data.WriteMask != NULL)) {
  5951. RtlZeroMemory(Scb->ScbType.Data.WriteMask, (((0x2000000) / PAGE_SIZE) / 8));
  5952. }
  5953. #endif
  5954. //
  5955. // Now cleanup the stream we created if there are no more user
  5956. // handles.
  5957. //
  5958. if ((Scb->CleanupCount == 0) && (Scb->FileObject != NULL)) {
  5959. NtfsDeleteInternalAttributeStream( Scb, FALSE, FALSE );
  5960. }
  5961. }
  5962. }
  5963. Status = STATUS_SUCCESS;
  5964. DebugTrace( -1, Dbg, ("NtfsSetEndOfFileInfo -> %08lx\n", Status) );
  5965. return Status;
  5966. }
  5967. //
  5968. // Internal Support Routine
  5969. //
  5970. NTSTATUS
  5971. NtfsSetValidDataLengthInfo (
  5972. IN PIRP_CONTEXT IrpContext,
  5973. IN PIRP Irp,
  5974. IN PSCB Scb,
  5975. IN PCCB Ccb
  5976. )
  5977. /*++
  5978. Routine Description:
  5979. This routine performs the set valid data length information function.
  5980. Notes: we interact with CC but do not initiate caching ourselves. This is
  5981. only possible if the file is not mapped so we can do purges on the section.
  5982. Also the filetype check that restricts this to fileopens only is done in the
  5983. CommonSetInformation call.
  5984. Arguments:
  5985. FileObject - Supplies the file object being processed
  5986. Irp - Supplies the Irp being processed
  5987. Scb - Supplies the Scb for the file/directory being modified
  5988. Ccb - Ccb attached to the file. Contains cached privileges of opener
  5989. Return Value:
  5990. NTSTATUS - The status of the operation
  5991. --*/
  5992. {
  5993. LONGLONG NewValidDataLength;
  5994. LONGLONG NewFileSize;
  5995. PIO_STACK_LOCATION IrpSp;
  5996. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  5997. //
  5998. // User must have manage volume privilege to explicitly tweak the VDL
  5999. //
  6000. if (!FlagOn( Ccb->AccessFlags, MANAGE_VOLUME_ACCESS)) {
  6001. return STATUS_PRIVILEGE_NOT_HELD;
  6002. }
  6003. //
  6004. // We don't support this call for compressed or sparse files
  6005. //
  6006. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE)) {
  6007. return STATUS_INVALID_PARAMETER;
  6008. }
  6009. NewValidDataLength = ((PFILE_VALID_DATA_LENGTH_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->ValidDataLength.QuadPart;
  6010. NewFileSize = Scb->Header.FileSize.QuadPart;
  6011. //
  6012. // VDL can only move forward
  6013. //
  6014. if ((NewValidDataLength < Scb->Header.ValidDataLength.QuadPart) ||
  6015. (NewValidDataLength > NewFileSize) ||
  6016. (NewValidDataLength < 0)) {
  6017. return STATUS_INVALID_PARAMETER;
  6018. }
  6019. //
  6020. // We only have work to do if the file is nonresident.
  6021. //
  6022. if (!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  6023. //
  6024. // We can't change the VDL without being able to purge. This should stay
  6025. // constant since we own everything exclusive
  6026. //
  6027. if (!MmCanFileBeTruncated( &Scb->NonpagedScb->SegmentObject, &Li0 )) {
  6028. return STATUS_USER_MAPPED_FILE;
  6029. }
  6030. NtfsSnapshotScb( IrpContext, Scb );
  6031. //
  6032. // Flush old data out and purge the cache so we can see new data
  6033. //
  6034. NtfsFlushAndPurgeScb( IrpContext, Scb, NULL );
  6035. //
  6036. // update the scb
  6037. //
  6038. Scb->Header.ValidDataLength.QuadPart = NewValidDataLength;
  6039. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  6040. Scb->ValidDataToDisk = NewValidDataLength;
  6041. }
  6042. #ifdef SYSCACHE_DEBUG
  6043. if (ScbIsBeingLogged( Scb )) {
  6044. FsRtlLogSyscacheEvent( Scb, SCE_VDL_CHANGE, SCE_FLAG_SET_VDL, 0, 0, NewValidDataLength );
  6045. }
  6046. #endif
  6047. ASSERT( IrpContext->CleanupStructure != NULL );
  6048. NtfsWriteFileSizes( IrpContext,
  6049. Scb,
  6050. &NewValidDataLength,
  6051. TRUE,
  6052. TRUE,
  6053. TRUE );
  6054. //
  6055. // Now capture any file size changes in this file object back to the Fcb.
  6056. //
  6057. NtfsUpdateScbFromFileObject( IrpContext, IrpSp->FileObject, Scb, FALSE );
  6058. //
  6059. // Inform CC of the new values
  6060. //
  6061. NtfsSetBothCacheSizes( IrpSp->FileObject,
  6062. (PCC_FILE_SIZES)&Scb->Header.AllocationSize,
  6063. Scb );
  6064. //
  6065. // We know the file size for this Scb is now correct on disk.
  6066. //
  6067. NtfsAcquireFsrtlHeader( Scb );
  6068. ClearFlag( Scb->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE );
  6069. NtfsReleaseFsrtlHeader( Scb );
  6070. //
  6071. // Post a usn record
  6072. //
  6073. NtfsPostUsnChange( IrpContext, Scb, USN_REASON_DATA_OVERWRITE );
  6074. NtfsWriteUsnJournalChanges( IrpContext );
  6075. }
  6076. return STATUS_SUCCESS;
  6077. }
  6078. //
  6079. // Local support routine
  6080. //
  6081. NTSTATUS
  6082. NtfsCheckScbForLinkRemoval (
  6083. IN PSCB Scb,
  6084. OUT PSCB *BatchOplockScb,
  6085. OUT PULONG BatchOplockCount
  6086. )
  6087. /*++
  6088. Routine Description:
  6089. This routine is called to check if a link to an open Scb may be
  6090. removed for rename. We walk through all the children and
  6091. verify that they have no user opens.
  6092. Arguments:
  6093. Scb - Scb whose children are to be examined.
  6094. BatchOplockScb - Address to store Scb which may have a batch oplock.
  6095. BatchOplockCount - Number of files which have batch oplocks on this
  6096. pass through the directory tree.
  6097. Return Value:
  6098. NTSTATUS - STATUS_SUCCESS if the link can be removed,
  6099. STATUS_ACCESS_DENIED otherwise.
  6100. --*/
  6101. {
  6102. NTSTATUS Status = STATUS_SUCCESS;
  6103. PSCB NextScb;
  6104. PAGED_CODE();
  6105. DebugTrace( +1, Dbg, ("NtfsCheckScbForLinkRemoval: Entered\n") );
  6106. //
  6107. // Initialize the batch oplock state.
  6108. //
  6109. *BatchOplockCount = 0;
  6110. *BatchOplockScb = NULL;
  6111. //
  6112. // If this is a directory file and we are removing a link,
  6113. // we need to examine its descendents. We may not remove a link which
  6114. // may be an ancestor path component of any open file.
  6115. //
  6116. //
  6117. // First look for any descendents with a non-zero unclean count.
  6118. //
  6119. NextScb = Scb;
  6120. while ((NextScb = NtfsGetNextScb( NextScb, Scb )) != NULL) {
  6121. //
  6122. // Stop if there are open handles. If there is a batch oplock on
  6123. // this file then we will try to break the batch oplock. In this
  6124. // pass we will just count the number of files with batch oplocks
  6125. // and remember the first one we encounter.
  6126. //
  6127. // Skip over the Scb's with a zero cleanup count as we would otherwise
  6128. // fail this if we encounter them.
  6129. //
  6130. if (NextScb->CleanupCount != 0) {
  6131. if ((NextScb->AttributeTypeCode == $DATA) &&
  6132. (NextScb->Header.NodeTypeCode == NTFS_NTC_SCB_DATA) &&
  6133. FsRtlCurrentBatchOplock( &NextScb->ScbType.Data.Oplock )) {
  6134. *BatchOplockCount += 1;
  6135. if (*BatchOplockScb == NULL) {
  6136. *BatchOplockScb = NextScb;
  6137. Status = STATUS_PENDING;
  6138. }
  6139. } else {
  6140. Status = STATUS_ACCESS_DENIED;
  6141. DebugTrace( 0, Dbg, ("NtfsCheckScbForLinkRemoval: Directory to rename has open children\n") );
  6142. break;
  6143. }
  6144. }
  6145. }
  6146. //
  6147. //
  6148. // We know there are no opens below this point. We will remove any prefix
  6149. // entries later.
  6150. //
  6151. DebugTrace( -1, Dbg, ("NtfsCheckScbForLinkRemoval: Exit -> %08lx\n") );
  6152. return Status;
  6153. }
  6154. //
  6155. // Local support routine
  6156. //
  6157. VOID
  6158. NtfsFindTargetElements (
  6159. IN PIRP_CONTEXT IrpContext,
  6160. IN PFILE_OBJECT TargetFileObject,
  6161. IN PSCB ParentScb,
  6162. OUT PSCB *TargetParentScb,
  6163. OUT PUNICODE_STRING FullTargetFileName,
  6164. OUT PUNICODE_STRING TargetFileName
  6165. )
  6166. /*++
  6167. Routine Description:
  6168. This routine determines the target directory for the rename and the
  6169. target link name. If these is a target file object, we use that to
  6170. find the target. Otherwise the target is the same directory as the
  6171. source.
  6172. Arguments:
  6173. TargetFileObject - This is the file object which describes the target
  6174. for the link operation.
  6175. ParentScb - This is current directory for the link.
  6176. TargetParentScb - This is the location to store the parent of the target.
  6177. FullTargetFileName - This is a pointer to a unicode string which will point
  6178. to the name from the root. We clear this if there is no full name
  6179. available.
  6180. TargetFileName - This is a pointer to a unicode string which will point to
  6181. the target name on exit.
  6182. Return Value:
  6183. BOOLEAN - TRUE if there is no work to do, FALSE otherwise.
  6184. --*/
  6185. {
  6186. PAGED_CODE();
  6187. DebugTrace( +1, Dbg, ("NtfsFindTargetElements: Entered\n") );
  6188. //
  6189. // We need to find the target parent directory, target file and target
  6190. // name for the new link. These three pieces of information allow
  6191. // us to see if the link already exists.
  6192. //
  6193. // Check if we have a file object for the target.
  6194. //
  6195. if (TargetFileObject != NULL) {
  6196. PVCB TargetVcb;
  6197. PFCB TargetFcb;
  6198. PCCB TargetCcb;
  6199. USHORT PreviousLength;
  6200. USHORT LastFileNameOffset;
  6201. //
  6202. // The target directory is given by the TargetFileObject.
  6203. // The name for the link is contained in the TargetFileObject.
  6204. //
  6205. // The target must be a user directory and must be on the
  6206. // current Vcb.
  6207. //
  6208. if ((NtfsDecodeFileObject( IrpContext,
  6209. TargetFileObject,
  6210. &TargetVcb,
  6211. &TargetFcb,
  6212. TargetParentScb,
  6213. &TargetCcb,
  6214. TRUE ) != UserDirectoryOpen) ||
  6215. ((ParentScb != NULL) &&
  6216. (TargetVcb != ParentScb->Vcb))) {
  6217. DebugTrace( -1, Dbg, ("NtfsFindTargetElements: Target file object is invalid\n") );
  6218. NtfsRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER, NULL, NULL );
  6219. }
  6220. //
  6221. // Temporarily set the file name to point to the full buffer.
  6222. //
  6223. LastFileNameOffset = PreviousLength = TargetFileObject->FileName.Length;
  6224. TargetFileObject->FileName.Length = TargetFileObject->FileName.MaximumLength;
  6225. *FullTargetFileName = TargetFileObject->FileName;
  6226. //
  6227. // If the first character at the final component is a backslash, move the
  6228. // offset ahead by 2.
  6229. //
  6230. if (TargetFileObject->FileName.Buffer[LastFileNameOffset / sizeof( WCHAR )] == L'\\') {
  6231. LastFileNameOffset += sizeof( WCHAR );
  6232. }
  6233. NtfsBuildLastFileName( IrpContext,
  6234. TargetFileObject,
  6235. LastFileNameOffset,
  6236. TargetFileName );
  6237. //
  6238. // Restore the file object length.
  6239. //
  6240. TargetFileObject->FileName.Length = PreviousLength;
  6241. //
  6242. // Otherwise the rename occurs in the current directory. The directory
  6243. // is the parent of this Fcb, the name is stored in a Rename buffer.
  6244. //
  6245. } else {
  6246. PFILE_RENAME_INFORMATION Buffer;
  6247. Buffer = IrpContext->OriginatingIrp->AssociatedIrp.SystemBuffer;
  6248. *TargetParentScb = ParentScb;
  6249. TargetFileName->MaximumLength =
  6250. TargetFileName->Length = (USHORT)Buffer->FileNameLength;
  6251. TargetFileName->Buffer = (PWSTR) &Buffer->FileName;
  6252. FullTargetFileName->Length =
  6253. FullTargetFileName->MaximumLength = 0;
  6254. FullTargetFileName->Buffer = NULL;
  6255. }
  6256. DebugTrace( -1, Dbg, ("NtfsFindTargetElements: Exit\n") );
  6257. return;
  6258. }
  6259. BOOLEAN
  6260. NtfsCheckLinkForNewLink (
  6261. IN PFCB Fcb,
  6262. IN PFILE_NAME FileNameAttr,
  6263. IN FILE_REFERENCE FileReference,
  6264. IN PUNICODE_STRING NewLinkName,
  6265. OUT PULONG LinkFlags
  6266. )
  6267. /*++
  6268. Routine Description:
  6269. This routine checks the source and target directories and files.
  6270. It determines whether the target link needs to be removed and
  6271. whether the target link spans the same parent and file as the
  6272. source link. This routine may determine that there
  6273. is absolutely no work remaining for this link operation. This is true
  6274. if the desired link already exists.
  6275. Arguments:
  6276. Fcb - This is the Fcb for the link which is being renamed.
  6277. FileNameAttr - This is the file name attribute for the matching link
  6278. on the disk.
  6279. FileReference - This is the file reference for the matching link found.
  6280. NewLinkName - This is the name to use for the rename.
  6281. LinkFlags - Address of flags field to store whether the source link and target
  6282. link traverse the same directory and file.
  6283. Return Value:
  6284. BOOLEAN - TRUE if there is no work to do, FALSE otherwise.
  6285. --*/
  6286. {
  6287. BOOLEAN NoWorkToDo = FALSE;
  6288. PAGED_CODE();
  6289. DebugTrace( +1, Dbg, ("NtfsCheckLinkForNewLink: Entered\n") );
  6290. //
  6291. // Check if the file references match.
  6292. //
  6293. if (NtfsEqualMftRef( &FileReference, &Fcb->FileReference )) {
  6294. SetFlag( *LinkFlags, TRAVERSE_MATCH );
  6295. }
  6296. //
  6297. // We need to determine if we have an exact match for the link names.
  6298. //
  6299. if (RtlEqualMemory( FileNameAttr->FileName,
  6300. NewLinkName->Buffer,
  6301. NewLinkName->Length )) {
  6302. SetFlag( *LinkFlags, EXACT_CASE_MATCH );
  6303. }
  6304. //
  6305. // We now have to decide whether we will be removing the target link.
  6306. // The following conditions must hold for us to preserve the target link.
  6307. //
  6308. // 1 - The target link connects the same directory to the same file.
  6309. //
  6310. // 2 - The names are an exact case match.
  6311. //
  6312. if (FlagOn( *LinkFlags, TRAVERSE_MATCH | EXACT_CASE_MATCH ) == (TRAVERSE_MATCH | EXACT_CASE_MATCH)) {
  6313. NoWorkToDo = TRUE;
  6314. }
  6315. DebugTrace( -1, Dbg, ("NtfsCheckLinkForNewLink: Exit\n") );
  6316. return NoWorkToDo;
  6317. }
  6318. //
  6319. // Local support routine
  6320. //
  6321. VOID
  6322. NtfsCheckLinkForRename (
  6323. IN PFCB Fcb,
  6324. IN PLCB Lcb,
  6325. IN PFILE_NAME FileNameAttr,
  6326. IN FILE_REFERENCE FileReference,
  6327. IN PUNICODE_STRING TargetFileName,
  6328. IN BOOLEAN IgnoreCase,
  6329. IN OUT PULONG RenameFlags
  6330. )
  6331. /*++
  6332. Routine Description:
  6333. This routine checks the source and target directories and files.
  6334. It determines whether the target link needs to be removed and
  6335. whether the target link spans the same parent and file as the
  6336. source link. We also determine if the new link name is an exact case
  6337. match for the existing link name. The booleans indicating which links
  6338. to remove or add have already been initialized to the default values.
  6339. Arguments:
  6340. Fcb - This is the Fcb for the link which is being renamed.
  6341. Lcb - This is the link being renamed.
  6342. FileNameAttr - This is the file name attribute for the matching link
  6343. on the disk.
  6344. FileReference - This is the file reference for the matching link found.
  6345. TargetFileName - This is the name to use for the rename.
  6346. IgnoreCase - Indicates if the user is case sensitive.
  6347. RenameFlags - Flag field which indicates which updates to perform.
  6348. Return Value:
  6349. None.
  6350. --*/
  6351. {
  6352. PAGED_CODE();
  6353. DebugTrace( +1, Dbg, ("NtfsCheckLinkForRename: Entered\n") );
  6354. //
  6355. // Check if the file references match.
  6356. //
  6357. if (NtfsEqualMftRef( &FileReference, &Fcb->FileReference )) {
  6358. SetFlag( *RenameFlags, TRAVERSE_MATCH );
  6359. }
  6360. //
  6361. // We need to determine if we have an exact match between the desired name
  6362. // and the current name for the link. We already know the length are the same.
  6363. //
  6364. if (RtlEqualMemory( FileNameAttr->FileName,
  6365. TargetFileName->Buffer,
  6366. TargetFileName->Length )) {
  6367. SetFlag( *RenameFlags, EXACT_CASE_MATCH );
  6368. }
  6369. //
  6370. // If this is a traverse match (meaning the desired link and the link
  6371. // being replaced connect the same directory to the same file) we check
  6372. // if we can leave the link on the file.
  6373. //
  6374. // At the end of the rename, there must be an Ntfs name or hard link
  6375. // which matches the target name exactly.
  6376. //
  6377. if (FlagOn( *RenameFlags, TRAVERSE_MATCH )) {
  6378. //
  6379. // If we are in the same directory and are renaming between Ntfs and Dos
  6380. // links then don't remove the link twice.
  6381. //
  6382. if (!FlagOn( *RenameFlags, MOVE_TO_NEW_DIR )) {
  6383. //
  6384. // If We are renaming from between primary links then don't remove the
  6385. // source. It is removed with the target.
  6386. //
  6387. if ((Lcb->FileNameAttr->Flags != 0) && (FileNameAttr->Flags != 0)) {
  6388. ClearFlag( *RenameFlags, ACTIVELY_REMOVE_SOURCE_LINK );
  6389. SetFlag( *RenameFlags, OVERWRITE_SOURCE_LINK );
  6390. //
  6391. // If this is an exact case match then don't remove the source at all.
  6392. //
  6393. if (FlagOn( *RenameFlags, EXACT_CASE_MATCH )) {
  6394. ClearFlag( *RenameFlags, REMOVE_SOURCE_LINK );
  6395. }
  6396. //
  6397. // If we are changing the case of a link only, then don't remove the link twice.
  6398. //
  6399. } else if (RtlEqualMemory( Lcb->ExactCaseLink.LinkName.Buffer,
  6400. FileNameAttr->FileName,
  6401. Lcb->ExactCaseLink.LinkName.Length )) {
  6402. SetFlag( *RenameFlags, OVERWRITE_SOURCE_LINK );
  6403. ClearFlag( *RenameFlags, ACTIVELY_REMOVE_SOURCE_LINK );
  6404. }
  6405. }
  6406. //
  6407. // If the names match exactly we can reuse the links if we don't have a
  6408. // conflict with the name flags.
  6409. //
  6410. if (FlagOn( *RenameFlags, EXACT_CASE_MATCH ) &&
  6411. (FlagOn( *RenameFlags, OVERWRITE_SOURCE_LINK ) ||
  6412. !IgnoreCase ||
  6413. !FlagOn( Lcb->FileNameAttr->Flags, FILE_NAME_DOS | FILE_NAME_NTFS ))) {
  6414. //
  6415. // Otherwise we are renaming hard links or this is a Posix opener.
  6416. //
  6417. ClearFlag( *RenameFlags, REMOVE_TARGET_LINK | ADD_TARGET_LINK );
  6418. }
  6419. }
  6420. //
  6421. // The non-traverse case is already initialized.
  6422. //
  6423. DebugTrace( -1, Dbg, ("NtfsCheckLinkForRename: Exit\n") );
  6424. return;
  6425. }
  6426. //
  6427. // Local support routine
  6428. //
  6429. VOID
  6430. NtfsCleanupLinkForRemoval (
  6431. IN PFCB PreviousFcb,
  6432. IN PSCB ParentScb,
  6433. IN BOOLEAN ExistingFcb
  6434. )
  6435. /*++
  6436. Routine Description:
  6437. This routine does the cleanup on a file/link which is the target
  6438. of either a rename or set link operation.
  6439. Arguments:
  6440. PreviousFcb - Address to store the Fcb for the file whose link is
  6441. being removed.
  6442. ParentScb - This is the parent for the link being removed.
  6443. ExistingFcb - Address to store whether this Fcb already existed.
  6444. Return Value:
  6445. None
  6446. --*/
  6447. {
  6448. PAGED_CODE();
  6449. DebugTrace( +1, Dbg, ("NtfsCleanupLinkForRemoval: Entered\n") );
  6450. //
  6451. // If the Fcb existed, we remove all of the prefix entries for it which
  6452. // belong to the parent we are given.
  6453. //
  6454. if (ExistingFcb) {
  6455. PLIST_ENTRY Links;
  6456. PLCB ThisLcb;
  6457. for (Links = PreviousFcb->LcbQueue.Flink;
  6458. Links != &PreviousFcb->LcbQueue;
  6459. Links = Links->Flink ) {
  6460. ThisLcb = CONTAINING_RECORD( Links,
  6461. LCB,
  6462. FcbLinks );
  6463. if (ThisLcb->Scb == ParentScb) {
  6464. ASSERT( NtfsIsExclusiveScb( ThisLcb->Scb ) );
  6465. NtfsRemovePrefix( ThisLcb );
  6466. }
  6467. //
  6468. // Remove any hash table entries for this Lcb.
  6469. //
  6470. NtfsRemoveHashEntriesForLcb( ThisLcb );
  6471. } // End for each Lcb of Fcb
  6472. }
  6473. DebugTrace( -1, Dbg, ("NtfsCleanupLinkForRemoval: Exit\n") );
  6474. return;
  6475. }
  6476. //
  6477. // Local support routine
  6478. //
  6479. VOID
  6480. NtfsUpdateFcbFromLinkRemoval (
  6481. IN PIRP_CONTEXT IrpContext,
  6482. IN PSCB ParentScb,
  6483. IN PFCB Fcb,
  6484. IN UNICODE_STRING FileName,
  6485. IN UCHAR FileNameFlags
  6486. )
  6487. /*++
  6488. Routine Description:
  6489. This routine is called to update the in-memory part of a link which
  6490. has been removed from a file. We find the Lcb's for the links and
  6491. mark them as deleted and removed.
  6492. Arguments:
  6493. ParentScb - Scb for the directory the was removed from.
  6494. ParentScb - This is the Scb for the new directory.
  6495. Fcb - The Fcb for the file whose link is being renamed.
  6496. FileName - File name for link being removed.
  6497. FileNameFlags - File name flags for link being removed.
  6498. Return Value:
  6499. None.
  6500. --*/
  6501. {
  6502. PLCB Lcb;
  6503. PLCB SplitPrimaryLcb;
  6504. PAGED_CODE();
  6505. DebugTrace( +1, Dbg, ("NtfsUpdateFcbFromLinkRemoval: Entered\n") );
  6506. SplitPrimaryLcb = NULL;
  6507. //
  6508. // Find the Lcb for the link which was removed.
  6509. //
  6510. Lcb = NtfsCreateLcb( IrpContext,
  6511. ParentScb,
  6512. Fcb,
  6513. FileName,
  6514. FileNameFlags,
  6515. NULL );
  6516. //
  6517. // If this is a split primary, we need to find the name flags for
  6518. // the Lcb.
  6519. //
  6520. if (LcbSplitPrimaryLink( Lcb )) {
  6521. SplitPrimaryLcb = NtfsLookupLcbByFlags( Fcb,
  6522. (UCHAR) LcbSplitPrimaryComplement( Lcb ));
  6523. }
  6524. //
  6525. // Mark any Lcb's we have as deleted and removed.
  6526. //
  6527. SetFlag( Lcb->LcbState, (LCB_STATE_DELETE_ON_CLOSE | LCB_STATE_LINK_IS_GONE) );
  6528. if (SplitPrimaryLcb) {
  6529. SetFlag( SplitPrimaryLcb->LcbState,
  6530. (LCB_STATE_DELETE_ON_CLOSE | LCB_STATE_LINK_IS_GONE) );
  6531. }
  6532. DebugTrace( -1, Dbg, ("NtfsUpdateFcbFromLinkRemoval: Exit\n") );
  6533. return;
  6534. }
  6535. //
  6536. // Local support routine
  6537. //
  6538. VOID
  6539. NtfsReplaceLinkInDir (
  6540. IN PIRP_CONTEXT IrpContext,
  6541. IN PSCB ParentScb,
  6542. IN PFCB Fcb,
  6543. IN PUNICODE_STRING NewLinkName,
  6544. IN UCHAR FileNameFlags,
  6545. IN PUNICODE_STRING PrevLinkName,
  6546. IN UCHAR PrevLinkNameFlags
  6547. )
  6548. /*++
  6549. Routine Description:
  6550. This routine is called to create the in-memory part of a link in a new
  6551. directory.
  6552. Arguments:
  6553. ParentScb - Scb for the directory the link is being created in.
  6554. Fcb - The Fcb for the file whose link is being created.
  6555. NewLinkName - Name for the new component.
  6556. FileNameFlags - These are the flags to use for the new link.
  6557. PrevLinkName - File name for link being removed.
  6558. PrevLinkNameFlags - File name flags for link being removed.
  6559. Return Value:
  6560. None.
  6561. --*/
  6562. {
  6563. PLCB TraverseLcb;
  6564. PLCB SplitPrimaryLcb = NULL;
  6565. PAGED_CODE();
  6566. DebugTrace( +1, Dbg, ("NtfsCreateLinkInNewDir: Entered\n") );
  6567. SplitPrimaryLcb = NULL;
  6568. //
  6569. // Build the name for the traverse link and call strucsup to
  6570. // give us an Lcb.
  6571. //
  6572. TraverseLcb = NtfsCreateLcb( IrpContext,
  6573. ParentScb,
  6574. Fcb,
  6575. *PrevLinkName,
  6576. PrevLinkNameFlags,
  6577. NULL );
  6578. //
  6579. // If this is a split primary, we need to find the name flags for
  6580. // the Lcb.
  6581. //
  6582. if (LcbSplitPrimaryLink( TraverseLcb )) {
  6583. SplitPrimaryLcb = NtfsLookupLcbByFlags( Fcb,
  6584. (UCHAR) LcbSplitPrimaryComplement( TraverseLcb ));
  6585. }
  6586. //
  6587. // We now need only to rename and combine any existing Lcb's.
  6588. //
  6589. NtfsRenameLcb( IrpContext,
  6590. TraverseLcb,
  6591. NewLinkName,
  6592. FileNameFlags,
  6593. FALSE );
  6594. if (SplitPrimaryLcb != NULL) {
  6595. NtfsRenameLcb( IrpContext,
  6596. SplitPrimaryLcb,
  6597. NewLinkName,
  6598. FileNameFlags,
  6599. FALSE );
  6600. NtfsCombineLcbs( IrpContext,
  6601. TraverseLcb,
  6602. SplitPrimaryLcb );
  6603. NtfsDeleteLcb( IrpContext, &SplitPrimaryLcb );
  6604. }
  6605. DebugTrace( -1, Dbg, ("NtfsCreateLinkInNewDir: Exit\n") );
  6606. return;
  6607. }
  6608. //
  6609. // Local support routine.
  6610. //
  6611. VOID
  6612. NtfsMoveLinkToNewDir (
  6613. IN PIRP_CONTEXT IrpContext,
  6614. IN PUNICODE_STRING NewFullLinkName,
  6615. IN PUNICODE_STRING NewLinkName,
  6616. IN UCHAR NewLinkNameFlags,
  6617. IN PSCB ParentScb,
  6618. IN PFCB Fcb,
  6619. IN OUT PLCB Lcb,
  6620. IN ULONG RenameFlags,
  6621. IN PUNICODE_STRING PrevLinkName,
  6622. IN UCHAR PrevLinkNameFlags
  6623. )
  6624. /*++
  6625. Routine Description:
  6626. This routine is called to move the in-memory part of a link to a new
  6627. directory. We move the link involved and its primary link partner if
  6628. it exists.
  6629. Arguments:
  6630. NewFullLinkName - This is the full name for the new link from the root.
  6631. NewLinkName - This is the last component name only.
  6632. NewLinkNameFlags - These are the flags to use for the new link.
  6633. ParentScb - This is the Scb for the new directory.
  6634. Fcb - The Fcb for the file whose link is being renamed.
  6635. Lcb - This is the Lcb which is the base of the rename.
  6636. RenameFlags - Flag field indicating the type of operations to perform
  6637. on file name links.
  6638. PrevLinkName - File name for link being removed. Only meaningful here
  6639. if this is a traverse match and there are remaining Lcbs for the
  6640. previous link.
  6641. PrevLinkNameFlags - File name flags for link being removed.
  6642. Return Value:
  6643. None.
  6644. --*/
  6645. {
  6646. PLCB TraverseLcb = NULL;
  6647. PLCB SplitPrimaryLcb = NULL;
  6648. BOOLEAN SplitSourceLcb = FALSE;
  6649. UNICODE_STRING TargetDirectoryName;
  6650. UNICODE_STRING SplitLinkName;
  6651. UCHAR SplitLinkNameFlags = NewLinkNameFlags;
  6652. BOOLEAN Found;
  6653. PFILE_NAME FileName;
  6654. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  6655. BOOLEAN CleanupAttrContext = FALSE;
  6656. ULONG Pass;
  6657. BOOLEAN CheckBufferOnly;
  6658. PAGED_CODE();
  6659. DebugTrace( +1, Dbg, ("NtfsMoveLinkToNewDir: Entered\n") );
  6660. //
  6661. // Use a try-finally to perform cleanup.
  6662. //
  6663. try {
  6664. //
  6665. // Construct the unicode string for the parent directory.
  6666. //
  6667. TargetDirectoryName = *NewFullLinkName;
  6668. TargetDirectoryName.Length -= NewLinkName->Length;
  6669. if (TargetDirectoryName.Length > sizeof( WCHAR )) {
  6670. TargetDirectoryName.Length -= sizeof( WCHAR );
  6671. }
  6672. // If the link being moved is a split primary link, we need to find
  6673. // its other half.
  6674. //
  6675. if (LcbSplitPrimaryLink( Lcb )) {
  6676. SplitPrimaryLcb = NtfsLookupLcbByFlags( Fcb,
  6677. (UCHAR) LcbSplitPrimaryComplement( Lcb ));
  6678. SplitSourceLcb = TRUE;
  6679. //
  6680. // If we found an existing Lcb we have to update its name as well. We may be
  6681. // able to use the new name used for the Lcb passed in. However we must check
  6682. // that we don't overwrite a DOS name with an NTFS only name.
  6683. //
  6684. if (SplitPrimaryLcb &&
  6685. (SplitPrimaryLcb->FileNameAttr->Flags == FILE_NAME_DOS) &&
  6686. (NewLinkNameFlags == FILE_NAME_NTFS)) {
  6687. //
  6688. // Lookup the dos only name on disk.
  6689. //
  6690. NtfsInitializeAttributeContext( &AttrContext );
  6691. CleanupAttrContext = TRUE;
  6692. //
  6693. // Walk through the names for this entry. There better
  6694. // be one which is not a DOS-only name.
  6695. //
  6696. Found = NtfsLookupAttributeByCode( IrpContext,
  6697. Fcb,
  6698. &Fcb->FileReference,
  6699. $FILE_NAME,
  6700. &AttrContext );
  6701. while (Found) {
  6702. FileName = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  6703. if (FileName->Flags == FILE_NAME_DOS) { break; }
  6704. Found = NtfsLookupNextAttributeByCode( IrpContext,
  6705. Fcb,
  6706. $FILE_NAME,
  6707. &AttrContext );
  6708. }
  6709. //
  6710. // We should have found the entry.
  6711. //
  6712. if (!Found) {
  6713. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  6714. }
  6715. //
  6716. // Now build the component name.
  6717. //
  6718. SplitLinkName.Buffer = FileName->FileName;
  6719. SplitLinkName.MaximumLength =
  6720. SplitLinkName.Length = FileName->FileNameLength * sizeof( WCHAR );
  6721. SplitLinkNameFlags = FILE_NAME_DOS;
  6722. } else {
  6723. SplitLinkName = *NewLinkName;
  6724. }
  6725. }
  6726. //
  6727. // If we removed or reused a traverse link, we need to check if there is
  6728. // an Lcb for it.
  6729. //
  6730. if (FlagOn( RenameFlags, REMOVE_TRAVERSE_LINK | REUSE_TRAVERSE_LINK )) {
  6731. //
  6732. // Build the name for the traverse link and call strucsup to
  6733. // give us an Lcb.
  6734. //
  6735. if (FlagOn( RenameFlags, EXACT_CASE_MATCH )) {
  6736. TraverseLcb = NtfsCreateLcb( IrpContext,
  6737. ParentScb,
  6738. Fcb,
  6739. *NewLinkName,
  6740. PrevLinkNameFlags,
  6741. NULL );
  6742. } else {
  6743. TraverseLcb = NtfsCreateLcb( IrpContext,
  6744. ParentScb,
  6745. Fcb,
  6746. *PrevLinkName,
  6747. PrevLinkNameFlags,
  6748. NULL );
  6749. }
  6750. if (FlagOn( RenameFlags, REMOVE_TRAVERSE_LINK )) {
  6751. //
  6752. // If this is a split primary, we need to find the name flags for
  6753. // the Lcb.
  6754. //
  6755. if (LcbSplitPrimaryLink( TraverseLcb )) {
  6756. SplitPrimaryLcb = NtfsLookupLcbByFlags( Fcb,
  6757. (UCHAR) LcbSplitPrimaryComplement( TraverseLcb ));
  6758. }
  6759. }
  6760. }
  6761. //
  6762. // Now move and combine the Lcbs. We will do this in two passes. One will allocate buffers
  6763. // of sufficient size. The other will store the names in.
  6764. //
  6765. Pass = 0;
  6766. CheckBufferOnly = TRUE;
  6767. do {
  6768. //
  6769. // Start with the Lcb used for the rename.
  6770. //
  6771. NtfsMoveLcb( IrpContext,
  6772. Lcb,
  6773. ParentScb,
  6774. Fcb,
  6775. &TargetDirectoryName,
  6776. NewLinkName,
  6777. NewLinkNameFlags,
  6778. CheckBufferOnly );
  6779. //
  6780. // Next do the split primary if from the source file or the target.
  6781. //
  6782. if (SplitPrimaryLcb && SplitSourceLcb) {
  6783. NtfsMoveLcb( IrpContext,
  6784. SplitPrimaryLcb,
  6785. ParentScb,
  6786. Fcb,
  6787. &TargetDirectoryName,
  6788. &SplitLinkName,
  6789. SplitLinkNameFlags,
  6790. CheckBufferOnly );
  6791. //
  6792. // If we are in the second pass then optionally combine these
  6793. // Lcb's and delete the split.
  6794. //
  6795. if ((SplitLinkNameFlags == NewLinkNameFlags) && !CheckBufferOnly) {
  6796. NtfsCombineLcbs( IrpContext, Lcb, SplitPrimaryLcb );
  6797. NtfsDeleteLcb( IrpContext, &SplitPrimaryLcb );
  6798. }
  6799. }
  6800. //
  6801. // If we have a traverse link and are in the second pass then combine
  6802. // with the primary Lcb.
  6803. //
  6804. if (!CheckBufferOnly) {
  6805. if (TraverseLcb != NULL) {
  6806. if (!FlagOn( RenameFlags, REUSE_TRAVERSE_LINK )) {
  6807. NtfsRenameLcb( IrpContext,
  6808. TraverseLcb,
  6809. NewLinkName,
  6810. NewLinkNameFlags,
  6811. CheckBufferOnly );
  6812. if (SplitPrimaryLcb && !SplitSourceLcb) {
  6813. NtfsRenameLcb( IrpContext,
  6814. SplitPrimaryLcb,
  6815. NewLinkName,
  6816. NewLinkNameFlags,
  6817. CheckBufferOnly );
  6818. //
  6819. // If we are in the second pass then optionally combine these
  6820. // Lcb's and delete the split.
  6821. //
  6822. if (!CheckBufferOnly) {
  6823. NtfsCombineLcbs( IrpContext, Lcb, SplitPrimaryLcb );
  6824. NtfsDeleteLcb( IrpContext, &SplitPrimaryLcb );
  6825. }
  6826. }
  6827. }
  6828. NtfsCombineLcbs( IrpContext,
  6829. Lcb,
  6830. TraverseLcb );
  6831. NtfsDeleteLcb( IrpContext, &TraverseLcb );
  6832. }
  6833. }
  6834. Pass += 1;
  6835. CheckBufferOnly = FALSE;
  6836. } while (Pass < 2);
  6837. } finally {
  6838. if (CleanupAttrContext) {
  6839. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  6840. }
  6841. }
  6842. DebugTrace( -1, Dbg, ("NtfsMoveLinkToNewDir: Exit\n") );
  6843. return;
  6844. }
  6845. //
  6846. // Local support routine.
  6847. //
  6848. VOID
  6849. NtfsCreateLinkInSameDir (
  6850. IN PIRP_CONTEXT IrpContext,
  6851. IN PSCB ParentScb,
  6852. IN PFCB Fcb,
  6853. IN UNICODE_STRING NewLinkName,
  6854. IN UCHAR NewFileNameFlags,
  6855. IN UNICODE_STRING PrevLinkName,
  6856. IN UCHAR PrevLinkNameFlags
  6857. )
  6858. /*++
  6859. Routine Description:
  6860. This routine is called when we are replacing a link in a single directory.
  6861. We need to find the link being renamed and any auxilary links and
  6862. then give them their new names.
  6863. Arguments:
  6864. ParentScb - Scb for the directory the rename is taking place in.
  6865. Fcb - The Fcb for the file whose link is being renamed.
  6866. NewLinkName - This is the name to use for the new link.
  6867. NewFileNameFlags - These are the flags to use for the new link.
  6868. PrevLinkName - File name for link being removed.
  6869. PrevLinkNameFlags - File name flags for link being removed.
  6870. Return Value:
  6871. None.
  6872. --*/
  6873. {
  6874. PLCB TraverseLcb;
  6875. PLCB SplitPrimaryLcb;
  6876. PAGED_CODE();
  6877. DebugTrace( +1, Dbg, ("NtfsCreateLinkInSameDir: Entered\n") );
  6878. //
  6879. // Initialize our local variables.
  6880. //
  6881. SplitPrimaryLcb = NULL;
  6882. TraverseLcb = NtfsCreateLcb( IrpContext,
  6883. ParentScb,
  6884. Fcb,
  6885. PrevLinkName,
  6886. PrevLinkNameFlags,
  6887. NULL );
  6888. //
  6889. // If this is a split primary, we need to find the name flags for
  6890. // the Lcb.
  6891. //
  6892. if (LcbSplitPrimaryLink( TraverseLcb )) {
  6893. SplitPrimaryLcb = NtfsLookupLcbByFlags( Fcb,
  6894. (UCHAR) LcbSplitPrimaryComplement( TraverseLcb ));
  6895. }
  6896. //
  6897. // We now need only to rename and combine any existing Lcb's.
  6898. //
  6899. NtfsRenameLcb( IrpContext,
  6900. TraverseLcb,
  6901. &NewLinkName,
  6902. NewFileNameFlags,
  6903. FALSE );
  6904. if (SplitPrimaryLcb != NULL) {
  6905. NtfsRenameLcb( IrpContext,
  6906. SplitPrimaryLcb,
  6907. &NewLinkName,
  6908. NewFileNameFlags,
  6909. FALSE );
  6910. NtfsCombineLcbs( IrpContext,
  6911. TraverseLcb,
  6912. SplitPrimaryLcb );
  6913. NtfsDeleteLcb( IrpContext, &SplitPrimaryLcb );
  6914. }
  6915. DebugTrace( -1, Dbg, ("NtfsCreateLinkInSameDir: Exit\n") );
  6916. return;
  6917. }
  6918. //
  6919. // Local support routine.
  6920. //
  6921. VOID
  6922. NtfsRenameLinkInDir (
  6923. IN PIRP_CONTEXT IrpContext,
  6924. IN PSCB ParentScb,
  6925. IN PFCB Fcb,
  6926. IN OUT PLCB Lcb,
  6927. IN PUNICODE_STRING NewLinkName,
  6928. IN UCHAR NewLinkNameFlags,
  6929. IN ULONG RenameFlags,
  6930. IN PUNICODE_STRING PrevLinkName,
  6931. IN UCHAR PrevLinkNameFlags
  6932. )
  6933. /*++
  6934. Routine Description:
  6935. This routine performs the in-memory work of moving renaming a link within
  6936. the same directory. It will rename an existing link to the
  6937. new name. It also merges whatever other links need to be joined with
  6938. this link. This includes the complement of a primary link pair or
  6939. an existing hard link which may be overwritten. Merging the existing
  6940. links has the effect of moving any of the Ccb's on the stale Links to
  6941. the newly modified link.
  6942. Arguments:
  6943. ParentScb - Scb for the directory the rename is taking place in.
  6944. Fcb - The Fcb for the file whose link is being renamed.
  6945. Lcb - This is the Lcb which is the base of the rename.
  6946. NewLinkName - This is the name to use for the new link.
  6947. NewLinkNameFlags - These are the flags to use for the new link.
  6948. RenameFlags - Flag field indicating the type of operations to perform
  6949. on the file name links.
  6950. PrevLinkName - File name for link being removed. Only meaningful for a traverse link.
  6951. PrevLinkNameFlags - File name flags for link being removed.
  6952. Return Value:
  6953. None.
  6954. --*/
  6955. {
  6956. UNICODE_STRING SplitLinkName;
  6957. UCHAR SplitLinkNameFlags = NewLinkNameFlags;
  6958. PLCB TraverseLcb = NULL;
  6959. PLCB SplitPrimaryLcb = NULL;
  6960. PFILE_NAME FileName;
  6961. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  6962. BOOLEAN CleanupAttrContext = FALSE;
  6963. BOOLEAN Found;
  6964. ULONG Pass;
  6965. BOOLEAN CheckBufferOnly;
  6966. PAGED_CODE();
  6967. DebugTrace( +1, Dbg, ("NtfsRenameLinkInDir: Entered\n") );
  6968. //
  6969. // Use a try-finally to facilitate cleanup.
  6970. //
  6971. try {
  6972. //
  6973. // We have the Lcb which will be our primary Lcb and the name we need
  6974. // to perform the rename. If the current Lcb is a split primary link
  6975. // or we removed a split primary link, then we need to find any
  6976. // the other split link.
  6977. //
  6978. if (LcbSplitPrimaryLink( Lcb )) {
  6979. SplitPrimaryLcb = NtfsLookupLcbByFlags( Fcb,
  6980. (UCHAR) LcbSplitPrimaryComplement( Lcb ));
  6981. //
  6982. // If we found an existing Lcb we have to update its name as well. We may be
  6983. // able to use the new name used for the Lcb passed in. However we must check
  6984. // that we don't overwrite a DOS name with an NTFS only name.
  6985. //
  6986. if (SplitPrimaryLcb &&
  6987. (SplitPrimaryLcb->FileNameAttr->Flags == FILE_NAME_DOS) &&
  6988. (NewLinkNameFlags == FILE_NAME_NTFS)) {
  6989. //
  6990. // Lookup the dos only name on disk.
  6991. //
  6992. NtfsInitializeAttributeContext( &AttrContext );
  6993. CleanupAttrContext = TRUE;
  6994. //
  6995. // Walk through the names for this entry. There better
  6996. // be one which is not a DOS-only name.
  6997. //
  6998. Found = NtfsLookupAttributeByCode( IrpContext,
  6999. Fcb,
  7000. &Fcb->FileReference,
  7001. $FILE_NAME,
  7002. &AttrContext );
  7003. while (Found) {
  7004. FileName = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  7005. if (FileName->Flags == FILE_NAME_DOS) { break; }
  7006. Found = NtfsLookupNextAttributeByCode( IrpContext,
  7007. Fcb,
  7008. $FILE_NAME,
  7009. &AttrContext );
  7010. }
  7011. //
  7012. // We should have found the entry.
  7013. //
  7014. if (!Found) {
  7015. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  7016. }
  7017. //
  7018. // Now build the component name.
  7019. //
  7020. SplitLinkName.Buffer = FileName->FileName;
  7021. SplitLinkName.MaximumLength =
  7022. SplitLinkName.Length = FileName->FileNameLength * sizeof( WCHAR );
  7023. SplitLinkNameFlags = FILE_NAME_DOS;
  7024. } else {
  7025. SplitLinkName = *NewLinkName;
  7026. }
  7027. }
  7028. //
  7029. // If we used a traverse link, we need to check if there is
  7030. // an Lcb for it. Ignore this for the case where we traversed to
  7031. // the other half of a primary link.
  7032. //
  7033. if (!FlagOn( RenameFlags, OVERWRITE_SOURCE_LINK ) &&
  7034. FlagOn( RenameFlags, REMOVE_TRAVERSE_LINK | REUSE_TRAVERSE_LINK )) {
  7035. if (FlagOn( RenameFlags, EXACT_CASE_MATCH )) {
  7036. TraverseLcb = NtfsCreateLcb( IrpContext,
  7037. ParentScb,
  7038. Fcb,
  7039. *NewLinkName,
  7040. PrevLinkNameFlags,
  7041. NULL );
  7042. } else {
  7043. TraverseLcb = NtfsCreateLcb( IrpContext,
  7044. ParentScb,
  7045. Fcb,
  7046. *PrevLinkName,
  7047. PrevLinkNameFlags,
  7048. NULL );
  7049. }
  7050. if (FlagOn( RenameFlags, REMOVE_TRAVERSE_LINK )) {
  7051. //
  7052. // If this is a split primary, we need to find the name flags for
  7053. // the Lcb.
  7054. //
  7055. if (LcbSplitPrimaryLink( TraverseLcb )) {
  7056. SplitPrimaryLcb = NtfsLookupLcbByFlags( Fcb,
  7057. (UCHAR) LcbSplitPrimaryComplement( TraverseLcb ));
  7058. SplitLinkName = *NewLinkName;
  7059. }
  7060. }
  7061. }
  7062. //
  7063. // Now move and combine the Lcbs. We will do this in two passes. One will allocate buffers
  7064. // of sufficient size. The other will store the names in.
  7065. //
  7066. Pass = 0;
  7067. CheckBufferOnly = TRUE;
  7068. do {
  7069. //
  7070. // Start with the Lcb used for the rename.
  7071. //
  7072. NtfsRenameLcb( IrpContext,
  7073. Lcb,
  7074. NewLinkName,
  7075. NewLinkNameFlags,
  7076. CheckBufferOnly );
  7077. //
  7078. // Next do the split primary if from the source file or the target.
  7079. //
  7080. if (SplitPrimaryLcb) {
  7081. NtfsRenameLcb( IrpContext,
  7082. SplitPrimaryLcb,
  7083. &SplitLinkName,
  7084. SplitLinkNameFlags,
  7085. CheckBufferOnly );
  7086. //
  7087. // If we are in the second pass then optionally combine these
  7088. // Lcb's and delete the split.
  7089. //
  7090. if (!CheckBufferOnly && (SplitLinkNameFlags == NewLinkNameFlags)) {
  7091. NtfsCombineLcbs( IrpContext, Lcb, SplitPrimaryLcb );
  7092. NtfsDeleteLcb( IrpContext, &SplitPrimaryLcb );
  7093. }
  7094. }
  7095. //
  7096. // If we have a traverse link and are in the second pass then combine
  7097. // with the primary Lcb.
  7098. //
  7099. if (!CheckBufferOnly) {
  7100. if (TraverseLcb != NULL) {
  7101. if (!FlagOn( RenameFlags, REUSE_TRAVERSE_LINK )) {
  7102. NtfsRenameLcb( IrpContext,
  7103. TraverseLcb,
  7104. NewLinkName,
  7105. NewLinkNameFlags,
  7106. CheckBufferOnly );
  7107. }
  7108. NtfsCombineLcbs( IrpContext,
  7109. Lcb,
  7110. TraverseLcb );
  7111. NtfsDeleteLcb( IrpContext, &TraverseLcb );
  7112. }
  7113. }
  7114. Pass += 1;
  7115. CheckBufferOnly = FALSE;
  7116. } while (Pass < 2);
  7117. } finally {
  7118. if (CleanupAttrContext) {
  7119. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  7120. }
  7121. DebugTrace( -1, Dbg, ("NtfsRenameLinkInDir: Exit\n") );
  7122. }
  7123. return;
  7124. }
  7125. //
  7126. // Local support routine
  7127. //
  7128. LONG
  7129. NtfsFileInfoExceptionFilter (
  7130. IN PIRP_CONTEXT IrpContext,
  7131. IN PEXCEPTION_POINTERS ExceptionPointer
  7132. )
  7133. /*++
  7134. Routine Description:
  7135. Exception filter for errors during cleanup. We want to raise if this is
  7136. a retryable condition or fatal error, plow on as best we can if not.
  7137. Arguments:
  7138. IrpContext - IrpContext
  7139. ExceptionPointer - Pointer to the exception context.
  7140. Status - Address to store the error status.
  7141. Return Value:
  7142. Exception status - EXCEPTION_CONTINUE_SEARCH if we want to raise to another handler,
  7143. EXCEPTION_EXECUTE_HANDLER if we plan to proceed on.
  7144. --*/
  7145. {
  7146. NTSTATUS Status = ExceptionPointer->ExceptionRecord->ExceptionCode;
  7147. //
  7148. // For now break if we catch corruption errors on both free and checked
  7149. // TODO: Remove this before we ship
  7150. //
  7151. if (NtfsBreakOnCorrupt &&
  7152. ((Status == STATUS_FILE_CORRUPT_ERROR) ||
  7153. (Status == STATUS_DISK_CORRUPT_ERROR))) {
  7154. if (*KdDebuggerEnabled) {
  7155. DbgPrint("*******************************************\n");
  7156. DbgPrint("NTFS detected corruption on your volume\n");
  7157. DbgPrint("IrpContext=0x%08x, VCB=0x%08x\n",IrpContext,IrpContext->Vcb);
  7158. DbgPrint("Send email to NTFSDEV\n");
  7159. DbgPrint("*******************************************\n");
  7160. DbgBreakPoint();
  7161. }
  7162. }
  7163. if (!FsRtlIsNtstatusExpected( Status )) {
  7164. return EXCEPTION_CONTINUE_SEARCH;
  7165. } else {
  7166. return EXCEPTION_EXECUTE_HANDLER;
  7167. }
  7168. UNREFERENCED_PARAMETER( IrpContext );
  7169. }
  7170. //
  7171. // Local support routine
  7172. //
  7173. VOID
  7174. NtfsUpdateFileDupInfo (
  7175. IN PIRP_CONTEXT IrpContext,
  7176. IN PFCB Fcb,
  7177. IN PCCB Ccb OPTIONAL
  7178. )
  7179. /*++
  7180. Routine Description:
  7181. This routine updates the duplicate information for a file for calls
  7182. to set allocation or EOF on the main data stream. It is in a separate routine
  7183. so we don't have to put a try-except in the main path.
  7184. We will overlook any expected errors in this path. If we get any errors we
  7185. will simply leave this update to be performed at some other time.
  7186. We are guaranteed that the current transaction has been checkpointed before this
  7187. routine is called. We will look to see if the MftScb is on the exclusive list
  7188. for this IrpContext and release it if so. This is to prevent a deadlock when
  7189. we attempt to acquire the parent of this file.
  7190. Arguments:
  7191. Fcb - This is the Fcb to update.
  7192. Ccb - If specified, this is the Ccb for the caller making the call.
  7193. Return Value:
  7194. None.
  7195. --*/
  7196. {
  7197. PLCB Lcb = NULL;
  7198. PSCB ParentScb = NULL;
  7199. ULONG FilterMatch;
  7200. PLIST_ENTRY Links;
  7201. PFCB NextFcb;
  7202. PFCB UnlockFcb = NULL;
  7203. PAGED_CODE();
  7204. ASSERT( IrpContext->TransactionId == 0 );
  7205. //
  7206. // Check if there is an Lcb in the Ccb.
  7207. //
  7208. if (ARGUMENT_PRESENT( Ccb )) {
  7209. Lcb = Ccb->Lcb;
  7210. }
  7211. //
  7212. // Use a try-except to catch any errors.
  7213. //
  7214. try {
  7215. //
  7216. // Check that we don't own the Mft Scb.
  7217. //
  7218. if (Fcb->Vcb->MftScb != NULL) {
  7219. for (Links = IrpContext->ExclusiveFcbList.Flink;
  7220. Links != &IrpContext->ExclusiveFcbList;
  7221. Links = Links->Flink) {
  7222. ULONG Count;
  7223. NextFcb = (PFCB) CONTAINING_RECORD( Links,
  7224. FCB,
  7225. ExclusiveFcbLinks );
  7226. //
  7227. // If this is the Fcb for the Mft then remove it from the list.
  7228. //
  7229. if (NextFcb == Fcb->Vcb->MftScb->Fcb) {
  7230. //
  7231. // Free the snapshots for the Fcb and release the Fcb enough times
  7232. // to remove it from the list.
  7233. //
  7234. NtfsFreeSnapshotsForFcb( IrpContext, NextFcb );
  7235. Count = NextFcb->BaseExclusiveCount;
  7236. while (Count--) {
  7237. NtfsReleaseFcb( IrpContext, NextFcb );
  7238. }
  7239. ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_RELEASE_MFT );
  7240. break;
  7241. }
  7242. }
  7243. }
  7244. //
  7245. // Check that we don't own the quota table Scb.
  7246. //
  7247. if (Fcb->Vcb->QuotaTableScb != NULL) {
  7248. for (Links = IrpContext->ExclusiveFcbList.Flink;
  7249. Links != &IrpContext->ExclusiveFcbList;
  7250. Links = Links->Flink) {
  7251. ULONG Count;
  7252. NextFcb = (PFCB) CONTAINING_RECORD( Links,
  7253. FCB,
  7254. ExclusiveFcbLinks );
  7255. //
  7256. // If this is the Fcb for the Quota table then remove
  7257. // it from the list.
  7258. //
  7259. if (NextFcb == Fcb->Vcb->QuotaTableScb->Fcb) {
  7260. //
  7261. // Free the snapshots for the Fcb and release the Fcb enough times
  7262. // to remove it from the list.
  7263. //
  7264. NtfsFreeSnapshotsForFcb( IrpContext, NextFcb );
  7265. Count = NextFcb->BaseExclusiveCount;
  7266. while (Count--) {
  7267. NtfsReleaseFcb( IrpContext, NextFcb );
  7268. }
  7269. break;
  7270. }
  7271. }
  7272. }
  7273. //
  7274. // Go through and free any Scb's in the queue of shared Scb's
  7275. // for transactions.
  7276. //
  7277. if (IrpContext->SharedScb != NULL) {
  7278. NtfsReleaseSharedResources( IrpContext );
  7279. }
  7280. NtfsPrepareForUpdateDuplicate( IrpContext, Fcb, &Lcb, &ParentScb, TRUE );
  7281. NtfsUpdateDuplicateInfo( IrpContext, Fcb, Lcb, ParentScb );
  7282. //
  7283. // Use a try-finally to guarantee we unlock the Fcb we might lock.
  7284. //
  7285. try {
  7286. //
  7287. // If there is no Ccb then look for one in the Lcb we just got.
  7288. //
  7289. if (!ARGUMENT_PRESENT( Ccb ) &&
  7290. ARGUMENT_PRESENT( Lcb )) {
  7291. PLIST_ENTRY Links;
  7292. PCCB NextCcb;
  7293. Links = Lcb->CcbQueue.Flink;
  7294. while (Links != &Lcb->CcbQueue) {
  7295. NextCcb = CONTAINING_RECORD( Links, CCB, LcbLinks );
  7296. NtfsLockFcb( IrpContext, NextCcb->Lcb->Fcb );
  7297. if (!FlagOn( NextCcb->Flags, CCB_FLAG_CLOSE | CCB_FLAG_OPEN_BY_FILE_ID )) {
  7298. Ccb= NextCcb;
  7299. UnlockFcb = NextCcb->Lcb->Fcb;
  7300. break;
  7301. }
  7302. NtfsUnlockFcb( IrpContext, NextCcb->Lcb->Fcb );
  7303. Links = Links->Flink;
  7304. }
  7305. }
  7306. //
  7307. // Now perform the dir notify call if there is a Ccb and this is not an
  7308. // open by FileId.
  7309. //
  7310. if (ARGUMENT_PRESENT( Ccb ) &&
  7311. (Fcb->Vcb->NotifyCount != 0) &&
  7312. (ParentScb != NULL) &&
  7313. !FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_FILE_ID )) {
  7314. FilterMatch = NtfsBuildDirNotifyFilter( IrpContext,
  7315. Fcb->InfoFlags | Lcb->InfoFlags );
  7316. if (FilterMatch != 0) {
  7317. NtfsReportDirNotify( IrpContext,
  7318. Fcb->Vcb,
  7319. &Ccb->FullFileName,
  7320. Ccb->LastFileNameOffset,
  7321. NULL,
  7322. ((FlagOn( Ccb->Flags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT ) &&
  7323. (Ccb->Lcb != NULL) &&
  7324. (Ccb->Lcb->Scb->ScbType.Index.NormalizedName.Length != 0)) ?
  7325. &Ccb->Lcb->Scb->ScbType.Index.NormalizedName :
  7326. NULL),
  7327. FilterMatch,
  7328. FILE_ACTION_MODIFIED,
  7329. ParentScb->Fcb );
  7330. }
  7331. }
  7332. } finally {
  7333. if (UnlockFcb != NULL) {
  7334. NtfsUnlockFcb( IrpContext, UnlockFcb );
  7335. }
  7336. }
  7337. NtfsUpdateLcbDuplicateInfo( Fcb, Lcb );
  7338. Fcb->InfoFlags = 0;
  7339. } except(NtfsFileInfoExceptionFilter( IrpContext, GetExceptionInformation() )) {
  7340. NtfsMinimumExceptionProcessing( IrpContext );
  7341. }
  7342. return;
  7343. }
  7344. //
  7345. // Local support routine
  7346. //
  7347. NTSTATUS
  7348. NtfsStreamRename (
  7349. IN PIRP_CONTEXT IrpContext,
  7350. IN PFILE_OBJECT FileObject,
  7351. IN PFCB Fcb,
  7352. IN PSCB Scb,
  7353. IN PCCB Ccb,
  7354. IN BOOLEAN ReplaceIfExists,
  7355. IN PUNICODE_STRING NewStreamName
  7356. )
  7357. /*++
  7358. Routine Description:
  7359. This routine performs a stream rename within a single Fcb.
  7360. Arguments:
  7361. IrpContext - Context of the call
  7362. FileObject - File object being used
  7363. Fcb - Fcb of file/directory
  7364. Scb - Stream being renamed. The parent Fcb is acquired exclusively.
  7365. Ccb - Handle used to perform the rename. Look here for usn source information.
  7366. ReplaceIfExists - TRUE => overwrite an existing stream
  7367. NewStreamName - name of new stream
  7368. Return Value:
  7369. NTSTATUS of the operation.
  7370. --*/
  7371. {
  7372. NTFS_NAME_DESCRIPTOR Name;
  7373. BOOLEAN FoundIllegalCharacter;
  7374. PSCB TargetScb = NULL;
  7375. NTSTATUS Status = STATUS_SUCCESS;
  7376. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  7377. ATTRIBUTE_TYPE_CODE TypeCode;
  7378. BOOLEAN EmptyFile;
  7379. BOOLEAN NamesSwapped = FALSE;
  7380. PSCB RestoreTargetScb = NULL;
  7381. ULONG TargetScbCompressionUnit;
  7382. USHORT TargetScbAttributeFlags;
  7383. UCHAR TargetScbCompressionUnitShift;
  7384. LONGLONG OldValidDataLengthOnDisk;
  7385. DebugDoit( int Count = 0 );
  7386. PAGED_CODE( );
  7387. DebugTrace( +1, Dbg, ( "NtfsStreamRename\n"
  7388. " IrpContext %x\n"
  7389. " Scb %x\n"
  7390. " ReplaceIf %x\n"
  7391. " NewStreamName '%Z'\n",
  7392. IrpContext, Scb, ReplaceIfExists, NewStreamName ));
  7393. //
  7394. // Take a snapshot if one doesn't exist because we'll be calling writefilesizes
  7395. //
  7396. NtfsSnapshotScb( IrpContext, Scb );
  7397. //
  7398. // Capture the ccb source information.
  7399. //
  7400. if (Ccb != NULL) {
  7401. IrpContext->SourceInfo = Ccb->UsnSourceInfo;
  7402. }
  7403. NtfsInitializeAttributeContext( &Context );
  7404. try {
  7405. //
  7406. // Validate name is just :stream:type. No file name is specified and
  7407. // at least a stream or type must be specified.
  7408. //
  7409. RtlZeroMemory( &Name, sizeof( Name ));
  7410. if (!NtfsParseName( *NewStreamName, FALSE, &FoundIllegalCharacter, &Name )
  7411. || FlagOn( Name.FieldsPresent, FILE_NAME_PRESENT_FLAG )
  7412. || (!FlagOn( Name.FieldsPresent, ATTRIBUTE_NAME_PRESENT_FLAG ) &&
  7413. !FlagOn( Name.FieldsPresent, ATTRIBUTE_TYPE_PRESENT_FLAG ))
  7414. || Name.AttributeName.Length > NTFS_MAX_ATTR_NAME_LEN * sizeof( WCHAR )
  7415. ) {
  7416. DebugTrace( 0, Dbg, ("Name is illegal\n"));
  7417. Status = STATUS_INVALID_PARAMETER;
  7418. leave;
  7419. }
  7420. DebugTrace( 0, Dbg, (" Fields: %x\n"
  7421. " AttributeName %x %x/%x\n",
  7422. Name.FieldsPresent,
  7423. Name.AttributeName.Buffer,
  7424. Name.AttributeName.Length,
  7425. Name.AttributeName.MaximumLength ));
  7426. //
  7427. // Find out the attribute type specified
  7428. //
  7429. if (FlagOn( Name.FieldsPresent, ATTRIBUTE_TYPE_PRESENT_FLAG )) {
  7430. NtfsUpcaseName ( IrpContext->Vcb->UpcaseTable,
  7431. IrpContext->Vcb->UpcaseTableSize,
  7432. &Name.AttributeType );
  7433. TypeCode = NtfsGetAttributeTypeCode( IrpContext->Vcb, &Name.AttributeType );
  7434. } else {
  7435. TypeCode = Scb->AttributeTypeCode;
  7436. }
  7437. if (TypeCode != Scb->AttributeTypeCode) {
  7438. DebugTrace( 0, Dbg, ("Attribute types don't match %x - %x\n", Scb->AttributeTypeCode, TypeCode));
  7439. Status = STATUS_OBJECT_TYPE_MISMATCH;
  7440. leave;
  7441. }
  7442. //
  7443. // Verify that the source stream is $DATA
  7444. //
  7445. if (Scb->AttributeTypeCode != $DATA) {
  7446. DebugTrace( 0, Dbg, ("Type code is illegal\n"));
  7447. Status = STATUS_INVALID_PARAMETER;
  7448. leave;
  7449. }
  7450. //
  7451. // Just to be non-orthogonal, we disallow renaming to default data stream
  7452. // on directories
  7453. //
  7454. if (TypeCode == $DATA &&
  7455. Name.AttributeName.Length == 0 &&
  7456. IsDirectory( &(Scb->Fcb->Info) )) {
  7457. DebugTrace( 0, Dbg, ("Cannot rename directory stream to ::$Data\n") );
  7458. Status = STATUS_INVALID_PARAMETER;
  7459. leave;
  7460. }
  7461. //
  7462. // We have a valid source stream and a valid target name. Take the short cut
  7463. // if the names match. Yes, you could argue about sharing violation, or
  7464. // renaming to non-empty streams. We just claim success and let it go.
  7465. //
  7466. if (NtfsAreNamesEqual( IrpContext->Vcb->UpcaseTable,
  7467. &Scb->AttributeName, &Name.AttributeName,
  7468. TRUE )) {
  7469. DebugTrace( 0, Dbg, ("Names are the same\n"));
  7470. Status = STATUS_SUCCESS;
  7471. leave;
  7472. }
  7473. //
  7474. // Open / Create the target stream and validate ReplaceIfExists.
  7475. //
  7476. Status = NtOfsCreateAttribute( IrpContext,
  7477. Fcb,
  7478. Name.AttributeName,
  7479. ReplaceIfExists ? CREATE_OR_OPEN : CREATE_NEW,
  7480. FALSE,
  7481. &TargetScb );
  7482. if (!NT_SUCCESS( Status )) {
  7483. DebugTrace( 0, Dbg, ("Unable to create target stream\n"));
  7484. leave;
  7485. }
  7486. if (TargetScb == Scb) {
  7487. DebugTrace( 0, Dbg, ("Somehow, you've got the same Scb\n"));
  7488. Status = STATUS_SUCCESS;
  7489. leave;
  7490. }
  7491. //
  7492. // Verify that the target Scb is not in use nor has any allocation
  7493. // or data.
  7494. //
  7495. NtfsAcquireFsrtlHeader( TargetScb );
  7496. EmptyFile = TargetScb->Header.AllocationSize.QuadPart == 0;
  7497. NtfsReleaseFsrtlHeader( TargetScb );
  7498. if (!EmptyFile) {
  7499. DebugTrace( 0, Dbg, ("Target has allocation\n"));
  7500. Status = STATUS_INVALID_PARAMETER;
  7501. leave;
  7502. }
  7503. if (TargetScb->CleanupCount != 0 ||
  7504. !MmCanFileBeTruncated( FileObject->SectionObjectPointer,
  7505. (PLARGE_INTEGER)&Li0 )) {
  7506. DebugTrace( 0, Dbg, ("Target in use\n"));
  7507. Status = STATUS_INVALID_PARAMETER;
  7508. leave;
  7509. }
  7510. NtfsAcquireFsrtlHeader( Scb );
  7511. EmptyFile = Scb->Header.AllocationSize.QuadPart == 0;
  7512. NtfsReleaseFsrtlHeader( Scb );
  7513. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &Context );
  7514. //
  7515. // Always force both streams to be non-resident. Then we will never have
  7516. // a conflict between the Scb and attribute. We don't want to take an
  7517. // empty non-resident stream and point it to a resident attribute.
  7518. // NOTE: we call with CreateSectionUnderWay set to true which forces
  7519. // the data to be flushed directly out to the new clusters. We need this
  7520. // because we explicitly move VDL a little later on.
  7521. //
  7522. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  7523. if (NtfsIsAttributeResident( NtfsFoundAttribute( &Context ))) {
  7524. NtfsConvertToNonresident( IrpContext,
  7525. Fcb,
  7526. NtfsFoundAttribute( &Context ),
  7527. TRUE,
  7528. &Context );
  7529. }
  7530. }
  7531. //
  7532. // Cache the old VDL presisted to disk so we can update it in the new location
  7533. //
  7534. OldValidDataLengthOnDisk = NtfsFoundAttribute( &Context )->Form.Nonresident.ValidDataLength;
  7535. NtfsCleanupAttributeContext( IrpContext, &Context );
  7536. if (FlagOn( TargetScb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  7537. NtfsLookupAttributeForScb( IrpContext, TargetScb, NULL, &Context );
  7538. if (NtfsIsAttributeResident( NtfsFoundAttribute( &Context ))) {
  7539. NtfsConvertToNonresident( IrpContext,
  7540. Fcb,
  7541. NtfsFoundAttribute( &Context ),
  7542. TRUE,
  7543. &Context );
  7544. }
  7545. NtfsCleanupAttributeContext( IrpContext, &Context );
  7546. }
  7547. //
  7548. // Load all Mcb information for the source stream, we'll need it to generate the mapping.
  7549. //
  7550. (VOID)NtfsPreloadAllocation( IrpContext, Scb, 0, MAXLONGLONG );
  7551. //
  7552. // Make sure the attribute flags on the target match that on the source.
  7553. //
  7554. if (TargetScb->AttributeFlags != Scb->AttributeFlags) {
  7555. RestoreTargetScb = TargetScb;
  7556. TargetScbCompressionUnit = TargetScb->CompressionUnit;
  7557. TargetScbAttributeFlags = TargetScb->AttributeFlags;
  7558. TargetScbCompressionUnitShift = TargetScb->CompressionUnitShift;
  7559. NtfsModifyAttributeFlags( IrpContext, TargetScb, Scb->AttributeFlags );
  7560. }
  7561. //
  7562. // At this point, we have Scb to the source of
  7563. // the rename and a target Scb. The Source has all its allocation loaded
  7564. // and the target has no allocation. The only thing that really ties
  7565. // either Scb to the disk attribute is the AttributeName field. We swap
  7566. // the attribute names in order to swap them on disk.
  7567. //
  7568. Name.FileName = TargetScb->AttributeName;
  7569. TargetScb->AttributeName = Scb->AttributeName;
  7570. Scb->AttributeName = Name.FileName;
  7571. NamesSwapped = TRUE;
  7572. //
  7573. // If there is data in the source attribute
  7574. //
  7575. if (!EmptyFile) {
  7576. VCN AllocationClusters;
  7577. //
  7578. // Now, we bring the disk image of these attributes up to date with
  7579. // the Mcb information. First, add the allocation to the new attribute.
  7580. //
  7581. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &Context );
  7582. AllocationClusters = LlClustersFromBytes( IrpContext->Vcb, Scb->Header.AllocationSize.QuadPart );
  7583. //
  7584. // Set the original scb to the target's size (This is really the target now)
  7585. //
  7586. ASSERT( Scb->ScbSnapshot != NULL );
  7587. Scb->Header.AllocationSize = TargetScb->Header.AllocationSize;
  7588. NtfsAddAttributeAllocation( IrpContext,
  7589. Scb,
  7590. &Context,
  7591. &Li0.QuadPart,
  7592. &AllocationClusters );
  7593. NtfsCleanupAttributeContext( IrpContext, &Context );
  7594. //
  7595. // We've put the mapping into the new attribute record. However
  7596. // the valid data length in the record is probably zero. Update
  7597. // it to reflect the data in this stream already written to disk.
  7598. // Otherwise we may never update the data.
  7599. //
  7600. ASSERT( OldValidDataLengthOnDisk <= Scb->Header.FileSize.QuadPart );
  7601. NtfsVerifySizes( &Scb->Header );
  7602. NtfsWriteFileSizes( IrpContext,
  7603. Scb,
  7604. &OldValidDataLengthOnDisk,
  7605. TRUE,
  7606. TRUE,
  7607. TRUE );
  7608. }
  7609. //
  7610. // Next, find all occurrences of the old attribute and delete them.
  7611. //
  7612. NtfsLookupAttributeForScb( IrpContext, TargetScb, NULL, &Context );
  7613. do {
  7614. DebugDoit(
  7615. if (Count++ != 0) {
  7616. DebugTrace( 0, Dbg, ("Deleting attribute record %d\n", Count));
  7617. } else {
  7618. DebugTrace( 0, Dbg, ("%x Mcb's\n", Scb->Mcb.NtfsMcbArraySizeInUse ));
  7619. if (Scb->Mcb.NtfsMcbArray[0].NtfsMcbEntry != NULL) {
  7620. DebugTrace( 0, Dbg, ("First Mcb has %x entries\n",
  7621. Scb->Mcb.NtfsMcbArray[0].NtfsMcbEntry->LargeMcb.PairCount ));
  7622. }
  7623. }
  7624. );
  7625. NtfsDeleteAttributeRecord( IrpContext,
  7626. Fcb,
  7627. DELETE_LOG_OPERATION |
  7628. DELETE_RELEASE_FILE_RECORD,
  7629. &Context );
  7630. } while (NtfsLookupNextAttributeForScb( IrpContext, TargetScb, &Context ));
  7631. NtfsCleanupAttributeContext( IrpContext, &Context );
  7632. //
  7633. // If we are renaming a stream on a file, we must make sure that
  7634. // there is a default data stream still on the file. Check the
  7635. // TargetScb to see if the name is for the default data stream
  7636. // and recreate the default data stream if we need to.
  7637. //
  7638. // We rely on the type code being $DATA and there being NO buffer
  7639. // in the attribute type name.
  7640. //
  7641. if (TypeCode == $DATA && TargetScb->AttributeName.Buffer == NULL) {
  7642. //
  7643. // Always create this stream non-resident in case the stream is encrypted.
  7644. //
  7645. NtfsAllocateAttribute( IrpContext,
  7646. TargetScb,
  7647. $DATA,
  7648. &TargetScb->AttributeName,
  7649. Scb->AttributeFlags,
  7650. TRUE,
  7651. TRUE,
  7652. 0,
  7653. NULL );
  7654. } else {
  7655. ASSERT( !FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA ));
  7656. }
  7657. //
  7658. // Checkpoint the transaction so we know we are done
  7659. //
  7660. NtfsPostUsnChange( IrpContext, Fcb, USN_REASON_STREAM_CHANGE );
  7661. NtfsCommitCurrentTransaction( IrpContext );
  7662. RestoreTargetScb = NULL;
  7663. //
  7664. // Let cleanup handle updating the standard information for the file
  7665. //
  7666. SetFlag( FileObject->Flags, FO_FILE_MODIFIED );
  7667. //
  7668. // If either Scb or TargetScb refers to the default data stream, then
  7669. // bring the Ccb, Scb, and Fcb flags and counts back into sync. This
  7670. // is due to the fact that all operations tied to the default data
  7671. // stream are believed to apply to the file as a whole and not
  7672. // simply to the stream.
  7673. //
  7674. if (FlagOn( TargetScb->ScbState, SCB_STATE_UNNAMED_DATA )) {
  7675. //
  7676. // We have renamed *TO* the default data stream. In this case we
  7677. // must mark all Ccb's for this stream as being CCB_FLAG_OPEN_AS_FILE
  7678. // and mark the Scb as the UNNAMED DATA stream
  7679. //
  7680. // Also, for each Ccb that is opened with DELETE access, we
  7681. // adjust the Fcb->FcbDeleteFile count and CCB_FLAG_DELETE_FILE.
  7682. //
  7683. PCCB Ccb;
  7684. DebugTrace( 0, Dbg, ("Renaming to default data stream\n"));
  7685. DebugTrace( 0, Dbg, ("Scanning Ccb's. FcbDeleteFile = %x\n", Fcb->FcbDeleteFile ));
  7686. SetFlag( Scb->ScbState, SCB_STATE_UNNAMED_DATA );
  7687. for (Ccb = NtfsGetFirstCcbEntry( Scb );
  7688. Ccb != NULL;
  7689. Ccb = NtfsGetNextCcbEntry( Scb, Ccb )) {
  7690. DebugTrace( 0, Dbg, ("Ccb = %x\n", Ccb));
  7691. //
  7692. // Mark Ccb as being opened for the file as a whole
  7693. //
  7694. SetFlag( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE );
  7695. //
  7696. // If deleted access was granted, then we need
  7697. // to adjust the FCB delete count
  7698. //
  7699. if (FlagOn( Ccb->Flags, CCB_FLAG_DELETE_ACCESS )) {
  7700. DebugTrace( 0, Dbg, ("Found one\n" ));
  7701. Fcb->FcbDeleteFile++;
  7702. SetFlag( Ccb->Flags, CCB_FLAG_DELETE_FILE );
  7703. }
  7704. //
  7705. // If the stream was marked as delete-on-close,
  7706. // propagate that to the CCB
  7707. //
  7708. if (FlagOn( Scb->ScbState, SCB_STATE_DELETE_ON_CLOSE )) {
  7709. SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE );
  7710. }
  7711. }
  7712. //
  7713. // Update the file size and allocation size in the Fcb Info field.
  7714. //
  7715. if (Fcb->Info.FileSize != Scb->Header.FileSize.QuadPart) {
  7716. Fcb->Info.FileSize = Scb->Header.FileSize.QuadPart;
  7717. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_FILE_SIZE );
  7718. }
  7719. if (Fcb->Info.AllocatedLength != Scb->TotalAllocated) {
  7720. Fcb->Info.AllocatedLength = Scb->TotalAllocated;
  7721. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_ALLOC_SIZE );
  7722. }
  7723. DebugTrace( 0, Dbg, ("Done Ccb's. FcbDeleteFile = %x\n", Fcb->FcbDeleteFile ));
  7724. } else if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
  7725. //
  7726. // We have renamed *FROM* the default data stream. In this case we
  7727. // must unmark all Ccb's for this stream as being CCB_FLAG_OPEN_AS_FILE
  7728. //
  7729. // Also, for each Ccb that is opened with DELETE access, we
  7730. // adjust the Fcb->FcbDeleteFile count and CCB_FLAG_DELETE_FILE.
  7731. //
  7732. PCCB Ccb;
  7733. DebugTrace( 0, Dbg, ("Renaming from default data stream\n"));
  7734. DebugTrace( 0, Dbg, ("Scanning Ccb's. FcbDeleteFile = %x\n", Fcb->FcbDeleteFile ));
  7735. ClearFlag( Scb->ScbState, SCB_STATE_UNNAMED_DATA );
  7736. for (Ccb = NtfsGetFirstCcbEntry( Scb );
  7737. Ccb != NULL;
  7738. Ccb = NtfsGetNextCcbEntry( Scb, Ccb )) {
  7739. DebugTrace( 0, Dbg, ("Ccb = %x\n", Ccb));
  7740. //
  7741. // Unmark Ccb from representing the file as a whole.
  7742. //
  7743. ClearFlag( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE );
  7744. //
  7745. // If deleted access was granted, then we need
  7746. // to unadjust the FCB delete count
  7747. //
  7748. if (FlagOn( Ccb->Flags, CCB_FLAG_DELETE_ACCESS )) {
  7749. DebugTrace( 0, Dbg, ("Found one\n" ));
  7750. Fcb->FcbDeleteFile--;
  7751. ClearFlag( Ccb->Flags, CCB_FLAG_DELETE_FILE );
  7752. }
  7753. }
  7754. //
  7755. // Update the file size and allocation size in the Fcb Info field.
  7756. //
  7757. if (Fcb->Info.FileSize != 0) {
  7758. Fcb->Info.FileSize = 0;
  7759. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_FILE_SIZE );
  7760. }
  7761. if (Fcb->Info.AllocatedLength != 0) {
  7762. Fcb->Info.AllocatedLength = 0;
  7763. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_ALLOC_SIZE );
  7764. }
  7765. DebugTrace( 0, Dbg, ("Done Ccb's. FcbDeleteFile = %x\n", Fcb->FcbDeleteFile ));
  7766. }
  7767. //
  7768. // Set the Scb flag to indicate that the attribute is gone. Mark the
  7769. // Scb so it will never be returned.
  7770. //
  7771. TargetScb->ValidDataToDisk =
  7772. TargetScb->Header.AllocationSize.QuadPart =
  7773. TargetScb->Header.FileSize.QuadPart =
  7774. TargetScb->Header.ValidDataLength.QuadPart = 0;
  7775. TargetScb->AttributeTypeCode = $UNUSED;
  7776. SetFlag( TargetScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
  7777. } finally {
  7778. if (AbnormalTermination( )) {
  7779. //
  7780. // Restore the names.
  7781. //
  7782. if (NamesSwapped) {
  7783. Name.FileName = TargetScb->AttributeName;
  7784. TargetScb->AttributeName = Scb->AttributeName;
  7785. Scb->AttributeName = Name.FileName;
  7786. }
  7787. //
  7788. // Restore the Target Scb flags.
  7789. //
  7790. if (RestoreTargetScb) {
  7791. RestoreTargetScb->CompressionUnit = TargetScbCompressionUnit;
  7792. RestoreTargetScb->AttributeFlags = TargetScbAttributeFlags;
  7793. RestoreTargetScb->CompressionUnitShift = TargetScbCompressionUnitShift;
  7794. }
  7795. }
  7796. if (TargetScb != NULL) {
  7797. NtOfsCloseAttribute( IrpContext, TargetScb );
  7798. }
  7799. NtfsCleanupAttributeContext( IrpContext, &Context );
  7800. DebugTrace( -1, Dbg, ("NtfsStreamRename --> %x\n", Status) );
  7801. }
  7802. return Status;
  7803. }
  7804. //
  7805. // Local support routine
  7806. //
  7807. NTSTATUS
  7808. NtfsCheckTreeForBatchOplocks (
  7809. IN PIRP_CONTEXT IrpContext,
  7810. IN PIRP Irp,
  7811. IN PSCB DirectoryScb
  7812. )
  7813. /*++
  7814. Routine Description:
  7815. This routine walks a directory tree and looks for batch oplocks which might
  7816. prevent the rename, link operation or short name operation from taking place.
  7817. This routine will release the Vcb if there are batch oplocks we are waiting on the
  7818. break for.
  7819. Arguments:
  7820. Irp - Irp for this request.
  7821. DirectoryScb - Scb for the root of the directory tree.
  7822. Return Value:
  7823. NTSTATUS of the operation. This routine can raise.
  7824. --*/
  7825. {
  7826. NTSTATUS Status;
  7827. PSCB BatchOplockScb;
  7828. ULONG BatchOplockCount;
  7829. PAGED_CODE();
  7830. ASSERT( NtfsIsSharedVcb( DirectoryScb->Vcb ));
  7831. Status = NtfsCheckScbForLinkRemoval( DirectoryScb, &BatchOplockScb, &BatchOplockCount );
  7832. //
  7833. // If STATUS_PENDING is returned then we need to check whether
  7834. // to break a batch oplock.
  7835. //
  7836. if (Status == STATUS_PENDING) {
  7837. //
  7838. // If the number of batch oplocks has grown then fail the request.
  7839. //
  7840. if ((Irp->IoStatus.Information != 0) &&
  7841. (BatchOplockCount >= Irp->IoStatus.Information)) {
  7842. Status = STATUS_ACCESS_DENIED;
  7843. } else {
  7844. //
  7845. // Remember the count of batch oplocks in the Irp and
  7846. // then call the oplock package.
  7847. //
  7848. Irp->IoStatus.Information = BatchOplockCount;
  7849. Status = FsRtlCheckOplock( &BatchOplockScb->ScbType.Data.Oplock,
  7850. Irp,
  7851. IrpContext,
  7852. NtfsOplockComplete,
  7853. NtfsPrePostIrp );
  7854. //
  7855. // If we got back success then raise CANT_WAIT to retry otherwise
  7856. // clean up.
  7857. //
  7858. if (Status == STATUS_SUCCESS) {
  7859. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  7860. }
  7861. }
  7862. }
  7863. return Status;
  7864. }