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

2207 lines
63 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. ObjIdSup.c
  5. Abstract:
  6. This module implements the object id support routines for Ntfs
  7. Author:
  8. Keith Kaplan [KeithKa] 27-Jun-1996
  9. Revision History:
  10. --*/
  11. #include "NtfsProc.h"
  12. //
  13. // The local debug trace level
  14. //
  15. #define Dbg (DEBUG_TRACE_OBJIDSUP)
  16. //
  17. // Define a tag for general pool allocations from this module
  18. //
  19. #undef MODULE_POOL_TAG
  20. #define MODULE_POOL_TAG ('OFtN')
  21. //
  22. // Local define for number of times to attempt to generate a unique object id.
  23. //
  24. #define NTFS_MAX_OBJID_RETRIES 16
  25. NTSTATUS
  26. NtfsSetObjectIdExtendedInfoInternal (
  27. IN PIRP_CONTEXT IrpContext,
  28. IN PFCB Fcb,
  29. IN PVCB Vcb,
  30. IN PUCHAR ExtendedInfoBuffer
  31. );
  32. VOID
  33. NtfsGetIdFromGenerator (
  34. OUT PFILE_OBJECTID_BUFFER ObjectId
  35. );
  36. NTSTATUS
  37. NtfsSetObjectIdInternal (
  38. IN PIRP_CONTEXT IrpContext,
  39. IN PFCB Fcb,
  40. IN PVCB Vcb,
  41. IN PFILE_OBJECTID_BUFFER ObjectIdBuffer
  42. );
  43. NTSTATUS
  44. NtfsDeleteObjectIdInternal (
  45. IN PIRP_CONTEXT IrpContext,
  46. IN PFCB Fcb,
  47. IN PVCB Vcb,
  48. IN BOOLEAN DeleteFileAttribute
  49. );
  50. VOID
  51. NtfsGetIdFromGenerator (
  52. OUT PFILE_OBJECTID_BUFFER ObjectId
  53. );
  54. #ifdef ALLOC_PRAGMA
  55. #pragma alloc_text(PAGE, NtfsCreateOrGetObjectId)
  56. #pragma alloc_text(PAGE, NtfsDeleteObjectId)
  57. #pragma alloc_text(PAGE, NtfsDeleteObjectIdInternal)
  58. #pragma alloc_text(PAGE, NtfsGetIdFromGenerator)
  59. #pragma alloc_text(PAGE, NtfsGetObjectId)
  60. #pragma alloc_text(PAGE, NtfsGetObjectIdExtendedInfo)
  61. #pragma alloc_text(PAGE, NtfsGetObjectIdInternal)
  62. #pragma alloc_text(PAGE, NtfsInitializeObjectIdIndex)
  63. #pragma alloc_text(PAGE, NtfsSetObjectId)
  64. #pragma alloc_text(PAGE, NtfsSetObjectIdExtendedInfo)
  65. #pragma alloc_text(PAGE, NtfsSetObjectIdExtendedInfoInternal)
  66. #pragma alloc_text(PAGE, NtfsSetObjectIdInternal)
  67. #endif
  68. VOID
  69. NtfsInitializeObjectIdIndex (
  70. IN PIRP_CONTEXT IrpContext,
  71. IN PFCB Fcb,
  72. IN PVCB Vcb
  73. )
  74. /*++
  75. Routine Description:
  76. This routine opens the object id index for the volume. If the index does not
  77. exist it is created and initialized. We also look up the volume's object id,
  78. if any, in this routine.
  79. Arguments:
  80. Fcb - Pointer to Fcb for the object id file.
  81. Vcb - Volume control block for volume being mounted.
  82. Return Value:
  83. None
  84. --*/
  85. {
  86. NTSTATUS Status;
  87. UNICODE_STRING IndexName = CONSTANT_UNICODE_STRING( L"$O" );
  88. FILE_OBJECTID_BUFFER ObjectId;
  89. PAGED_CODE();
  90. NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, 0 );
  91. try {
  92. Status = NtOfsCreateIndex( IrpContext,
  93. Fcb,
  94. IndexName,
  95. CREATE_OR_OPEN,
  96. 0,
  97. COLLATION_NTOFS_ULONGS,
  98. NtOfsCollateUlongs,
  99. NULL,
  100. &Vcb->ObjectIdTableScb );
  101. if (NT_SUCCESS( Status )) {
  102. //
  103. // We were able to create the index, now let's see if the volume has an object id.
  104. //
  105. Status = NtfsGetObjectIdInternal( IrpContext,
  106. Vcb->VolumeDasdScb->Fcb,
  107. FALSE,
  108. &ObjectId );
  109. if (NT_SUCCESS( Status )) {
  110. //
  111. // The volume does indeed have an object id, so copy it into the Vcb
  112. // and set the appropriate flag.
  113. //
  114. RtlCopyMemory( Vcb->VolumeObjectId,
  115. &ObjectId.ObjectId,
  116. OBJECT_ID_KEY_LENGTH );
  117. SetFlag( Vcb->VcbState, VCB_STATE_VALID_OBJECT_ID );
  118. }
  119. }
  120. } finally {
  121. NtfsReleaseFcb( IrpContext, Fcb );
  122. }
  123. }
  124. NTSTATUS
  125. NtfsSetObjectId (
  126. IN PIRP_CONTEXT IrpContext,
  127. IN PIRP Irp
  128. )
  129. /*++
  130. Routine Description:
  131. This routine associates an object id with a file. If the object id is already
  132. in use on the volume we return STATUS_DUPLICATE_NAME. If the file already has
  133. an object id, we return STATUS_OBJECT_NAME_COLLISION.
  134. Arguments:
  135. Irp - Supplies the Irp to process.
  136. Return Value:
  137. NTSTATUS - The return status for the operation.
  138. --*/
  139. {
  140. NTSTATUS Status = STATUS_OBJECT_NAME_INVALID;
  141. PIO_STACK_LOCATION IrpSp;
  142. TYPE_OF_OPEN TypeOfOpen;
  143. PVCB Vcb;
  144. PFCB Fcb;
  145. PSCB Scb;
  146. PCCB Ccb;
  147. //
  148. // Get the current Irp stack location and save some references.
  149. //
  150. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  151. TypeOfOpen = NtfsDecodeFileObject( IrpContext, IrpSp->FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
  152. if (!(((TypeOfOpen == UserFileOpen) || (TypeOfOpen == UserDirectoryOpen)) &&
  153. (IrpSp->Parameters.FileSystemControl.InputBufferLength == sizeof( FILE_OBJECTID_BUFFER )))) {
  154. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  155. return STATUS_INVALID_PARAMETER;
  156. }
  157. //
  158. // Read only volumes stay read only.
  159. //
  160. if (NtfsIsVolumeReadOnly( Vcb )) {
  161. NtfsCompleteRequest( IrpContext, Irp, STATUS_MEDIA_WRITE_PROTECTED );
  162. return STATUS_MEDIA_WRITE_PROTECTED;
  163. }
  164. //
  165. // Cleanly exit for volumes without oid indices, such as non-upgraded
  166. // version 1.x volumes.
  167. //
  168. if (Vcb->ObjectIdTableScb == NULL) {
  169. NtfsCompleteRequest( IrpContext, Irp, STATUS_VOLUME_NOT_UPGRADED );
  170. return STATUS_VOLUME_NOT_UPGRADED;
  171. }
  172. //
  173. // Capture the source information.
  174. //
  175. IrpContext->SourceInfo = Ccb->UsnSourceInfo;
  176. try {
  177. //
  178. // Only a restore operator or the I/O system (using its private irp minor code)
  179. // is allowed to set an arbitrary object id.
  180. //
  181. if ((FlagOn( Ccb->AccessFlags, RESTORE_ACCESS )) ||
  182. (IrpSp->MinorFunction == IRP_MN_KERNEL_CALL)) {
  183. Status = NtfsSetObjectIdInternal( IrpContext,
  184. Fcb,
  185. Vcb,
  186. (PFILE_OBJECTID_BUFFER) Irp->AssociatedIrp.SystemBuffer );
  187. //
  188. // Remember to update the timestamps.
  189. //
  190. if (NT_SUCCESS( Status )) {
  191. SetFlag( Ccb->Flags, CCB_FLAG_UPDATE_LAST_CHANGE );
  192. }
  193. } else {
  194. Status = STATUS_ACCESS_DENIED;
  195. }
  196. } finally {
  197. if (!AbnormalTermination()) {
  198. NtfsCompleteRequest( IrpContext, Irp, Status );
  199. }
  200. }
  201. return Status;
  202. }
  203. NTSTATUS
  204. NtfsSetObjectIdExtendedInfo (
  205. IN PIRP_CONTEXT IrpContext,
  206. IN PIRP Irp
  207. )
  208. /*++
  209. Routine Description:
  210. This routine sets the extended info for a file which already has an object
  211. id. If the file does not yet have an object id, we return a status other
  212. than STATUS_SUCCESS.
  213. Arguments:
  214. Irp - Supplies the Irp to process.
  215. Return Value:
  216. NTSTATUS - The return status for the operation.
  217. --*/
  218. {
  219. NTSTATUS Status = STATUS_OBJECT_NAME_INVALID;
  220. PIO_STACK_LOCATION IrpSp;
  221. TYPE_OF_OPEN TypeOfOpen;
  222. PVCB Vcb;
  223. PFCB Fcb;
  224. PSCB Scb;
  225. PCCB Ccb;
  226. //
  227. // Get the current Irp stack location and save some references.
  228. //
  229. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  230. TypeOfOpen = NtfsDecodeFileObject( IrpContext, IrpSp->FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
  231. if (!(((TypeOfOpen == UserFileOpen) || (TypeOfOpen == UserDirectoryOpen)) &&
  232. (IrpSp->Parameters.FileSystemControl.InputBufferLength == OBJECT_ID_EXT_INFO_LENGTH))) {
  233. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  234. return STATUS_INVALID_PARAMETER;
  235. }
  236. //
  237. // Read only volumes stay read only.
  238. //
  239. if (NtfsIsVolumeReadOnly( Vcb )) {
  240. NtfsCompleteRequest( IrpContext, Irp, STATUS_MEDIA_WRITE_PROTECTED );
  241. return STATUS_MEDIA_WRITE_PROTECTED;
  242. }
  243. //
  244. // Cleanly exit for volumes without oid indices, such as non-upgraded
  245. // version 1.x volumes.
  246. //
  247. if (Vcb->ObjectIdTableScb == NULL) {
  248. NtfsCompleteRequest( IrpContext, Irp, STATUS_VOLUME_NOT_UPGRADED );
  249. return STATUS_VOLUME_NOT_UPGRADED;
  250. }
  251. //
  252. // Capture the source information.
  253. //
  254. IrpContext->SourceInfo = Ccb->UsnSourceInfo;
  255. try {
  256. //
  257. // Setting extended info requires either write access or else it has
  258. // to be the I/O system using its private irp minor code.
  259. //
  260. if ((FlagOn( Ccb->AccessFlags, WRITE_DATA_ACCESS | WRITE_ATTRIBUTES_ACCESS )) ||
  261. (IrpSp->MinorFunction == IRP_MN_KERNEL_CALL)) {
  262. Status = NtfsSetObjectIdExtendedInfoInternal( IrpContext,
  263. Fcb,
  264. Vcb,
  265. (PUCHAR) Irp->AssociatedIrp.SystemBuffer );
  266. //
  267. // Remember to update the timestamps.
  268. //
  269. if (NT_SUCCESS( Status )) {
  270. SetFlag( Ccb->Flags, CCB_FLAG_UPDATE_LAST_CHANGE );
  271. }
  272. } else {
  273. Status = STATUS_ACCESS_DENIED;
  274. }
  275. } finally {
  276. if (!AbnormalTermination()) {
  277. NtfsCompleteRequest( IrpContext, Irp, Status );
  278. }
  279. }
  280. return Status;
  281. }
  282. NTSTATUS
  283. NtfsSetObjectIdInternal (
  284. IN PIRP_CONTEXT IrpContext,
  285. IN PFCB Fcb,
  286. IN PVCB Vcb,
  287. IN PFILE_OBJECTID_BUFFER ObjectIdBuffer
  288. )
  289. /*++
  290. Routine Description:
  291. This routine associates an object id with a file. If the object id is already
  292. in use on the volume we return STATUS_DUPLICATE_NAME. If the file already has
  293. an object id, we return STATUS_OBJECT_NAME_COLLISION.
  294. Arguments:
  295. Fcb - The file to associate with the object id.
  296. Vcb - The volume whose object id index the entry should be added to.
  297. ObjectIdBuffer - Supplies both the object id and the extended info.
  298. Return Value:
  299. NTSTATUS - The return status for the operation.
  300. --*/
  301. {
  302. NTSTATUS Status = STATUS_OBJECT_NAME_INVALID;
  303. NTFS_OBJECTID_INFORMATION ObjectIdInfo;
  304. FILE_OBJECTID_INFORMATION FileObjectIdInfo;
  305. INDEX_KEY IndexKey;
  306. INDEX_ROW IndexRow;
  307. ATTRIBUTE_ENUMERATION_CONTEXT AttributeContext;
  308. BOOLEAN InitializedAttributeContext = FALSE;
  309. BOOLEAN AcquiredPaging = FALSE;
  310. try {
  311. RtlZeroMemory( &ObjectIdInfo,
  312. sizeof( NTFS_OBJECTID_INFORMATION ) );
  313. RtlCopyMemory( &ObjectIdInfo.FileSystemReference,
  314. &Fcb->FileReference,
  315. sizeof( FILE_REFERENCE ) );
  316. RtlCopyMemory( ObjectIdInfo.ExtendedInfo,
  317. ObjectIdBuffer->ExtendedInfo,
  318. OBJECT_ID_EXT_INFO_LENGTH );
  319. } except(EXCEPTION_EXECUTE_HANDLER) {
  320. return STATUS_INVALID_ADDRESS;
  321. }
  322. //
  323. // Acquire the file we're setting the object id on. Main blocks
  324. // anybody else from deleting the file or setting another object
  325. // id behind our backs. Paging blocks collided flushes if we have to convert
  326. // another (data) attribute to be non-resident.
  327. //
  328. // Don't use AcquireFcbWithPaging because
  329. // it can't recursively acquire paging and we come in often with it
  330. // preacquired
  331. //
  332. if (Fcb->PagingIoResource != NULL) {
  333. ExAcquireResourceExclusiveLite( Fcb->PagingIoResource, TRUE );
  334. AcquiredPaging = TRUE;
  335. }
  336. NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, 0 );
  337. try {
  338. //
  339. // if there is now a paging resource release main and grab both
  340. // This is the case for a named data stream in a directory created between our
  341. // unsafe test and owning the main
  342. // Note: if we already owned main before entrance this could never happen. So we can just drop
  343. // and not worry about still owning main and taking paging
  344. //
  345. if (!AcquiredPaging && (Fcb->PagingIoResource != NULL)) {
  346. NtfsReleaseFcb( IrpContext, Fcb );
  347. ExAcquireResourceExclusiveLite( Fcb->PagingIoResource, TRUE );
  348. AcquiredPaging = TRUE;
  349. NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, 0 );
  350. }
  351. if (!IsListEmpty( &Fcb->ScbQueue )) {
  352. PSCB Scb;
  353. Scb = CONTAINING_RECORD( Fcb->ScbQueue.Flink, SCB, FcbLinks );
  354. ASSERT( Scb->Header.Resource == Fcb->Resource );
  355. if (FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) {
  356. try_return( Status = STATUS_VOLUME_DISMOUNTED );
  357. }
  358. }
  359. //
  360. // Post the change to the Usn Journal (on errors change is backed out).
  361. // We dont' want to do this if we've been called because create is
  362. // trying to set an object id from the tunnel cache, since we can't
  363. // call the Usn package yet, since the file record doesn't have a file
  364. // name yet.
  365. //
  366. if (IrpContext->MajorFunction != IRP_MJ_CREATE) {
  367. NtfsPostUsnChange( IrpContext, Fcb, USN_REASON_OBJECT_ID_CHANGE );
  368. }
  369. //
  370. // Make sure the file doesn't already have an object id.
  371. //
  372. NtfsInitializeAttributeContext( &AttributeContext );
  373. InitializedAttributeContext = TRUE;
  374. if (NtfsLookupAttributeByCode( IrpContext,
  375. Fcb,
  376. &Fcb->FileReference,
  377. $OBJECT_ID,
  378. &AttributeContext )) {
  379. try_return( Status = STATUS_OBJECT_NAME_COLLISION );
  380. }
  381. //
  382. // Add ObjectId to the index, associate it with this file.
  383. //
  384. IndexKey.Key = ObjectIdBuffer->ObjectId;
  385. IndexKey.KeyLength = OBJECT_ID_KEY_LENGTH;
  386. IndexRow.KeyPart = IndexKey;
  387. IndexRow.DataPart.DataLength = sizeof( ObjectIdInfo );
  388. IndexRow.DataPart.Data = &ObjectIdInfo;
  389. //
  390. // NtOfsAddRecords may raise if the object id isn't unique.
  391. //
  392. NtOfsAddRecords( IrpContext,
  393. Vcb->ObjectIdTableScb,
  394. 1, // adding one record to the index
  395. &IndexRow,
  396. FALSE ); // sequential insert
  397. //
  398. // Now add the objectid attribute to the file. Notice that
  399. // we do _not_ log this operation if we're within a create
  400. // operation, i.e. if we're restoring an object id from the
  401. // tunnel cache. The create path has its own logging scheme
  402. // that we don't want to interfere with.
  403. //
  404. NtfsCleanupAttributeContext( IrpContext, &AttributeContext );
  405. NtfsCreateAttributeWithValue( IrpContext,
  406. Fcb,
  407. $OBJECT_ID,
  408. NULL,
  409. ObjectIdBuffer->ObjectId,
  410. OBJECT_ID_KEY_LENGTH,
  411. 0,
  412. NULL,
  413. (BOOLEAN)(IrpContext->MajorFunction != IRP_MJ_CREATE),
  414. &AttributeContext );
  415. ASSERT( IrpContext->TransactionId != 0 );
  416. //
  417. // Notify anybody who's interested.
  418. //
  419. if (Vcb->ViewIndexNotifyCount != 0) {
  420. //
  421. // The FRS field is only populated for the notification of a failed
  422. // object id restore from the tunnel cache.
  423. //
  424. FileObjectIdInfo.FileReference = 0L;
  425. RtlCopyMemory( FileObjectIdInfo.ObjectId,
  426. ObjectIdBuffer->ObjectId,
  427. OBJECT_ID_KEY_LENGTH );
  428. RtlCopyMemory( FileObjectIdInfo.ExtendedInfo,
  429. ObjectIdBuffer->ExtendedInfo,
  430. OBJECT_ID_EXT_INFO_LENGTH );
  431. NtfsReportViewIndexNotify( Vcb,
  432. Vcb->ObjectIdTableScb->Fcb,
  433. FILE_NOTIFY_CHANGE_FILE_NAME,
  434. FILE_ACTION_ADDED,
  435. &FileObjectIdInfo,
  436. sizeof(FILE_OBJECTID_INFORMATION) );
  437. }
  438. //
  439. // If we made it this far and didn't have to jump into the
  440. // finally clause yet, all must have gone well.
  441. //
  442. Status = STATUS_SUCCESS;
  443. try_exit: NOTHING;
  444. NtfsCleanupTransaction( IrpContext, Status, FALSE );
  445. } finally {
  446. if (AcquiredPaging) {
  447. ExReleaseResourceLite( Fcb->PagingIoResource );
  448. }
  449. NtfsReleaseFcb( IrpContext, Fcb );
  450. if (InitializedAttributeContext) {
  451. NtfsCleanupAttributeContext( IrpContext, &AttributeContext );
  452. }
  453. }
  454. return Status;
  455. }
  456. NTSTATUS
  457. NtfsCreateOrGetObjectId (
  458. IN PIRP_CONTEXT IrpContext,
  459. IN PIRP Irp
  460. )
  461. /*++
  462. Routine Description:
  463. This routine generates a new object id, if possible, for a given file. It is
  464. different from NtfsSetObjectId in that it does not take an object id as an
  465. input, rather it calls a routine to generate one. If the file already has
  466. an object id, that existing object id is returned.
  467. Arguments:
  468. Irp - Supplies the Irp to process.
  469. Return Value:
  470. NTSTATUS - The return status for the operation.
  471. STATUS_DUPLICATE_NAME if we are unable to generate a unique id
  472. in NTFS_MAX_OBJID_RETRIES retries.
  473. --*/
  474. {
  475. NTSTATUS Status;
  476. PIO_STACK_LOCATION IrpSp;
  477. TYPE_OF_OPEN TypeOfOpen;
  478. PVCB Vcb;
  479. PFCB Fcb;
  480. PSCB Scb;
  481. PCCB Ccb;
  482. FILE_OBJECTID_BUFFER ObjectId;
  483. FILE_OBJECTID_BUFFER *OutputBuffer;
  484. ULONG RetryCount = 0;
  485. //
  486. // Get the current Irp stack location and save some references.
  487. //
  488. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  489. TypeOfOpen = NtfsDecodeFileObject( IrpContext, IrpSp->FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
  490. //
  491. // This only works for files and directories.
  492. //
  493. if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen)) {
  494. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  495. return STATUS_INVALID_PARAMETER;
  496. }
  497. //
  498. // Cleanly exit for volumes without oid indices, such as non-upgraded
  499. // version 1.x volumes.
  500. //
  501. if (Vcb->ObjectIdTableScb == NULL) {
  502. NtfsCompleteRequest( IrpContext, Irp, STATUS_VOLUME_NOT_UPGRADED );
  503. return STATUS_VOLUME_NOT_UPGRADED;
  504. }
  505. //
  506. // Get a pointer to the output buffer. Look at the system buffer field in the
  507. // irp first, then the Irp Mdl.
  508. //
  509. if (Irp->AssociatedIrp.SystemBuffer != NULL) {
  510. OutputBuffer = (FILE_OBJECTID_BUFFER *)Irp->AssociatedIrp.SystemBuffer;
  511. } else if (Irp->MdlAddress != NULL) {
  512. OutputBuffer = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, NormalPagePriority );
  513. if (OutputBuffer == NULL) {
  514. NtfsCompleteRequest( IrpContext, Irp, STATUS_INSUFFICIENT_RESOURCES );
  515. return STATUS_INSUFFICIENT_RESOURCES;
  516. }
  517. } else {
  518. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_USER_BUFFER );
  519. return STATUS_INVALID_USER_BUFFER;
  520. }
  521. //
  522. // Make sure the output buffer is large enough.
  523. //
  524. if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ObjectId)) {
  525. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  526. return STATUS_INVALID_PARAMETER;
  527. }
  528. //
  529. // Capture the source information.
  530. //
  531. IrpContext->SourceInfo = Ccb->UsnSourceInfo;
  532. try {
  533. //
  534. // Get this file exlusively so we know nobody else is trying
  535. // to do this at the same time. At this point the irpcontext flag
  536. // is not set so paging is not acquired.
  537. //
  538. NtfsAcquireFcbWithPaging( IrpContext, Fcb, 0 );
  539. //
  540. // Let's make sure the volume is still mounted.
  541. //
  542. if (FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) {
  543. try_return( Status = STATUS_VOLUME_DISMOUNTED );
  544. }
  545. DebugTrace( +1, Dbg, ("NtfsCreateOrGetObjectId\n") );
  546. //
  547. // If this file already has an object id, let's return it. Doing this
  548. // first saves a (possibly expensive) call to NtfsGetIdFromGenerator.
  549. //
  550. Status = NtfsGetObjectIdInternal( IrpContext, Fcb, TRUE, OutputBuffer );
  551. if (Status == STATUS_OBJECTID_NOT_FOUND) {
  552. DebugTrace( 0, Dbg, ("File has no oid, we have to generate one\n") );
  553. //
  554. // We want to keep retrying if the object id generator returns a
  555. // duplicate name. If we have success, or any other error, we
  556. // should stop trying. For instance, if we fail because the file
  557. // already has an object id, retrying is just a waste of time.
  558. // We also need some sane limit on the number of times we retry
  559. // this operation.
  560. //
  561. do {
  562. RetryCount += 1;
  563. //
  564. // Drop this file so we don't deadlock in the guid generator.
  565. //
  566. ASSERT( 0 == IrpContext->TransactionId );
  567. NtfsReleaseFcbWithPaging( IrpContext, Fcb );
  568. DebugTrace( 0, Dbg, ("Calling oid generator\n") );
  569. NtfsGetIdFromGenerator( &ObjectId );
  570. //
  571. // Reacquire the file so we know nobody else is trying to do
  572. // this at the same time. SetObjIdInternal acquires both so we need to
  573. // do the same
  574. //
  575. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_PAGING );
  576. NtfsAcquireFcbWithPaging( IrpContext, Fcb, 0 );
  577. //
  578. // Make sure we didn't miss a dismount.
  579. //
  580. if (FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) {
  581. try_return( Status = STATUS_VOLUME_DISMOUNTED );
  582. }
  583. //
  584. // Let's make sure this file didn't get an object id assigned to it
  585. // while we weren't holding the Fcb above.
  586. //
  587. Status = NtfsGetObjectIdInternal( IrpContext, Fcb, TRUE, OutputBuffer );
  588. if (Status == STATUS_OBJECTID_NOT_FOUND) {
  589. if (NtfsIsVolumeReadOnly( Vcb )) {
  590. try_return( Status = STATUS_MEDIA_WRITE_PROTECTED );
  591. }
  592. DebugTrace( 0, Dbg, ("File still has no oid, attempting to set generated one\n") );
  593. //
  594. // The object id generator only generates the indexed part, so
  595. // we need to fill in the rest of the 'birth id' now. Note that if
  596. // the volume has no object id, we're relying on the Vcb creation
  597. // code to zero init the Vcb->VolumeObjectId for us. The net result
  598. // is right -- we get zeroes in the volume id part of the extended
  599. // info if the volume has no object id.
  600. //
  601. RtlCopyMemory( &ObjectId.BirthVolumeId,
  602. Vcb->VolumeObjectId,
  603. OBJECT_ID_KEY_LENGTH );
  604. RtlCopyMemory( &ObjectId.BirthObjectId,
  605. &ObjectId.ObjectId,
  606. OBJECT_ID_KEY_LENGTH );
  607. RtlZeroMemory( &ObjectId.DomainId,
  608. OBJECT_ID_KEY_LENGTH );
  609. Status = NtfsSetObjectIdInternal( IrpContext,
  610. Fcb,
  611. Vcb,
  612. &ObjectId );
  613. if (Status == STATUS_SUCCESS) {
  614. DebugTrace( 0, Dbg, ("Successfully set generated oid\n") );
  615. //
  616. // We have successfully generated and set an object id for this
  617. // file, so we need to tell our caller what that id is.
  618. //
  619. RtlCopyMemory( OutputBuffer,
  620. &ObjectId,
  621. sizeof(ObjectId) );
  622. //
  623. // Let's also remember to update the timestamps.
  624. //
  625. SetFlag( Ccb->Flags, CCB_FLAG_UPDATE_LAST_CHANGE );
  626. }
  627. }
  628. } while ((Status == STATUS_DUPLICATE_NAME) &&
  629. (RetryCount <= NTFS_MAX_OBJID_RETRIES));
  630. } else if (Status == STATUS_SUCCESS) {
  631. //
  632. // If we found an ID, make sure it isn't a partially formed id with
  633. // an all zero extended info. If it's partially formed, we'll generate
  634. // extended info now.
  635. //
  636. if (RtlCompareMemory( (PUCHAR)&OutputBuffer->ExtendedInfo, &NtfsZeroExtendedInfo, sizeof(ObjectId.ExtendedInfo)) == sizeof(ObjectId.ExtendedInfo)) {
  637. RtlCopyMemory( &OutputBuffer->BirthVolumeId,
  638. Vcb->VolumeObjectId,
  639. OBJECT_ID_KEY_LENGTH );
  640. RtlCopyMemory( &OutputBuffer->BirthObjectId,
  641. &OutputBuffer->ObjectId,
  642. OBJECT_ID_KEY_LENGTH );
  643. Status = NtfsSetObjectIdExtendedInfoInternal( IrpContext,
  644. Fcb,
  645. Vcb,
  646. (PUCHAR) &OutputBuffer->ExtendedInfo );
  647. }
  648. }
  649. if (Status == STATUS_SUCCESS) {
  650. //
  651. // If we found an existing id for the file, or managed to generate one
  652. // ourselves, we need to set the size in the information field so the
  653. // rdr can handle this operation correctly.
  654. //
  655. IrpContext->OriginatingIrp->IoStatus.Information = sizeof( ObjectId );
  656. }
  657. try_exit: NOTHING;
  658. } finally {
  659. }
  660. NtfsCompleteRequest( IrpContext, Irp, Status );
  661. DebugTrace( -1, Dbg, ("NtfsCreateOrGetObjectId -> %08lx\n", Status) );
  662. return Status;
  663. }
  664. NTSTATUS
  665. NtfsGetObjectId (
  666. IN PIRP_CONTEXT IrpContext,
  667. IN PIRP Irp
  668. )
  669. /*++
  670. Routine Description:
  671. This routine finds the object id, if any, for a given file.
  672. Arguments:
  673. Irp - Supplies the Irp to process.
  674. Return Value:
  675. NTSTATUS - The return status for the operation.
  676. --*/
  677. {
  678. NTSTATUS Status = STATUS_SUCCESS;
  679. PIO_STACK_LOCATION IrpSp;
  680. TYPE_OF_OPEN TypeOfOpen;
  681. PVCB Vcb;
  682. PFCB Fcb;
  683. PSCB Scb;
  684. PCCB Ccb;
  685. FILE_OBJECTID_BUFFER *OutputBuffer;
  686. //
  687. // Get the current Irp stack location and save some references.
  688. //
  689. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  690. TypeOfOpen = NtfsDecodeFileObject( IrpContext, IrpSp->FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
  691. //
  692. // This only works for files and directories.
  693. //
  694. if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen)) {
  695. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  696. return STATUS_INVALID_PARAMETER;
  697. }
  698. //
  699. // Cleanly exit for volumes without oid indices, such as non-upgraded
  700. // version 1.x volumes.
  701. //
  702. if (Vcb->ObjectIdTableScb == NULL) {
  703. NtfsCompleteRequest( IrpContext, Irp, STATUS_VOLUME_NOT_UPGRADED );
  704. return STATUS_VOLUME_NOT_UPGRADED;
  705. }
  706. //
  707. // Get a pointer to the output buffer. Look at the system buffer field in the
  708. // irp first, then the Irp Mdl.
  709. //
  710. if (Irp->AssociatedIrp.SystemBuffer != NULL) {
  711. OutputBuffer = (FILE_OBJECTID_BUFFER *)Irp->AssociatedIrp.SystemBuffer;
  712. } else if (Irp->MdlAddress != NULL) {
  713. OutputBuffer = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, NormalPagePriority );
  714. if (OutputBuffer == NULL) {
  715. NtfsCompleteRequest( IrpContext, Irp, STATUS_INSUFFICIENT_RESOURCES );
  716. return STATUS_INSUFFICIENT_RESOURCES;
  717. }
  718. } else {
  719. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_USER_BUFFER );
  720. return STATUS_INVALID_USER_BUFFER;
  721. }
  722. //
  723. // Make sure the output buffer is large enough.
  724. //
  725. if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(FILE_OBJECTID_BUFFER)) {
  726. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  727. return STATUS_INVALID_PARAMETER;
  728. }
  729. try {
  730. //
  731. // Call the function that does the real work.
  732. //
  733. Status = NtfsGetObjectIdInternal( IrpContext, Fcb, TRUE, OutputBuffer );
  734. if (NT_SUCCESS( Status )) {
  735. //
  736. // And set the size in the information field so the rdr
  737. // can handle this correctly.
  738. //
  739. IrpContext->OriginatingIrp->IoStatus.Information = sizeof( FILE_OBJECTID_BUFFER );
  740. }
  741. } finally {
  742. if (!AbnormalTermination()) {
  743. NtfsCompleteRequest( IrpContext, Irp, Status );
  744. }
  745. }
  746. return Status;
  747. }
  748. NTSTATUS
  749. NtfsGetObjectIdInternal (
  750. IN PIRP_CONTEXT IrpContext,
  751. IN PFCB Fcb,
  752. IN BOOLEAN GetExtendedInfo,
  753. OUT FILE_OBJECTID_BUFFER *OutputBuffer
  754. )
  755. /*++
  756. Routine Description:
  757. Internal function to find the object id, if any, for a given file. Called
  758. in response to the user's ioctl and by NtfsDeleteObjectIdInternal.
  759. Arguments:
  760. Fcb - The file whose object id we need to look up.
  761. GetExtendedInfo - If TRUE, we also copy the object id's extended information
  762. to the OutputBuffer, otherwise we only copy the object id
  763. itself. For instance, NtfsDeleteObjectIdInternal is not
  764. interested in the extended info -- it only needs to know
  765. which object id to delete from the index.
  766. OutputBuffer - Where to store the object id (and optionally, extended info)
  767. if an object id is found.
  768. Return Value:
  769. NTSTATUS - The return status for the operation.
  770. STATUS_OBJECT_NAME_NOT_FOUND if the file does not have an object id.
  771. --*/
  772. {
  773. NTSTATUS Status = STATUS_SUCCESS;
  774. UCHAR *ObjectId;
  775. ATTRIBUTE_ENUMERATION_CONTEXT AttributeContext;
  776. BOOLEAN InitializedAttributeContext = FALSE;
  777. if ((OutputBuffer == NULL) ||
  778. (OutputBuffer->ObjectId == NULL)) {
  779. return STATUS_INVALID_PARAMETER;
  780. }
  781. //
  782. // Acquire the file we're getting the object id for. We don't
  783. // want anybody else deleting the file or setting an object
  784. // id behind our backs.
  785. //
  786. NtfsAcquireSharedFcb( IrpContext, Fcb, NULL, 0 );
  787. try {
  788. if (!IsListEmpty( &Fcb->ScbQueue )) {
  789. PSCB Scb;
  790. Scb = CONTAINING_RECORD( Fcb->ScbQueue.Flink, SCB, FcbLinks );
  791. ASSERT( Scb->Header.Resource == Fcb->Resource );
  792. if (FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) {
  793. Status = STATUS_VOLUME_DISMOUNTED;
  794. leave;
  795. }
  796. }
  797. //
  798. // Make sure the file has an object id.
  799. //
  800. NtfsInitializeAttributeContext( &AttributeContext );
  801. InitializedAttributeContext = TRUE;
  802. if (NtfsLookupAttributeByCode( IrpContext,
  803. Fcb,
  804. &Fcb->FileReference,
  805. $OBJECT_ID,
  806. &AttributeContext )) {
  807. //
  808. // Prepare the object id to be returned
  809. //
  810. ObjectId = (UCHAR *) NtfsAttributeValue( NtfsFoundAttribute( &AttributeContext ));
  811. RtlCopyMemory( &OutputBuffer->ObjectId,
  812. ObjectId,
  813. OBJECT_ID_KEY_LENGTH );
  814. if (GetExtendedInfo) {
  815. Status = NtfsGetObjectIdExtendedInfo( IrpContext,
  816. Fcb->Vcb,
  817. ObjectId,
  818. OutputBuffer->ExtendedInfo );
  819. }
  820. } else {
  821. //
  822. // This file has no object id.
  823. //
  824. Status = STATUS_OBJECTID_NOT_FOUND;
  825. }
  826. } finally {
  827. NtfsReleaseFcb( IrpContext, Fcb );
  828. if (InitializedAttributeContext) {
  829. NtfsCleanupAttributeContext( IrpContext, &AttributeContext );
  830. }
  831. }
  832. return Status;
  833. }
  834. NTSTATUS
  835. NtfsGetObjectIdExtendedInfo (
  836. IN PIRP_CONTEXT IrpContext,
  837. IN PVCB Vcb,
  838. IN UCHAR *ObjectId,
  839. IN OUT UCHAR *ExtendedInfo
  840. )
  841. /*++
  842. Routine Description:
  843. This routine finds the extended info stored with a given object id.
  844. Arguments:
  845. Vcb - Supplies the volume whose object id index should be searched.
  846. ObjectId - Supplies the object id to lookup in the index.
  847. ExtendedInfo - Where to store the extended info. Must be a buffer with
  848. room for OBJECT_ID_EXT_INFO_LENGTH UCHARs.
  849. Return Value:
  850. NTSTATUS - The return status for the operation.
  851. --*/
  852. {
  853. NTSTATUS Status = STATUS_SUCCESS;
  854. INDEX_KEY IndexKey;
  855. INDEX_ROW IndexRow;
  856. MAP_HANDLE MapHandle;
  857. BOOLEAN InitializedMapHandle = FALSE;
  858. BOOLEAN IndexAcquired = FALSE;
  859. try {
  860. //
  861. // Now look for object id in the index so we can return the
  862. // extended info.
  863. //
  864. IndexKey.Key = ObjectId;
  865. IndexKey.KeyLength = OBJECT_ID_KEY_LENGTH;
  866. NtOfsInitializeMapHandle( &MapHandle );
  867. InitializedMapHandle = TRUE;
  868. //
  869. // Acquire the object id index before doing the lookup.
  870. // We need to make sure the file is acquired first to prevent
  871. // a possible deadlock.
  872. //
  873. // **** ASSERT_EXCLUSIVE_FCB( Fcb ); ****
  874. //
  875. // We shouldn't try to get the object id index while holding the Mft.
  876. //
  877. ASSERT( !NtfsIsExclusiveScb( Vcb->MftScb ) ||
  878. NtfsIsSharedScb( Vcb->ObjectIdTableScb ) );
  879. NtfsAcquireSharedScb( IrpContext, Vcb->ObjectIdTableScb );
  880. IndexAcquired = TRUE;
  881. if ( NtOfsFindRecord( IrpContext,
  882. Vcb->ObjectIdTableScb,
  883. &IndexKey,
  884. &IndexRow,
  885. &MapHandle,
  886. NULL) != STATUS_SUCCESS ) {
  887. //
  888. // If the object id attribute exists for the file,
  889. // but it isn't in the index, the object id index
  890. // for this volume is corrupt.
  891. //
  892. SetFlag( Vcb->ObjectIdState, VCB_OBJECT_ID_CORRUPT );
  893. try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND );
  894. }
  895. RtlCopyMemory( ExtendedInfo,
  896. ((NTFS_OBJECTID_INFORMATION *)IndexRow.DataPart.Data)->ExtendedInfo,
  897. OBJECT_ID_EXT_INFO_LENGTH );
  898. try_exit: NOTHING;
  899. } finally {
  900. if (IndexAcquired) {
  901. NtfsReleaseScb( IrpContext, Vcb->ObjectIdTableScb );
  902. }
  903. if (InitializedMapHandle) {
  904. NtOfsReleaseMap( IrpContext, &MapHandle );
  905. }
  906. }
  907. return Status;
  908. }
  909. NTSTATUS
  910. NtfsDeleteObjectId (
  911. IN PIRP_CONTEXT IrpContext,
  912. IN PIRP Irp
  913. )
  914. /*++
  915. Routine Description:
  916. This routine deletes the object id attribute from a file
  917. and removes that object id from the index.
  918. Arguments:
  919. Irp - Supplies the Irp to process
  920. Return Value:
  921. NTSTATUS - The return status for the operation.
  922. --*/
  923. {
  924. NTSTATUS Status = STATUS_SUCCESS;
  925. PIO_STACK_LOCATION IrpSp;
  926. TYPE_OF_OPEN TypeOfOpen;
  927. PVCB Vcb;
  928. PFCB Fcb;
  929. PSCB Scb;
  930. PCCB Ccb;
  931. //
  932. // Get the current Irp stack location and save some references.
  933. //
  934. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  935. TypeOfOpen = NtfsDecodeFileObject( IrpContext, IrpSp->FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
  936. //
  937. // This only works for files and directories.
  938. //
  939. if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen)) {
  940. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  941. return STATUS_INVALID_PARAMETER;
  942. }
  943. //
  944. // Read only volumes stay read only.
  945. //
  946. if (NtfsIsVolumeReadOnly( Vcb )) {
  947. NtfsCompleteRequest( IrpContext, Irp, STATUS_MEDIA_WRITE_PROTECTED );
  948. return STATUS_MEDIA_WRITE_PROTECTED;
  949. }
  950. //
  951. // Cleanly exit for volumes without oid indices, such as non-upgraded
  952. // version 1.x volumes.
  953. //
  954. if (Vcb->ObjectIdTableScb == NULL) {
  955. NtfsCompleteRequest( IrpContext, Irp, STATUS_VOLUME_NOT_UPGRADED );
  956. return STATUS_VOLUME_NOT_UPGRADED;
  957. }
  958. //
  959. // Capture the source information.
  960. //
  961. IrpContext->SourceInfo = Ccb->UsnSourceInfo;
  962. try {
  963. //
  964. // Only a restore operator or the I/O system (using its private irp minor code)
  965. // is allowed to delete an object id.
  966. //
  967. if (FlagOn( Ccb->AccessFlags, RESTORE_ACCESS | WRITE_DATA_ACCESS) ||
  968. (IrpSp->MinorFunction == IRP_MN_KERNEL_CALL)) {
  969. Status = NtfsDeleteObjectIdInternal( IrpContext,
  970. Fcb,
  971. Vcb,
  972. TRUE );
  973. } else {
  974. Status = STATUS_ACCESS_DENIED;
  975. }
  976. } finally {
  977. //
  978. // Update the last change timestamp
  979. //
  980. if (NT_SUCCESS( Status )) {
  981. SetFlag( Ccb->Flags, CCB_FLAG_UPDATE_LAST_CHANGE );
  982. }
  983. //
  984. // If there was no object id - just return success
  985. //
  986. if (STATUS_OBJECTID_NOT_FOUND == Status) {
  987. Status = STATUS_SUCCESS;
  988. }
  989. if (!AbnormalTermination()) {
  990. NtfsCompleteRequest( IrpContext, Irp, Status );
  991. }
  992. }
  993. return Status;
  994. }
  995. NTSTATUS
  996. NtfsDeleteObjectIdInternal (
  997. IN PIRP_CONTEXT IrpContext,
  998. IN PFCB Fcb,
  999. IN PVCB Vcb,
  1000. IN BOOLEAN DeleteFileAttribute
  1001. )
  1002. /*++
  1003. Routine Description:
  1004. Internal function to (optionally) delete the object id attribute from
  1005. a file and remove that object id from the index.
  1006. Arguments:
  1007. Fcb - The file from which to delete the object id.
  1008. Vcb - The volume whose object id index the object id should be removed from.
  1009. DeleteFileAttribute - Specifies whether to delete the object id file attribute
  1010. from the file in addition to removing the id from the index.
  1011. Return Value:
  1012. NTSTATUS - The return status for the operation.
  1013. --*/
  1014. {
  1015. NTSTATUS Status = STATUS_SUCCESS;
  1016. FILE_OBJECTID_BUFFER ObjectIdBuffer;
  1017. FILE_OBJECTID_INFORMATION FileObjectIdInfo;
  1018. INDEX_KEY IndexKey;
  1019. INDEX_ROW IndexRow;
  1020. MAP_HANDLE MapHandle;
  1021. ATTRIBUTE_ENUMERATION_CONTEXT AttributeContext;
  1022. BOOLEAN InitializedAttributeContext = FALSE;
  1023. BOOLEAN InitializedMapHandle = FALSE;
  1024. BOOLEAN IndexAcquired = FALSE;
  1025. //
  1026. // Cleanly exit for volumes without oid indices, such as non-upgraded
  1027. // version 1.x volumes.
  1028. //
  1029. if (Vcb->ObjectIdTableScb == NULL) {
  1030. return STATUS_VOLUME_NOT_UPGRADED;
  1031. }
  1032. //
  1033. // Acquire the file we're deleting the object id from. We don't
  1034. // want anybody else deleting the file or object id behind
  1035. // our backs.
  1036. //
  1037. NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, 0 );
  1038. try {
  1039. //
  1040. // We need to look up the object id. It's quite possible that
  1041. // this file has no object id, so we'll treat that as success.
  1042. //
  1043. Status = NtfsGetObjectIdInternal( IrpContext,
  1044. Fcb,
  1045. FALSE,
  1046. &ObjectIdBuffer );
  1047. if (Status != STATUS_SUCCESS) {
  1048. try_return( NOTHING );
  1049. }
  1050. //
  1051. // Look for object id in the index.
  1052. //
  1053. IndexKey.Key = ObjectIdBuffer.ObjectId;
  1054. IndexKey.KeyLength = sizeof( ObjectIdBuffer.ObjectId );
  1055. NtOfsInitializeMapHandle( &MapHandle );
  1056. InitializedMapHandle = TRUE;
  1057. //
  1058. // Acquire the object id index before doing the lookup.
  1059. // We need to make sure the file is acquired first to prevent
  1060. // a possible deadlock.
  1061. //
  1062. ASSERT_EXCLUSIVE_FCB( Fcb );
  1063. NtfsAcquireExclusiveScb( IrpContext, Vcb->ObjectIdTableScb );
  1064. IndexAcquired = TRUE;
  1065. if ( NtOfsFindRecord( IrpContext,
  1066. Vcb->ObjectIdTableScb,
  1067. &IndexKey,
  1068. &IndexRow,
  1069. &MapHandle,
  1070. NULL) != STATUS_SUCCESS ) {
  1071. try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND );
  1072. }
  1073. ASSERT( IndexRow.DataPart.DataLength == sizeof( NTFS_OBJECTID_INFORMATION ) );
  1074. //
  1075. // Copy objectid info into the correct buffer if we need it for the notify
  1076. // below.
  1077. //
  1078. if ((Vcb->ViewIndexNotifyCount != 0) &&
  1079. (IndexRow.DataPart.DataLength == sizeof( NTFS_OBJECTID_INFORMATION ))) {
  1080. //
  1081. // The FRS field is only populated for the notification of a failed
  1082. // object id restore from the tunnel cache.
  1083. //
  1084. FileObjectIdInfo.FileReference = 0L;
  1085. RtlCopyMemory( &FileObjectIdInfo.ObjectId,
  1086. ObjectIdBuffer.ObjectId,
  1087. OBJECT_ID_KEY_LENGTH );
  1088. RtlCopyMemory( &FileObjectIdInfo.ExtendedInfo,
  1089. ((NTFS_OBJECTID_INFORMATION *)IndexRow.DataPart.Data)->ExtendedInfo,
  1090. OBJECT_ID_EXT_INFO_LENGTH );
  1091. }
  1092. //
  1093. // Remove ObjectId from the index.
  1094. //
  1095. NtOfsDeleteRecords( IrpContext,
  1096. Vcb->ObjectIdTableScb,
  1097. 1, // deleting one record from the index
  1098. &IndexKey );
  1099. //
  1100. // Notify anybody who's interested. We use a different action if the
  1101. // object id is being deleted by the fsctl versus a delete file.
  1102. //
  1103. if (Vcb->ViewIndexNotifyCount != 0) {
  1104. NtfsReportViewIndexNotify( Vcb,
  1105. Vcb->ObjectIdTableScb->Fcb,
  1106. FILE_NOTIFY_CHANGE_FILE_NAME,
  1107. (DeleteFileAttribute ?
  1108. FILE_ACTION_REMOVED :
  1109. FILE_ACTION_REMOVED_BY_DELETE),
  1110. &FileObjectIdInfo,
  1111. sizeof(FILE_OBJECTID_INFORMATION) );
  1112. }
  1113. if (DeleteFileAttribute) {
  1114. //
  1115. // Post the change to the Usn Journal (on errors change is backed out)
  1116. //
  1117. NtfsPostUsnChange( IrpContext, Fcb, USN_REASON_OBJECT_ID_CHANGE );
  1118. //
  1119. // Now remove the object id attribute from the file.
  1120. //
  1121. NtfsInitializeAttributeContext( &AttributeContext );
  1122. InitializedAttributeContext = TRUE;
  1123. if (NtfsLookupAttributeByCode( IrpContext,
  1124. Fcb,
  1125. &Fcb->FileReference,
  1126. $OBJECT_ID,
  1127. &AttributeContext )) {
  1128. NtfsDeleteAttributeRecord( IrpContext,
  1129. Fcb,
  1130. DELETE_LOG_OPERATION |
  1131. DELETE_RELEASE_FILE_RECORD |
  1132. DELETE_RELEASE_ALLOCATION,
  1133. &AttributeContext );
  1134. } else {
  1135. //
  1136. // If the object id was in the index, but the attribute
  1137. // isn't on the file, then the object id index for this
  1138. // volume is corrupt. We can repair this corruption in
  1139. // the background, so let's start doing that now.
  1140. //
  1141. NtfsPostSpecial( IrpContext, Vcb, NtfsRepairObjectId, NULL );
  1142. }
  1143. NtfsCleanupTransaction( IrpContext, Status, FALSE );
  1144. }
  1145. try_exit: NOTHING;
  1146. } finally {
  1147. if (InitializedMapHandle) {
  1148. NtOfsReleaseMap( IrpContext, &MapHandle );
  1149. }
  1150. if (InitializedAttributeContext) {
  1151. NtfsCleanupAttributeContext( IrpContext, &AttributeContext );
  1152. }
  1153. }
  1154. return Status;
  1155. }
  1156. VOID
  1157. NtfsRepairObjectId (
  1158. IN PIRP_CONTEXT IrpContext,
  1159. IN PVOID Context
  1160. )
  1161. /*++
  1162. Routine Description:
  1163. This routine is called to repair the object Id index. This is called when
  1164. the system detects that the object Id index may be out of date. For example
  1165. after the volume was mounted on 4.0.
  1166. Arguments:
  1167. IrpContext - context of the call
  1168. Context - NULL
  1169. Return Value:
  1170. None
  1171. --*/
  1172. {
  1173. NTSTATUS Status = STATUS_SUCCESS;
  1174. BOOLEAN AcquiredVcb = FALSE;
  1175. BOOLEAN SetRepairFlag = FALSE;
  1176. BOOLEAN IncrementedCloseCounts = FALSE;
  1177. PBCB Bcb = NULL;
  1178. PVCB Vcb = IrpContext->Vcb;
  1179. PSCB ObjectIdScb;
  1180. PREAD_CONTEXT ReadContext = NULL;
  1181. PINDEX_ROW IndexRow = NULL;
  1182. PINDEX_ROW ObjectIdRow;
  1183. INDEX_KEY IndexKey;
  1184. MAP_HANDLE MapHandle;
  1185. PNTFS_OBJECTID_INFORMATION ObjectIdInfo;
  1186. PVOID RowBuffer = NULL;
  1187. ULONG Count;
  1188. ULONG i;
  1189. BOOLEAN IndexAcquired = FALSE;
  1190. FILE_OBJECTID_BUFFER ObjectIdBuffer;
  1191. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  1192. LONGLONG MftOffset;
  1193. PAGED_CODE();
  1194. UNREFERENCED_PARAMETER( Context );
  1195. ASSERT( Vcb->MajorVersion >= NTFS_OBJECT_ID_VERSION );
  1196. //
  1197. // Use a try-except to catch errors.
  1198. //
  1199. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  1200. AcquiredVcb = TRUE;
  1201. try {
  1202. //
  1203. // Now that we're holding the Vcb, we can safely test for the presence
  1204. // of the ObjectId index, as well as whether the volume is mounted.
  1205. //
  1206. if (FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED ) &&
  1207. (Vcb->ObjectIdTableScb != NULL) &&
  1208. (!FlagOn( Vcb->ObjectIdTableScb->ScbState, SCB_STATE_VOLUME_DISMOUNTED ))) {
  1209. ObjectIdScb = Vcb->ObjectIdTableScb;
  1210. NtfsAcquireExclusiveScb( IrpContext, ObjectIdScb );
  1211. IndexAcquired = TRUE;
  1212. //
  1213. // Since we'll be dropping the ObjectIdScb periodically, and we're
  1214. // not holding anything else, there's a chance that a dismount could
  1215. // happen, and make it unsafe for us to reacquire the ObjectIdScb.
  1216. // By incrementing the close counts, we keep it around as long as
  1217. // we need it.
  1218. //
  1219. NtfsIncrementCloseCounts( ObjectIdScb, TRUE, FALSE );
  1220. IncrementedCloseCounts = TRUE;
  1221. NtfsReleaseVcb( IrpContext, Vcb );
  1222. AcquiredVcb = FALSE;
  1223. } else {
  1224. NtfsRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED, NULL, NULL );
  1225. }
  1226. //
  1227. // The volume could've gotten write-protected by now.
  1228. //
  1229. if (NtfsIsVolumeReadOnly( Vcb )) {
  1230. NtfsRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED, NULL, NULL );
  1231. }
  1232. if (!FlagOn( Vcb->ObjectIdState, VCB_OBJECT_ID_REPAIR_RUNNING )) {
  1233. SetFlag( Vcb->ObjectIdState, VCB_OBJECT_ID_REPAIR_RUNNING );
  1234. SetRepairFlag = TRUE;
  1235. //
  1236. // Check the object id index. Periodically release all resources.
  1237. // See NtfsClearAndVerifyQuotaIndex
  1238. //
  1239. NtOfsInitializeMapHandle( &MapHandle );
  1240. //
  1241. // Allocate a buffer large enough for several rows.
  1242. //
  1243. RowBuffer = NtfsAllocatePool( PagedPool, PAGE_SIZE );
  1244. try {
  1245. //
  1246. // Allocate a bunch of index row entries.
  1247. //
  1248. Count = PAGE_SIZE / sizeof( NTFS_OBJECTID_INFORMATION );
  1249. IndexRow = NtfsAllocatePool( PagedPool,
  1250. Count * sizeof( INDEX_ROW ) );
  1251. //
  1252. // Iterate through the object id entries. Start at the beginning.
  1253. //
  1254. RtlZeroMemory( &ObjectIdBuffer, sizeof(ObjectIdBuffer) );
  1255. IndexKey.Key = ObjectIdBuffer.ObjectId;
  1256. IndexKey.KeyLength = sizeof( ObjectIdBuffer.ObjectId );
  1257. Status = NtOfsReadRecords( IrpContext,
  1258. ObjectIdScb,
  1259. &ReadContext,
  1260. &IndexKey,
  1261. NtOfsMatchAll,
  1262. NULL,
  1263. &Count,
  1264. IndexRow,
  1265. PAGE_SIZE,
  1266. RowBuffer );
  1267. while (NT_SUCCESS( Status )) {
  1268. //
  1269. // Acquire the VCB shared and check whether we should
  1270. // continue.
  1271. //
  1272. if (!NtfsIsVcbAvailable( Vcb )) {
  1273. //
  1274. // The volume is going away, bail out.
  1275. //
  1276. Status = STATUS_VOLUME_DISMOUNTED;
  1277. leave;
  1278. }
  1279. ObjectIdRow = IndexRow;
  1280. for (i = 0; i < Count; i++, ObjectIdRow++) {
  1281. ObjectIdInfo = ObjectIdRow->DataPart.Data;
  1282. //
  1283. // Make sure the mft record referenced in the index
  1284. // row still exists and hasn't been deleted, etc.
  1285. //
  1286. // We start by reading the disk and checking that the file record
  1287. // sequence number matches and that the file record is in use. If
  1288. // we find an invalid entry, we will simply delete it from the
  1289. // object id index.
  1290. //
  1291. MftOffset = NtfsFullSegmentNumber( &ObjectIdInfo->FileSystemReference );
  1292. MftOffset = Int64ShllMod32(MftOffset, Vcb->MftShift);
  1293. if (MftOffset >= Vcb->MftScb->Header.FileSize.QuadPart) {
  1294. DebugTrace( 0, Dbg, ("File Id doesn't lie within Mft FRS %04x:%08lx\n",
  1295. ObjectIdInfo->FileSystemReference.SequenceNumber,
  1296. ObjectIdInfo->FileSystemReference.SegmentNumberLowPart) );
  1297. NtOfsDeleteRecords( IrpContext,
  1298. ObjectIdScb,
  1299. 1, // deleting one record from the index
  1300. &ObjectIdRow->KeyPart );
  1301. } else {
  1302. NtfsReadMftRecord( IrpContext,
  1303. Vcb,
  1304. &ObjectIdInfo->FileSystemReference,
  1305. FALSE,
  1306. &Bcb,
  1307. &FileRecord,
  1308. NULL );
  1309. //
  1310. // This file record better be in use, have a matching sequence number and
  1311. // be the primary file record for this file.
  1312. //
  1313. if ((*((PULONG) FileRecord->MultiSectorHeader.Signature) != *((PULONG) FileSignature)) ||
  1314. !FlagOn( FileRecord->Flags, FILE_RECORD_SEGMENT_IN_USE ) ||
  1315. (FileRecord->SequenceNumber != ObjectIdInfo->FileSystemReference.SequenceNumber) ||
  1316. (*((PLONGLONG) &FileRecord->BaseFileRecordSegment) != 0)) {
  1317. DebugTrace( 0, Dbg, ("RepairOID removing an orphaned OID\n") );
  1318. NtOfsDeleteRecords( IrpContext,
  1319. ObjectIdScb,
  1320. 1, // deleting one record from the index
  1321. &ObjectIdRow->KeyPart );
  1322. } else {
  1323. DebugTrace( 0, Dbg, ("RepairOID happy with OID %08lx on FRS %04x:%08lx\n",
  1324. *((PULONG) ObjectIdRow->KeyPart.Key),
  1325. ObjectIdInfo->FileSystemReference.SequenceNumber,
  1326. ObjectIdInfo->FileSystemReference.SegmentNumberLowPart) );
  1327. }
  1328. NtfsUnpinBcb( IrpContext, &Bcb );
  1329. }
  1330. }
  1331. //
  1332. // Release the index and commit what has been done so far.
  1333. //
  1334. ASSERT( IndexAcquired );
  1335. NtfsReleaseScb( IrpContext, ObjectIdScb );
  1336. IndexAcquired = FALSE;
  1337. //
  1338. // Complete the request which commits the pending
  1339. // transaction if there is one and releases of the
  1340. // acquired resources. The IrpContext will not
  1341. // be deleted because the no delete flag is set.
  1342. //
  1343. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DONT_DELETE | IRP_CONTEXT_FLAG_RETAIN_FLAGS );
  1344. NtfsCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
  1345. //
  1346. // Remember how far we got so we can restart correctly. **** ??? ****
  1347. //
  1348. // Vcb->QuotaFileReference.SegmentNumberLowPart =
  1349. // *((PULONG) IndexRow[Count - 1].KeyPart.Key);
  1350. //
  1351. // Reacquire the object id index for the next pass.
  1352. //
  1353. NtfsAcquireExclusiveScb( IrpContext, ObjectIdScb );
  1354. IndexAcquired = TRUE;
  1355. //
  1356. // Make sure a dismount didn't occur while we weren't holding any
  1357. // resources.
  1358. //
  1359. if (FlagOn( ObjectIdScb->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) {
  1360. NtfsRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED, NULL, NULL );
  1361. }
  1362. //
  1363. // Look up the next set of entries in the object id index.
  1364. //
  1365. Count = PAGE_SIZE / sizeof( NTFS_OBJECTID_INFORMATION );
  1366. Status = NtOfsReadRecords( IrpContext,
  1367. ObjectIdScb,
  1368. &ReadContext,
  1369. NULL,
  1370. NtOfsMatchAll,
  1371. NULL,
  1372. &Count,
  1373. IndexRow,
  1374. PAGE_SIZE,
  1375. RowBuffer );
  1376. }
  1377. ASSERT( (Status == STATUS_NO_MORE_MATCHES) ||
  1378. (Status == STATUS_NO_MATCH) );
  1379. } finally {
  1380. NtfsUnpinBcb( IrpContext, &Bcb );
  1381. NtfsFreePool( RowBuffer );
  1382. NtOfsReleaseMap( IrpContext, &MapHandle );
  1383. if (IndexAcquired) {
  1384. NtfsReleaseScb( IrpContext, ObjectIdScb );
  1385. IndexAcquired = FALSE;
  1386. }
  1387. if (IndexRow != NULL) {
  1388. NtfsFreePool( IndexRow );
  1389. }
  1390. if (ReadContext != NULL) {
  1391. NtOfsFreeReadContext( ReadContext );
  1392. }
  1393. }
  1394. //
  1395. // Acquire the Vcb to clear the object ID flag on disk. Since we got the
  1396. // Vcb shared before, we better not still be holding it when we try to
  1397. // get it exclusively now or else we'll have a one thread deadlock.
  1398. //
  1399. ASSERT( !AcquiredVcb );
  1400. NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
  1401. AcquiredVcb = TRUE;
  1402. if (!NtfsIsVcbAvailable( Vcb )) {
  1403. NtfsRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED, NULL, NULL );
  1404. }
  1405. //
  1406. // Clear the on-disk flag indicating the repair is underway.
  1407. //
  1408. NtfsSetVolumeInfoFlagState( IrpContext,
  1409. Vcb,
  1410. VOLUME_REPAIR_OBJECT_ID,
  1411. FALSE,
  1412. TRUE );
  1413. //
  1414. // Make sure we don't own any resources at this point.
  1415. //
  1416. NtfsPurgeFileRecordCache( IrpContext );
  1417. NtfsCheckpointCurrentTransaction( IrpContext );
  1418. }
  1419. } except( NtfsExceptionFilter( IrpContext, GetExceptionInformation())) {
  1420. Status = IrpContext->TopLevelIrpContext->ExceptionStatus;
  1421. }
  1422. //
  1423. // Clear the repair_running flag if we're the ones who set it, making sure
  1424. // to only change the ObjectIdState bits while holding the ObjectId index.
  1425. //
  1426. if (SetRepairFlag) {
  1427. if (!IndexAcquired) {
  1428. NtfsAcquireExclusiveScb( IrpContext, ObjectIdScb );
  1429. IndexAcquired = TRUE;
  1430. }
  1431. ClearFlag( Vcb->ObjectIdState, VCB_OBJECT_ID_REPAIR_RUNNING );
  1432. }
  1433. if (IncrementedCloseCounts) {
  1434. if (!IndexAcquired) {
  1435. NtfsAcquireExclusiveScb( IrpContext, ObjectIdScb );
  1436. IndexAcquired = TRUE;
  1437. }
  1438. NtfsDecrementCloseCounts( IrpContext, ObjectIdScb, NULL, TRUE, FALSE, FALSE );
  1439. }
  1440. //
  1441. // Drop the index and the Vcb.
  1442. //
  1443. if (IndexAcquired) {
  1444. NtfsReleaseScb( IrpContext, ObjectIdScb );
  1445. }
  1446. if (AcquiredVcb) {
  1447. NtfsReleaseVcb( IrpContext, Vcb );
  1448. }
  1449. //
  1450. // If this is a fatal failure then do any final cleanup.
  1451. //
  1452. if (!NT_SUCCESS( Status )) {
  1453. //
  1454. // If we will not be called back then clear the running state bits.
  1455. //
  1456. if ((Status != STATUS_CANT_WAIT) && (Status != STATUS_LOG_FILE_FULL)) {
  1457. //
  1458. // Do we want to log this error? Some may be expected (i.e. STATUS_VOLUME_DISMOUNTED ).
  1459. //
  1460. // NtfsLogEvent( IrpContext, NULL, IO_FILE_OBJECTID_REPAIR_FAILED, Status );
  1461. }
  1462. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  1463. }
  1464. }
  1465. //
  1466. // Local support routine
  1467. //
  1468. NTSTATUS
  1469. NtfsSetObjectIdExtendedInfoInternal (
  1470. IN PIRP_CONTEXT IrpContext,
  1471. IN PFCB Fcb,
  1472. IN PVCB Vcb,
  1473. IN PUCHAR ExtendedInfoBuffer
  1474. )
  1475. /*++
  1476. Routine Description:
  1477. This routine sets the extended info for a file which already has an object
  1478. id. If the file does not yet have an object id, we return a status other
  1479. than STATUS_SUCCESS.
  1480. Arguments:
  1481. Fcb - The file whose extended info is to be set.
  1482. Vcb - The volume whose object id index the entry should be modified in.
  1483. ExtendedInfoBuffer - Supplies the new extended info.
  1484. Return Value:
  1485. NTSTATUS - The return status for the operation.
  1486. --*/
  1487. {
  1488. NTSTATUS Status;
  1489. NTFS_OBJECTID_INFORMATION ObjectIdInfo;
  1490. FILE_OBJECTID_BUFFER ObjectIdBuffer;
  1491. INDEX_ROW IndexRow;
  1492. PAGED_CODE( );
  1493. Status = NtfsGetObjectIdInternal( IrpContext,
  1494. Fcb,
  1495. FALSE, // GetExtendedInfo
  1496. &ObjectIdBuffer );
  1497. if (Status != STATUS_SUCCESS) {
  1498. //
  1499. // This file may not have an object id yet.
  1500. //
  1501. return Status;
  1502. }
  1503. //
  1504. // Setup the index row for updating. Since part of the data
  1505. // is passed into this function (the new extended info) and
  1506. // the rest can be determined easily (the file reference), we
  1507. // don't need to look up any of the existing data before
  1508. // proceeding. If the NTFS_OBJECTID_INFORMATION structure
  1509. // ever changes, this code may have to be changed to include
  1510. // a lookup of the data currently in the object id index.
  1511. //
  1512. RtlCopyMemory( &ObjectIdInfo.FileSystemReference,
  1513. &Fcb->FileReference,
  1514. sizeof( ObjectIdInfo.FileSystemReference ) );
  1515. RtlCopyMemory( &ObjectIdInfo.ExtendedInfo,
  1516. ExtendedInfoBuffer,
  1517. OBJECT_ID_EXT_INFO_LENGTH );
  1518. IndexRow.DataPart.Data = &ObjectIdInfo;
  1519. IndexRow.DataPart.DataLength = sizeof( NTFS_OBJECTID_INFORMATION );
  1520. IndexRow.KeyPart.Key = &ObjectIdBuffer;
  1521. IndexRow.KeyPart.KeyLength = OBJECT_ID_KEY_LENGTH;
  1522. //
  1523. // Acquire the object id index before doing the modification.
  1524. //
  1525. NtfsAcquireExclusiveScb( IrpContext, Vcb->ObjectIdTableScb );
  1526. //
  1527. // Post the change to the Usn Journal (on errors change is backed out)
  1528. //
  1529. NtfsPostUsnChange( IrpContext, Fcb, USN_REASON_OBJECT_ID_CHANGE );
  1530. //
  1531. // Update the ObjectId index record's data in place.
  1532. //
  1533. NtOfsUpdateRecord( IrpContext,
  1534. Vcb->ObjectIdTableScb,
  1535. 1, // Count
  1536. &IndexRow,
  1537. NULL, // QuickIndexHint
  1538. NULL ); // MapHandle
  1539. NtfsCleanupTransaction( IrpContext, Status, FALSE );
  1540. return Status;
  1541. }
  1542. //
  1543. // Local support routine
  1544. //
  1545. VOID
  1546. NtfsGetIdFromGenerator (
  1547. OUT PFILE_OBJECTID_BUFFER ObjectId
  1548. )
  1549. /*++
  1550. Routine Description:
  1551. This function conjures up a random object id.
  1552. Arguments:
  1553. ObjectId - The location where the generated object id will be stored.
  1554. Return Value:
  1555. None.
  1556. --*/
  1557. {
  1558. PAGED_CODE( );
  1559. //
  1560. // Cal the id generator.
  1561. //
  1562. ExUuidCreate( (UUID *)ObjectId->ObjectId );
  1563. }