Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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