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.

1810 lines
56 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. DirCtrl.c
  5. Abstract:
  6. This module implements the File Directory Control routine for Ntfs called
  7. by the dispatch driver.
  8. Author:
  9. Tom Miller [TomM] 1-Jan-1992
  10. (Based heavily on GaryKi's dirctrl.c for pinball.)
  11. Revision History:
  12. --*/
  13. #include "NtfsProc.h"
  14. //
  15. // The local debug trace level
  16. //
  17. #define Dbg (DEBUG_TRACE_DIRCTRL)
  18. //
  19. // Define a tag for general pool allocations from this module
  20. //
  21. #undef MODULE_POOL_TAG
  22. #define MODULE_POOL_TAG ('dFtN')
  23. NTSTATUS
  24. NtfsQueryDirectory (
  25. IN PIRP_CONTEXT IrpContext,
  26. IN PIRP Irp,
  27. IN PVCB Vcb,
  28. IN PSCB Scb,
  29. IN PCCB Ccb
  30. );
  31. NTSTATUS
  32. NtfsNotifyChangeDirectory (
  33. IN PIRP_CONTEXT IrpContext,
  34. IN PIRP Irp,
  35. IN PVCB Vcb,
  36. IN PSCB Scb,
  37. IN PCCB Ccb
  38. );
  39. #ifdef ALLOC_PRAGMA
  40. #pragma alloc_text(PAGE, NtfsCommonDirectoryControl)
  41. #pragma alloc_text(PAGE, NtfsFsdDirectoryControl)
  42. #pragma alloc_text(PAGE, NtfsNotifyChangeDirectory)
  43. #pragma alloc_text(PAGE, NtfsReportViewIndexNotify)
  44. #pragma alloc_text(PAGE, NtfsQueryDirectory)
  45. #endif
  46. NTSTATUS
  47. NtfsFsdDirectoryControl (
  48. IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
  49. IN PIRP Irp
  50. )
  51. /*++
  52. Routine Description:
  53. This routine implements the FSD part of Directory Control.
  54. Arguments:
  55. VolumeDeviceObject - Supplies the volume device object where the
  56. file exists
  57. Irp - Supplies the Irp being processed
  58. Return Value:
  59. NTSTATUS - The FSD status for the IRP
  60. --*/
  61. {
  62. TOP_LEVEL_CONTEXT TopLevelContext;
  63. PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
  64. NTSTATUS Status = STATUS_SUCCESS;
  65. PIRP_CONTEXT IrpContext = NULL;
  66. IRP_CONTEXT LocalIrpContext;
  67. BOOLEAN Wait;
  68. ASSERT_IRP( Irp );
  69. UNREFERENCED_PARAMETER( VolumeDeviceObject );
  70. PAGED_CODE();
  71. DebugTrace( +1, Dbg, ("NtfsFsdDirectoryControl\n") );
  72. //
  73. // Call the common Directory Control routine
  74. //
  75. FsRtlEnterFileSystem();
  76. //
  77. // Always make these requests look top level.
  78. //
  79. ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, TRUE, TRUE );
  80. do {
  81. try {
  82. //
  83. // We are either initiating this request or retrying it.
  84. //
  85. if (IrpContext == NULL) {
  86. //
  87. // Allocate and initialize the IrpContext.
  88. //
  89. Wait = FALSE;
  90. if (CanFsdWait( Irp )) {
  91. Wait = TRUE;
  92. IrpContext = &LocalIrpContext;
  93. }
  94. NtfsInitializeIrpContext( Irp, Wait, &IrpContext );
  95. //
  96. // Initialize the thread top level structure, if needed.
  97. //
  98. NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
  99. } else if (Status == STATUS_LOG_FILE_FULL) {
  100. NtfsCheckpointForLogFileFull( IrpContext );
  101. }
  102. Status = NtfsCommonDirectoryControl( IrpContext, Irp );
  103. break;
  104. } except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
  105. //
  106. // We had some trouble trying to perform the requested
  107. // operation, so we'll abort the I/O request with
  108. // the error status that we get back from the
  109. // execption code
  110. //
  111. Status = NtfsProcessException( IrpContext, Irp, GetExceptionCode() );
  112. }
  113. } while (Status == STATUS_CANT_WAIT ||
  114. Status == STATUS_LOG_FILE_FULL);
  115. ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
  116. FsRtlExitFileSystem();
  117. //
  118. // And return to our caller
  119. //
  120. DebugTrace( -1, Dbg, ("NtfsFsdDirectoryControl -> %08lx\n", Status) );
  121. return Status;
  122. }
  123. NTSTATUS
  124. NtfsCommonDirectoryControl (
  125. IN PIRP_CONTEXT IrpContext,
  126. IN PIRP Irp
  127. )
  128. /*++
  129. Routine Description:
  130. This is the common routine for Directory Control called by both the fsd
  131. and fsp threads.
  132. Arguments:
  133. Irp - Supplies the Irp to process
  134. Return Value:
  135. NTSTATUS - The return status for the operation
  136. --*/
  137. {
  138. NTSTATUS Status;
  139. PIO_STACK_LOCATION IrpSp;
  140. PFILE_OBJECT FileObject;
  141. TYPE_OF_OPEN TypeOfOpen;
  142. PVCB Vcb;
  143. PSCB Scb;
  144. PCCB Ccb;
  145. PFCB Fcb;
  146. ASSERT_IRP_CONTEXT( IrpContext );
  147. ASSERT_IRP( Irp );
  148. ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
  149. PAGED_CODE();
  150. //
  151. // Get the current Irp stack location
  152. //
  153. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  154. DebugTrace( +1, Dbg, ("NtfsCommonDirectoryControl\n") );
  155. DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
  156. DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
  157. //
  158. // Extract and decode the file object
  159. //
  160. FileObject = IrpSp->FileObject;
  161. TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
  162. //
  163. // We know this is a directory control so we'll case on the
  164. // minor function, and call an internal worker routine to complete
  165. // the irp.
  166. //
  167. switch ( IrpSp->MinorFunction ) {
  168. case IRP_MN_QUERY_DIRECTORY:
  169. //
  170. // Decide if this is a view or filename index.
  171. //
  172. if ((UserViewIndexOpen == TypeOfOpen) &&
  173. FlagOn( Scb->ScbState, SCB_STATE_VIEW_INDEX )) {
  174. Status = NtfsQueryViewIndex( IrpContext, Irp, Vcb, Scb, Ccb );
  175. } else if ((UserDirectoryOpen == TypeOfOpen) &&
  176. !FlagOn( Scb->ScbState, SCB_STATE_VIEW_INDEX )) {
  177. Status = NtfsQueryDirectory( IrpContext, Irp, Vcb, Scb, Ccb );
  178. } else {
  179. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  180. DebugTrace( -1, Dbg, ("NtfsCommonDirectoryControl -> STATUS_INVALID_PARAMETER\n") );
  181. return STATUS_INVALID_PARAMETER;
  182. }
  183. break;
  184. case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
  185. //
  186. // We can't perform this operation on open by Id or if the caller has
  187. // closed his handle. Make sure the handle is for either a view index
  188. // or file name index.
  189. //
  190. if (((TypeOfOpen != UserDirectoryOpen) &&
  191. (TypeOfOpen != UserViewIndexOpen)) ||
  192. FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_FILE_ID ) ||
  193. FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE )) {
  194. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  195. DebugTrace( -1, Dbg, ("NtfsCommonDirectoryControl -> STATUS_INVALID_PARAMETER\n") );
  196. return STATUS_INVALID_PARAMETER;
  197. }
  198. Status = NtfsNotifyChangeDirectory( IrpContext, Irp, Vcb, Scb, Ccb );
  199. break;
  200. default:
  201. DebugTrace( 0, Dbg, ("Invalid Minor Function %08lx\n", IrpSp->MinorFunction) );
  202. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
  203. Status = STATUS_INVALID_DEVICE_REQUEST;
  204. break;
  205. }
  206. //
  207. // And return to our caller
  208. //
  209. DebugTrace( -1, Dbg, ("NtfsCommonDirectoryControl -> %08lx\n", Status) );
  210. return Status;
  211. }
  212. VOID
  213. NtfsReportViewIndexNotify (
  214. IN PVCB Vcb,
  215. IN PFCB Fcb,
  216. IN ULONG FilterMatch,
  217. IN ULONG Action,
  218. IN PVOID ChangeInfoBuffer,
  219. IN USHORT ChangeInfoBufferLength
  220. )
  221. /*++
  222. Routine Description:
  223. This function notifies processes that there has been a change to a
  224. view index they are watching. It is analogous to the NtfsReportDirNotify
  225. macro, which is used only for directories, while this function is used
  226. only for view indices.
  227. Arguments:
  228. Vcb - The volume on which the change is taking place.
  229. Fcb - The file on which the change is taking place.
  230. FilterMatch - This flag field is compared with the completion filter
  231. in the notify structure. If any of the corresponding bits in the
  232. completion filter are set, then a notify condition exists.
  233. Action - This is the action code to store in the user's buffer if
  234. present.
  235. ChangeInfoBuffer - Pointer to a buffer of information related to the
  236. change being reported. This information is returned to the
  237. process that owns the notify handle.
  238. ChangeInfoBufferLength - The length, in bytes, of the buffer passed
  239. in ChangeInfoBuffer.
  240. Return Value:
  241. None.
  242. --*/
  243. {
  244. STRING ChangeInfo;
  245. PAGED_CODE( );
  246. ChangeInfo.Length = ChangeInfo.MaximumLength = ChangeInfoBufferLength;
  247. ChangeInfo.Buffer = ChangeInfoBuffer;
  248. FsRtlNotifyFilterReportChange( Vcb->NotifySync,
  249. &Vcb->ViewIndexNotifyList,
  250. NULL,
  251. 0,
  252. &ChangeInfo,
  253. &ChangeInfo,
  254. FilterMatch,
  255. Action,
  256. Fcb,
  257. NULL );
  258. }
  259. //
  260. // Local Support Routine
  261. //
  262. NTSTATUS
  263. NtfsQueryDirectory (
  264. IN PIRP_CONTEXT IrpContext,
  265. IN PIRP Irp,
  266. IN PVCB Vcb,
  267. IN PSCB Scb,
  268. IN PCCB Ccb
  269. )
  270. /*++
  271. Routine Description:
  272. This routine performs the query directory operation. It is responsible
  273. for either completing or enqueuing the input Irp.
  274. Arguments:
  275. Irp - Supplies the Irp to process
  276. Vcb - Supplies its Vcb
  277. Scb - Supplies its Scb
  278. Ccb - Supplies its Ccb
  279. Return Value:
  280. NTSTATUS - The return status for the operation
  281. --*/
  282. {
  283. NTSTATUS Status = STATUS_SUCCESS;
  284. PIO_STACK_LOCATION IrpSp;
  285. PUCHAR Buffer;
  286. CLONG UserBufferLength;
  287. ULONG BaseLength;
  288. PUNICODE_STRING UniFileName;
  289. FILE_INFORMATION_CLASS FileInformationClass;
  290. ULONG FileIndex;
  291. BOOLEAN RestartScan;
  292. BOOLEAN ReturnSingleEntry;
  293. BOOLEAN IndexSpecified;
  294. BOOLEAN AccessingUserBuffer = FALSE;
  295. BOOLEAN IgnoreCase;
  296. BOOLEAN NextFlag;
  297. BOOLEAN GotEntry;
  298. BOOLEAN CallRestart;
  299. ULONG NextEntry;
  300. ULONG LastEntry;
  301. PFILE_DIRECTORY_INFORMATION DirInfo;
  302. PFILE_FULL_DIR_INFORMATION FullDirInfo;
  303. PFILE_BOTH_DIR_INFORMATION BothDirInfo;
  304. PFILE_NAMES_INFORMATION NamesInfo;
  305. PFILE_NAME FileNameBuffer;
  306. PVOID UnwindFileNameBuffer = NULL;
  307. ULONG FileNameLength;
  308. ULONG SizeOfFileName = FIELD_OFFSET( FILE_NAME, FileName );
  309. INDEX_CONTEXT OtherContext;
  310. PFCB AcquiredFcb = NULL;
  311. BOOLEAN VcbAcquired = FALSE;
  312. BOOLEAN CcbAcquired = FALSE;
  313. BOOLEAN ScbAcquired = FALSE;
  314. BOOLEAN FirstQuery = FALSE;
  315. ASSERT_IRP_CONTEXT( IrpContext );
  316. ASSERT_IRP( Irp );
  317. ASSERT_VCB( Vcb );
  318. ASSERT_CCB( Ccb );
  319. ASSERT_SCB( Scb );
  320. PAGED_CODE();
  321. //
  322. // Get the current Stack location
  323. //
  324. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  325. DebugTrace( +1, Dbg, ("NtfsQueryDirectory...\n") );
  326. DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
  327. DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
  328. DebugTrace( 0, Dbg, (" ->Length = %08lx\n", IrpSp->Parameters.QueryDirectory.Length) );
  329. DebugTrace( 0, Dbg, (" ->FileName = %08lx\n", IrpSp->Parameters.QueryDirectory.FileName) );
  330. DebugTrace( 0, Dbg, (" ->FileInformationClass = %08lx\n", IrpSp->Parameters.QueryDirectory.FileInformationClass) );
  331. DebugTrace( 0, Dbg, (" ->FileIndex = %08lx\n", IrpSp->Parameters.QueryDirectory.FileIndex) );
  332. DebugTrace( 0, Dbg, (" ->SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer) );
  333. DebugTrace( 0, Dbg, (" ->RestartScan = %08lx\n", FlagOn(IrpSp->Flags, SL_RESTART_SCAN)) );
  334. DebugTrace( 0, Dbg, (" ->ReturnSingleEntry = %08lx\n", FlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY)) );
  335. DebugTrace( 0, Dbg, (" ->IndexSpecified = %08lx\n", FlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED)) );
  336. DebugTrace( 0, Dbg, ("Vcb = %08lx\n", Vcb) );
  337. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  338. DebugTrace( 0, Dbg, ("Ccb = %08lx\n", Ccb) );
  339. #if DBG
  340. //
  341. // Enable debug port displays when certain enumeration strings are given
  342. //
  343. #if NTFSPOOLCHECK
  344. if (IrpSp->Parameters.QueryDirectory.FileName != NULL) {
  345. if (IrpSp->Parameters.QueryDirectory.FileName->Length >= 10 &&
  346. RtlEqualMemory( IrpSp->Parameters.QueryDirectory.FileName->Buffer, L"$HEAP", 10 )) {
  347. NtfsDebugHeapDump( (PUNICODE_STRING) IrpSp->Parameters.QueryDirectory.FileName );
  348. }
  349. }
  350. #endif // NTFSPOOLCHECK
  351. #endif // DBG
  352. //
  353. // Because we probably need to do the I/O anyway we'll reject any request
  354. // right now that cannot wait for I/O. We do not want to abort after
  355. // processing a few index entries.
  356. //
  357. if (!FlagOn(IrpContext->State, IRP_CONTEXT_STATE_WAIT)) {
  358. DebugTrace( 0, Dbg, ("Automatically enqueue Irp to Fsp\n") );
  359. Status = NtfsPostRequest( IrpContext, Irp );
  360. DebugTrace( -1, Dbg, ("NtfsQueryDirectory -> %08lx\n", Status) );
  361. return Status;
  362. }
  363. //
  364. // Reference our input parameters to make things easier
  365. //
  366. UserBufferLength = IrpSp->Parameters.QueryDirectory.Length;
  367. FileInformationClass = IrpSp->Parameters.QueryDirectory.FileInformationClass;
  368. FileIndex = IrpSp->Parameters.QueryDirectory.FileIndex;
  369. //
  370. // Look in the Ccb to see the type of search.
  371. //
  372. IgnoreCase = BooleanFlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE );
  373. RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN);
  374. ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY);
  375. IndexSpecified = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED);
  376. //
  377. // Determine the size of the constant part of the structure.
  378. //
  379. switch (FileInformationClass) {
  380. case FileDirectoryInformation:
  381. BaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION,
  382. FileName[0] );
  383. break;
  384. case FileFullDirectoryInformation:
  385. BaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION,
  386. FileName[0] );
  387. break;
  388. case FileIdFullDirectoryInformation:
  389. BaseLength = FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION,
  390. FileName[0] );
  391. break;
  392. case FileNamesInformation:
  393. BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION,
  394. FileName[0] );
  395. break;
  396. case FileBothDirectoryInformation:
  397. BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION,
  398. FileName[0] );
  399. break;
  400. case FileIdBothDirectoryInformation:
  401. BaseLength = FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION,
  402. FileName[0] );
  403. break;
  404. default:
  405. Status = STATUS_INVALID_INFO_CLASS;
  406. NtfsCompleteRequest( IrpContext, Irp, Status );
  407. DebugTrace( -1, Dbg, ("NtfsQueryDirectory -> %08lx\n", Status) );
  408. return Status;
  409. }
  410. NtfsInitializeIndexContext( &OtherContext );
  411. //
  412. // Use a try-finally to facilitate cleanup.
  413. //
  414. try {
  415. //
  416. // We only allow one active request in this handle at a time. If this is
  417. // not a synchronous request then wait on the handle.
  418. //
  419. if (!FlagOn( IrpSp->FileObject->Flags, FO_SYNCHRONOUS_IO )) {
  420. EOF_WAIT_BLOCK WaitBlock;
  421. NtfsAcquireIndexCcb( Scb, Ccb, &WaitBlock );
  422. CcbAcquired = TRUE;
  423. }
  424. //
  425. // We have to create a File Name string for querying if there is either
  426. // one specified in this request, or we do not already have a value
  427. // in the Ccb. If we already have one then we will ignore the input
  428. // name in this case unless the INDEX_SPECIFIED bit is set.
  429. //
  430. if ((Ccb->QueryBuffer == NULL) ||
  431. ((IrpSp->Parameters.QueryDirectory.FileName != NULL) && IndexSpecified)) {
  432. //
  433. // Now, if the input string is NULL, we have to create the default
  434. // string "*".
  435. //
  436. if (IrpSp->Parameters.QueryDirectory.FileName == NULL) {
  437. FileNameLength = SizeOfFileName + sizeof(WCHAR);
  438. FileNameBuffer = NtfsAllocatePool(PagedPool, FileNameLength );
  439. //
  440. // Initialize it.
  441. //
  442. FileNameBuffer->ParentDirectory = Scb->Fcb->FileReference;
  443. FileNameBuffer->FileNameLength = 1;
  444. FileNameBuffer->Flags = 0;
  445. FileNameBuffer->FileName[0] = '*';
  446. //
  447. // We know we have an input file name, and we may or may not already
  448. // have one in the Ccb. Allocate space for it, initialize it, and
  449. // set up to deallocate on the way out if we already have a pattern
  450. // in the Ccb.
  451. //
  452. } else {
  453. UniFileName = (PUNICODE_STRING) IrpSp->Parameters.QueryDirectory.FileName;
  454. if (!NtfsIsFileNameValid(UniFileName, TRUE)) {
  455. if (Ccb->QueryBuffer == NULL
  456. || UniFileName->Length > 4
  457. || UniFileName->Length == 0
  458. || UniFileName->Buffer[0] != L'.'
  459. || (UniFileName->Length == 4
  460. && UniFileName->Buffer[1] != L'.')) {
  461. try_return( Status = STATUS_OBJECT_NAME_INVALID );
  462. }
  463. }
  464. FileNameLength = (USHORT)IrpSp->Parameters.QueryDirectory.FileName->Length;
  465. FileNameBuffer = NtfsAllocatePool(PagedPool, SizeOfFileName + FileNameLength );
  466. RtlCopyMemory( FileNameBuffer->FileName,
  467. UniFileName->Buffer,
  468. FileNameLength );
  469. FileNameLength += SizeOfFileName;
  470. FileNameBuffer->ParentDirectory = Scb->Fcb->FileReference;
  471. FileNameBuffer->FileNameLength = (UCHAR)((FileNameLength - SizeOfFileName) / sizeof( WCHAR ));
  472. FileNameBuffer->Flags = 0;
  473. }
  474. //
  475. // If we already have a query buffer, deallocate this on the way
  476. // out.
  477. //
  478. if (Ccb->QueryBuffer != NULL) {
  479. //
  480. // If we have a name to resume from then override the restart
  481. // scan boolean.
  482. //
  483. if ((UnwindFileNameBuffer = FileNameBuffer) != NULL) {
  484. RestartScan = FALSE;
  485. }
  486. //
  487. // Otherwise, store this one in the Ccb.
  488. //
  489. } else {
  490. UNICODE_STRING Expression;
  491. Ccb->QueryBuffer = (PVOID)FileNameBuffer;
  492. Ccb->QueryLength = (USHORT)FileNameLength;
  493. FirstQuery = TRUE;
  494. //
  495. // If the search expression contains a wild card then remember this in
  496. // the Ccb.
  497. //
  498. Expression.MaximumLength =
  499. Expression.Length = FileNameBuffer->FileNameLength * sizeof( WCHAR );
  500. Expression.Buffer = FileNameBuffer->FileName;
  501. //
  502. // When we establish the search pattern, we must also establish
  503. // whether the user wants to see "." and "..". This code does
  504. // not necessarily have to be perfect (he said), but should be
  505. // good enough to catch the common cases. Dos does not have
  506. // perfect semantics for these cases, and the following determination
  507. // will mimic what FastFat does exactly.
  508. //
  509. if (Scb != Vcb->RootIndexScb) {
  510. static UNICODE_STRING DotString = CONSTANT_UNICODE_STRING( L"." );
  511. if (FsRtlDoesNameContainWildCards(&Expression)) {
  512. if (FsRtlIsNameInExpression( &Expression,
  513. &DotString,
  514. FALSE,
  515. NULL )) {
  516. SetFlag( Ccb->Flags, CCB_FLAG_RETURN_DOT | CCB_FLAG_RETURN_DOTDOT );
  517. }
  518. } else {
  519. if (NtfsAreNamesEqual( Vcb->UpcaseTable, &Expression, &DotString, FALSE )) {
  520. SetFlag( Ccb->Flags, CCB_FLAG_RETURN_DOT | CCB_FLAG_RETURN_DOTDOT );
  521. }
  522. }
  523. }
  524. }
  525. //
  526. // Otherwise we are just restarting the query from the Ccb.
  527. //
  528. } else {
  529. FileNameBuffer = (PFILE_NAME)Ccb->QueryBuffer;
  530. FileNameLength = Ccb->QueryLength;
  531. }
  532. Irp->IoStatus.Information = 0;
  533. //
  534. // Use a try-except to handle errors accessing the user buffer.
  535. //
  536. try {
  537. ULONG BytesToCopy;
  538. FCB_TABLE_ELEMENT Key;
  539. PFCB_TABLE_ELEMENT Entry;
  540. BOOLEAN MatchAll = FALSE;
  541. //
  542. // See if we are supposed to try to acquire an Fcb on this
  543. // resume.
  544. //
  545. if (Ccb->FcbToAcquire.LongValue != 0) {
  546. //
  547. // First we need to acquire the Vcb shared, since we will
  548. // acquire two Fcbs.
  549. //
  550. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  551. VcbAcquired = TRUE;
  552. //
  553. // Now look up the Fcb, and if it is there, reference it
  554. // and remember it.
  555. //
  556. Key.FileReference = Ccb->FcbToAcquire.FileReference;
  557. NtfsAcquireFcbTable( IrpContext, Vcb );
  558. Entry = RtlLookupElementGenericTable( &Vcb->FcbTable, &Key );
  559. if (Entry != NULL) {
  560. AcquiredFcb = Entry->Fcb;
  561. AcquiredFcb->ReferenceCount += 1;
  562. }
  563. NtfsReleaseFcbTable( IrpContext, Vcb );
  564. //
  565. // Now that it cannot go anywhere, acquire it.
  566. //
  567. if (AcquiredFcb != NULL) {
  568. NtfsAcquireSharedFcb( IrpContext, AcquiredFcb, NULL, ACQUIRE_NO_DELETE_CHECK );
  569. }
  570. //
  571. // Now that we actually acquired it, we may as well clear this
  572. // field.
  573. //
  574. Ccb->FcbToAcquire.LongValue = 0;
  575. }
  576. //
  577. // Acquire shared access to the Scb.
  578. //
  579. NtfsAcquireSharedScb( IrpContext, Scb );
  580. ScbAcquired = TRUE;
  581. //
  582. // Now that we have both files acquired, we can free the Vcb.
  583. //
  584. if (VcbAcquired) {
  585. NtfsReleaseVcb( IrpContext, Vcb );
  586. VcbAcquired = FALSE;
  587. }
  588. //
  589. // If the volume is no longer mounted, we should fail this
  590. // request. Since we have the Scb shared now, we know that
  591. // a dismount request can't sneak in.
  592. //
  593. if (FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) {
  594. try_return( Status = STATUS_VOLUME_DISMOUNTED );
  595. }
  596. //
  597. // If we are in the Fsp now because we had to wait earlier,
  598. // we must map the user buffer, otherwise we can use the
  599. // user's buffer directly.
  600. //
  601. Buffer = NtfsMapUserBuffer( Irp );
  602. //
  603. // Check if this is the first call to query directory for this file
  604. // object. It is the first call if the enumeration context field of
  605. // the ccb is null. Also check if we are to restart the scan.
  606. //
  607. if (FirstQuery || RestartScan) {
  608. CallRestart = TRUE;
  609. NextFlag = FALSE;
  610. //
  611. // On first/restarted scan, note that we have not returned either
  612. // of these guys.
  613. //
  614. ClearFlag( Ccb->Flags, CCB_FLAG_DOT_RETURNED | CCB_FLAG_DOTDOT_RETURNED );
  615. //
  616. // Otherwise check to see if we were given a file name to restart from
  617. //
  618. } else if (UnwindFileNameBuffer != NULL) {
  619. CallRestart = TRUE;
  620. NextFlag = TRUE;
  621. //
  622. // The guy could actually be asking to return to one of the dot
  623. // file positions, so we must handle that correctly.
  624. //
  625. if ((FileNameBuffer->FileNameLength <= 2) &&
  626. (FileNameBuffer->FileName[0] == L'.')) {
  627. if (FileNameBuffer->FileNameLength == 1) {
  628. //
  629. // He wants to resume after ".", so we set to return
  630. // ".." again, and change the temporary pattern to
  631. // rewind our context to the front.
  632. //
  633. ClearFlag( Ccb->Flags, CCB_FLAG_DOTDOT_RETURNED );
  634. SetFlag( Ccb->Flags, CCB_FLAG_DOT_RETURNED );
  635. FileNameBuffer->FileName[0] = L'*';
  636. NextFlag = FALSE;
  637. } else if (FileNameBuffer->FileName[1] == L'.') {
  638. //
  639. // He wants to resume after "..", so we the change
  640. // the temporary pattern to rewind our context to the
  641. // front.
  642. //
  643. SetFlag( Ccb->Flags, CCB_FLAG_DOT_RETURNED | CCB_FLAG_DOTDOT_RETURNED );
  644. FileNameBuffer->FileName[0] =
  645. FileNameBuffer->FileName[1] = L'*';
  646. NextFlag = FALSE;
  647. }
  648. //
  649. // Always return the entry after the user's file name.
  650. //
  651. } else {
  652. SetFlag( Ccb->Flags, CCB_FLAG_DOT_RETURNED | CCB_FLAG_DOTDOT_RETURNED );
  653. }
  654. //
  655. // Otherwise we're simply continuing a previous enumeration from
  656. // where we last left off. And we always leave off one beyond the
  657. // last entry we returned.
  658. //
  659. } else {
  660. CallRestart = FALSE;
  661. NextFlag = FALSE;
  662. }
  663. //
  664. // At this point we are about to enter our query loop. We have
  665. // already decided if we need to call restart or continue when we
  666. // go after an index entry. The variables LastEntry and NextEntry are
  667. // used to index into the user buffer. LastEntry is the last entry
  668. // we added to the user buffer, and NextEntry is the current
  669. // one we're working on.
  670. //
  671. LastEntry = 0;
  672. NextEntry = 0;
  673. //
  674. // Remember if we are matching everything by checking these two common
  675. // cases.
  676. //
  677. MatchAll = (FileNameBuffer->FileName[0] == L'*')
  678. &&
  679. ((FileNameBuffer->FileNameLength == 1) ||
  680. ((FileNameBuffer->FileNameLength == 3) &&
  681. (FileNameBuffer->FileName[1] == L'.') &&
  682. (FileNameBuffer->FileName[2] == L'*')));
  683. while (TRUE) {
  684. PINDEX_ENTRY IndexEntry;
  685. PFILE_NAME NtfsFileName;
  686. PDUPLICATED_INFORMATION DupInfo;
  687. PFILE_NAME DosFileName;
  688. FILE_REFERENCE FileId;
  689. ULONG BytesRemainingInBuffer;
  690. ULONG FoundFileNameLength;
  691. struct {
  692. FILE_NAME FileName;
  693. WCHAR LastChar;
  694. } DotDotName;
  695. BOOLEAN SynchronizationError;
  696. DebugTrace( 0, Dbg, ("Top of Loop\n") );
  697. DebugTrace( 0, Dbg, ("LastEntry = %08lx\n", LastEntry) );
  698. DebugTrace( 0, Dbg, ("NextEntry = %08lx\n", NextEntry) );
  699. //
  700. // If a previous pass through the loop acquired the Fcb table then
  701. // release it now. We don't want to be holding it if we take a fault
  702. // on the directory stream. Otherwise we can get into a circular
  703. // deadlock if we need to acquire the mutex for this file while
  704. // holding the mutex for the Fcb Table.
  705. //
  706. if (FlagOn( OtherContext.Flags, INDX_CTX_FLAG_FCB_TABLE_ACQUIRED )) {
  707. NtfsReleaseFcbTable( IrpContext, IrpContext->Vcb );
  708. ClearFlag( OtherContext.Flags, INDX_CTX_FLAG_FCB_TABLE_ACQUIRED );
  709. }
  710. DosFileName = NULL;
  711. //
  712. // Lookup the next index entry. Check if we need to do the lookup
  713. // by calling restart or continue. If we do need to call restart
  714. // check to see if we have a real AnsiFileName. And set ourselves
  715. // up for subsequent iternations through the loop
  716. //
  717. if (CallRestart) {
  718. GotEntry = NtfsRestartIndexEnumeration( IrpContext,
  719. Ccb,
  720. Scb,
  721. (PVOID)FileNameBuffer,
  722. IgnoreCase,
  723. NextFlag,
  724. &IndexEntry,
  725. AcquiredFcb );
  726. CallRestart = FALSE;
  727. } else {
  728. GotEntry = NtfsContinueIndexEnumeration( IrpContext,
  729. Ccb,
  730. Scb,
  731. NextFlag,
  732. &IndexEntry );
  733. }
  734. //
  735. // Check to see if we should quit the loop because we are only
  736. // returning a single entry. We actually want to spin around
  737. // the loop top twice so that our enumeration has has us left off
  738. // at the last entry we didn't return. We know this is now our
  739. // second time though the loop if NextEntry is not zero.
  740. //
  741. if ((ReturnSingleEntry) && (NextEntry != 0)) {
  742. break;
  743. }
  744. //
  745. // Assume we won't be returning the file id.
  746. //
  747. *((PLONGLONG) &FileId) = 0;
  748. //
  749. // Assume we are to return one of the names "." or "..".
  750. // We should not search farther in the index so we set
  751. // NextFlag to FALSE.
  752. //
  753. RtlZeroMemory( &DotDotName, sizeof(DotDotName) );
  754. NtfsFileName = &DotDotName.FileName;
  755. NtfsFileName->Flags = FILE_NAME_NTFS | FILE_NAME_DOS;
  756. NtfsFileName->FileName[0] =
  757. NtfsFileName->FileName[1] = L'.';
  758. DupInfo = &Scb->Fcb->Info;
  759. NextFlag = FALSE;
  760. //
  761. // Handle "." first.
  762. //
  763. if (!FlagOn(Ccb->Flags, CCB_FLAG_DOT_RETURNED) &&
  764. FlagOn(Ccb->Flags, CCB_FLAG_RETURN_DOT)) {
  765. FoundFileNameLength = 2;
  766. GotEntry = TRUE;
  767. SetFlag( Ccb->Flags, CCB_FLAG_DOT_RETURNED );
  768. FileId = Scb->Fcb->FileReference;
  769. //
  770. // Handle ".." next.
  771. //
  772. } else if (!FlagOn(Ccb->Flags, CCB_FLAG_DOTDOT_RETURNED) &&
  773. FlagOn(Ccb->Flags, CCB_FLAG_RETURN_DOTDOT)) {
  774. FoundFileNameLength = 4;
  775. GotEntry = TRUE;
  776. SetFlag( Ccb->Flags, CCB_FLAG_DOTDOT_RETURNED );
  777. } else {
  778. //
  779. // Compute the length of the name we found.
  780. //
  781. if (GotEntry) {
  782. FileId = IndexEntry->FileReference;
  783. NtfsFileName = (PFILE_NAME)(IndexEntry + 1);
  784. FoundFileNameLength = NtfsFileName->FileNameLength * sizeof( WCHAR );
  785. //
  786. // Verify the index entry is valid.
  787. //
  788. if (FoundFileNameLength != IndexEntry->AttributeLength - SizeOfFileName) {
  789. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  790. }
  791. DupInfo = &NtfsFileName->Info;
  792. NextFlag = TRUE;
  793. //
  794. // Don't return any system files.
  795. //
  796. if (NtfsSegmentNumber( &IndexEntry->FileReference ) < FIRST_USER_FILE_NUMBER &&
  797. NtfsProtectSystemFiles) {
  798. continue;
  799. }
  800. }
  801. }
  802. //
  803. // Now check to see if we actually got another index entry. If
  804. // we didn't then we also need to check if we never got any
  805. // or if we just ran out. If we just ran out then we break out
  806. // of the main loop and finish the Irp after the loop
  807. //
  808. if (!GotEntry) {
  809. DebugTrace( 0, Dbg, ("GotEntry is FALSE\n") );
  810. if (NextEntry == 0) {
  811. if (FirstQuery) {
  812. try_return( Status = STATUS_NO_SUCH_FILE );
  813. }
  814. try_return( Status = STATUS_NO_MORE_FILES );
  815. }
  816. break;
  817. }
  818. //
  819. // Cleanup and reinitialize context from previous loop.
  820. //
  821. NtfsReinitializeIndexContext( IrpContext, &OtherContext );
  822. //
  823. // We may have matched a Dos-Only name. If so we will save
  824. // it and go get the Ntfs name.
  825. //
  826. if (!FlagOn(NtfsFileName->Flags, FILE_NAME_NTFS) &&
  827. FlagOn(NtfsFileName->Flags, FILE_NAME_DOS)) {
  828. //
  829. // If we are returning everything, then we can skip
  830. // the Dos-Only names and save some cycles.
  831. //
  832. if (MatchAll) {
  833. continue;
  834. }
  835. DosFileName = NtfsFileName;
  836. NtfsFileName = NtfsRetrieveOtherFileName( IrpContext,
  837. Ccb,
  838. Scb,
  839. IndexEntry,
  840. &OtherContext,
  841. AcquiredFcb,
  842. &SynchronizationError );
  843. //
  844. // If we got an Ntfs name, then we need to list this entry now
  845. // iff the Ntfs name is not in the expression. If the Ntfs
  846. // name is in the expression, we can just continue and print
  847. // this name when we encounter it by the Ntfs name.
  848. //
  849. if (NtfsFileName != NULL) {
  850. if (FlagOn(Ccb->Flags, CCB_FLAG_WILDCARD_IN_EXPRESSION)) {
  851. if (NtfsFileNameIsInExpression( Vcb->UpcaseTable,
  852. (PFILE_NAME)Ccb->QueryBuffer,
  853. NtfsFileName,
  854. IgnoreCase )) {
  855. continue;
  856. }
  857. } else {
  858. if (NtfsFileNameIsEqual( Vcb->UpcaseTable,
  859. (PFILE_NAME)Ccb->QueryBuffer,
  860. NtfsFileName,
  861. IgnoreCase )) {
  862. continue;
  863. }
  864. }
  865. FoundFileNameLength = NtfsFileName->FileNameLength * sizeof( WCHAR );
  866. } else if (SynchronizationError) {
  867. if (Irp->IoStatus.Information != 0) {
  868. try_return( Status = STATUS_SUCCESS );
  869. } else {
  870. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  871. }
  872. } else {
  873. continue;
  874. }
  875. }
  876. //
  877. // Here are the rules concerning filling up the buffer:
  878. //
  879. // 1. The Io system garentees that there will always be
  880. // enough room for at least one base record.
  881. //
  882. // 2. If the full first record (including file name) cannot
  883. // fit, as much of the name as possible is copied and
  884. // STATUS_BUFFER_OVERFLOW is returned.
  885. //
  886. // 3. If a subsequent record cannot completely fit into the
  887. // buffer, none of it (as in 0 bytes) is copied, and
  888. // STATUS_SUCCESS is returned. A subsequent query will
  889. // pick up with this record.
  890. //
  891. BytesRemainingInBuffer = UserBufferLength - NextEntry;
  892. if ( (NextEntry != 0) &&
  893. ( (BaseLength + FoundFileNameLength > BytesRemainingInBuffer) ||
  894. (UserBufferLength < NextEntry) ) ) {
  895. DebugTrace( 0, Dbg, ("Next entry won't fit\n") );
  896. try_return( Status = STATUS_SUCCESS );
  897. }
  898. ASSERT( BytesRemainingInBuffer >= BaseLength );
  899. //
  900. // Zero the base part of the structure.
  901. //
  902. AccessingUserBuffer = TRUE;
  903. RtlZeroMemory( &Buffer[NextEntry], BaseLength );
  904. AccessingUserBuffer = FALSE;
  905. //
  906. // Now we have an entry to return to our caller. we'll
  907. // case on the type of information requested and fill up the
  908. // user buffer if everything fits
  909. //
  910. switch (FileInformationClass) {
  911. case FileIdFullDirectoryInformation:
  912. ((PFILE_ID_FULL_DIR_INFORMATION)&Buffer[NextEntry])->FileId.QuadPart = *((PLONGLONG) &FileId);
  913. goto FillFullDirectoryInformation;
  914. case FileIdBothDirectoryInformation:
  915. ((PFILE_ID_BOTH_DIR_INFORMATION)&Buffer[NextEntry])->FileId.QuadPart = *((PLONGLONG) &FileId);
  916. // Fall thru
  917. case FileBothDirectoryInformation:
  918. BothDirInfo = (PFILE_BOTH_DIR_INFORMATION)&Buffer[NextEntry];
  919. //
  920. // If this is not also a Dos name, and the Ntfs flag is set
  921. // (meaning there is a separate Dos name), then call the
  922. // routine to get the short name, if we do not already have
  923. // it from above.
  924. //
  925. if (!FlagOn(NtfsFileName->Flags, FILE_NAME_DOS) &&
  926. FlagOn(NtfsFileName->Flags, FILE_NAME_NTFS)) {
  927. if (DosFileName == NULL) {
  928. DosFileName = NtfsRetrieveOtherFileName( IrpContext,
  929. Ccb,
  930. Scb,
  931. IndexEntry,
  932. &OtherContext,
  933. AcquiredFcb,
  934. &SynchronizationError );
  935. }
  936. if (DosFileName != NULL) {
  937. AccessingUserBuffer = TRUE;
  938. BothDirInfo->ShortNameLength = DosFileName->FileNameLength * sizeof( WCHAR );
  939. RtlCopyMemory( BothDirInfo->ShortName,
  940. DosFileName->FileName,
  941. BothDirInfo->ShortNameLength );
  942. } else if (SynchronizationError) {
  943. if (Irp->IoStatus.Information != 0) {
  944. try_return( Status = STATUS_SUCCESS );
  945. } else {
  946. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  947. }
  948. }
  949. }
  950. // Fallthru
  951. case FileFullDirectoryInformation:
  952. FillFullDirectoryInformation:
  953. DebugTrace( 0, Dbg, ("Getting file full Unicode directory information\n") );
  954. FullDirInfo = (PFILE_FULL_DIR_INFORMATION)&Buffer[NextEntry];
  955. //
  956. // EAs and reparse points cannot both be in a file at the same
  957. // time. We return different information for each case.
  958. //
  959. AccessingUserBuffer = TRUE;
  960. if (FlagOn( DupInfo->FileAttributes, FILE_ATTRIBUTE_REPARSE_POINT)) {
  961. FullDirInfo->EaSize = DupInfo->ReparsePointTag;
  962. } else {
  963. FullDirInfo->EaSize = DupInfo->PackedEaSize;
  964. //
  965. // Add 4 bytes for the CbListHeader.
  966. //
  967. if (DupInfo->PackedEaSize != 0) {
  968. FullDirInfo->EaSize += 4;
  969. }
  970. }
  971. // Fallthru
  972. case FileDirectoryInformation:
  973. DebugTrace( 0, Dbg, ("Getting file Unicode directory information\n") );
  974. DirInfo = (PFILE_DIRECTORY_INFORMATION)&Buffer[NextEntry];
  975. AccessingUserBuffer = TRUE;
  976. DirInfo->CreationTime.QuadPart = DupInfo->CreationTime;
  977. DirInfo->LastAccessTime.QuadPart = DupInfo->LastAccessTime;
  978. DirInfo->LastWriteTime.QuadPart = DupInfo->LastModificationTime;
  979. DirInfo->ChangeTime.QuadPart = DupInfo->LastChangeTime;
  980. DirInfo->FileAttributes = DupInfo->FileAttributes & FILE_ATTRIBUTE_VALID_FLAGS;
  981. if (IsDirectory( DupInfo ) || IsViewIndex( DupInfo )) {
  982. DirInfo->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
  983. }
  984. if (DirInfo->FileAttributes == 0) {
  985. DirInfo->FileAttributes = FILE_ATTRIBUTE_NORMAL;
  986. }
  987. DirInfo->FileNameLength = FoundFileNameLength;
  988. DirInfo->EndOfFile.QuadPart = DupInfo->FileSize;
  989. DirInfo->AllocationSize.QuadPart = DupInfo->AllocatedLength;
  990. break;
  991. case FileNamesInformation:
  992. DebugTrace( 0, Dbg, ("Getting file Unicode names information\n") );
  993. AccessingUserBuffer = TRUE;
  994. NamesInfo = (PFILE_NAMES_INFORMATION)&Buffer[NextEntry];
  995. NamesInfo->FileNameLength = FoundFileNameLength;
  996. break;
  997. default:
  998. try_return( Status = STATUS_INVALID_INFO_CLASS );
  999. }
  1000. //
  1001. // Compute how many bytes we can copy. This should only be less
  1002. // than the file name length if we are only returning a single
  1003. // entry.
  1004. //
  1005. if (BytesRemainingInBuffer >= BaseLength + FoundFileNameLength) {
  1006. BytesToCopy = FoundFileNameLength;
  1007. } else {
  1008. BytesToCopy = BytesRemainingInBuffer - BaseLength;
  1009. Status = STATUS_BUFFER_OVERFLOW;
  1010. }
  1011. ASSERT( AccessingUserBuffer );
  1012. RtlCopyMemory( &Buffer[NextEntry + BaseLength],
  1013. NtfsFileName->FileName,
  1014. BytesToCopy );
  1015. //
  1016. // If/when we actually emit a record for the Fcb acquired,
  1017. // then we can release that file now. Note we do not just
  1018. // do it on the first time through the loop, because some of
  1019. // our callers back up a bit when they give us the resume point.
  1020. //
  1021. if ((AcquiredFcb != NULL) &&
  1022. (DupInfo != &Scb->Fcb->Info) &&
  1023. NtfsEqualMftRef(&IndexEntry->FileReference, &Ccb->FcbToAcquire.FileReference)) {
  1024. //
  1025. // Now look up the Fcb, and if it is there, reference it
  1026. // and remember it.
  1027. //
  1028. // It is pretty inconvenient here to see if the ReferenceCount
  1029. // goes to zero and try to do a TearDown, we do not have the
  1030. // right resources. Note that the window is small, and the Fcb
  1031. // will go away if either someone opens the file again, someone
  1032. // tries to delete the directory, or someone tries to lock the
  1033. // volume.
  1034. //
  1035. NtfsAcquireFcbTable( IrpContext, Vcb );
  1036. AcquiredFcb->ReferenceCount -= 1;
  1037. NtfsReleaseFcbTable( IrpContext, Vcb );
  1038. NtfsReleaseFcb( IrpContext, AcquiredFcb );
  1039. AcquiredFcb = NULL;
  1040. }
  1041. //
  1042. // Set up the previous next entry offset
  1043. //
  1044. *((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry;
  1045. AccessingUserBuffer = FALSE;
  1046. //
  1047. // And indicate how much of the user buffer we have currently
  1048. // used up. We must compute this value before we long align
  1049. // ourselves for the next entry. This is the point where we
  1050. // quad-align the length of the previous entry.
  1051. //
  1052. Irp->IoStatus.Information = QuadAlign( Irp->IoStatus.Information) +
  1053. BaseLength + BytesToCopy;
  1054. //
  1055. // If we weren't able to copy the whole name, then we bail here.
  1056. //
  1057. if ( !NT_SUCCESS( Status ) ) {
  1058. try_return( Status );
  1059. }
  1060. //
  1061. // Set ourselves up for the next iteration
  1062. //
  1063. LastEntry = NextEntry;
  1064. NextEntry += (ULONG)QuadAlign( BaseLength + BytesToCopy );
  1065. }
  1066. //
  1067. // At this point we've successfully filled up some of the buffer so
  1068. // now is the time to set our status to success.
  1069. //
  1070. Status = STATUS_SUCCESS;
  1071. } except( (!FsRtlIsNtstatusExpected( GetExceptionCode() ) && AccessingUserBuffer) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
  1072. NtfsRaiseStatus( IrpContext, STATUS_INVALID_USER_BUFFER, NULL, NULL );
  1073. }
  1074. try_exit:
  1075. //
  1076. // Abort transaction on error by raising.
  1077. //
  1078. NtfsCleanupTransaction( IrpContext, Status, FALSE );
  1079. //
  1080. // Set the last access flag in the Fcb if the caller
  1081. // didn't set it explicitly.
  1082. //
  1083. if (!FlagOn( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS_TIME ) &&
  1084. !FlagOn( NtfsData.Flags, NTFS_FLAGS_DISABLE_LAST_ACCESS )) {
  1085. NtfsGetCurrentTime( IrpContext, Scb->Fcb->CurrentLastAccess );
  1086. SetFlag( Scb->Fcb->InfoFlags, FCB_INFO_UPDATE_LAST_ACCESS );
  1087. }
  1088. } finally {
  1089. DebugUnwind( NtfsQueryDirectory );
  1090. if (VcbAcquired) {
  1091. NtfsReleaseVcb( IrpContext, Vcb );
  1092. }
  1093. NtfsCleanupIndexContext( IrpContext, &OtherContext );
  1094. if (AcquiredFcb != NULL) {
  1095. //
  1096. // Now look up the Fcb, and if it is there, reference it
  1097. // and remember it.
  1098. //
  1099. // It is pretty inconvenient here to see if the ReferenceCount
  1100. // goes to zero and try to do a TearDown, we do not have the
  1101. // right resources. Note that the window is small, and the Fcb
  1102. // will go away if either someone opens the file again, someone
  1103. // tries to delete the directory, or someone tries to lock the
  1104. // volume.
  1105. //
  1106. NtfsAcquireFcbTable( IrpContext, Vcb );
  1107. AcquiredFcb->ReferenceCount -= 1;
  1108. NtfsReleaseFcbTable( IrpContext, Vcb );
  1109. NtfsReleaseFcb( IrpContext, AcquiredFcb );
  1110. }
  1111. if (ScbAcquired) {
  1112. NtfsReleaseScb( IrpContext, Scb );
  1113. }
  1114. NtfsCleanupAfterEnumeration( IrpContext, Ccb );
  1115. if (CcbAcquired) {
  1116. NtfsReleaseIndexCcb( Scb, Ccb );
  1117. }
  1118. if (!AbnormalTermination()) {
  1119. NtfsCompleteRequest( IrpContext, Irp, Status );
  1120. }
  1121. if (UnwindFileNameBuffer != NULL) {
  1122. NtfsFreePool(UnwindFileNameBuffer);
  1123. }
  1124. }
  1125. //
  1126. // And return to our caller
  1127. //
  1128. DebugTrace( -1, Dbg, ("NtfsQueryDirectory -> %08lx\n", Status) );
  1129. return Status;
  1130. }
  1131. //
  1132. // Local Support Routine
  1133. //
  1134. NTSTATUS
  1135. NtfsNotifyChangeDirectory (
  1136. IN PIRP_CONTEXT IrpContext,
  1137. IN PIRP Irp,
  1138. IN PVCB Vcb,
  1139. IN PSCB Scb,
  1140. IN PCCB Ccb
  1141. )
  1142. /*++
  1143. Routine Description:
  1144. This routine performs the notify change directory operation. It is
  1145. responsible for either completing or enqueuing the input Irp.
  1146. Arguments:
  1147. Irp - Supplies the Irp to process
  1148. Vcb - Supplies its Vcb
  1149. Scb - Supplies its Scb
  1150. Ccb - Supplies its Ccb
  1151. Return Value:
  1152. NTSTATUS - The return status for the operation
  1153. --*/
  1154. {
  1155. NTSTATUS Status;
  1156. PIO_STACK_LOCATION IrpSp;
  1157. ULONG CompletionFilter;
  1158. BOOLEAN WatchTree;
  1159. BOOLEAN ViewIndex;
  1160. PSECURITY_SUBJECT_CONTEXT SubjectContext = NULL;
  1161. BOOLEAN FreeSubjectContext = FALSE;
  1162. PCHECK_FOR_TRAVERSE_ACCESS CallBack = NULL;
  1163. ASSERT_IRP_CONTEXT( IrpContext );
  1164. ASSERT_IRP( Irp );
  1165. ASSERT_VCB( Vcb );
  1166. ASSERT_CCB( Ccb );
  1167. ASSERT_SCB( Scb );
  1168. PAGED_CODE();
  1169. //
  1170. // Get the current Stack location
  1171. //
  1172. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  1173. DebugTrace( +1, Dbg, ("NtfsNotifyChangeDirectory...\n") );
  1174. DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
  1175. DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
  1176. DebugTrace( 0, Dbg, (" ->CompletionFilter = %08lx\n", IrpSp->Parameters.NotifyDirectory.CompletionFilter) );
  1177. DebugTrace( 0, Dbg, (" ->WatchTree = %08lx\n", FlagOn( IrpSp->Flags, SL_WATCH_TREE )) );
  1178. DebugTrace( 0, Dbg, ("Vcb = %08lx\n", Vcb) );
  1179. DebugTrace( 0, Dbg, ("Ccb = %08lx\n", Ccb) );
  1180. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  1181. //
  1182. // Reference our input parameter to make things easier
  1183. //
  1184. CompletionFilter = IrpSp->Parameters.NotifyDirectory.CompletionFilter;
  1185. WatchTree = BooleanFlagOn( IrpSp->Flags, SL_WATCH_TREE );
  1186. //
  1187. // Always set the wait bit in the IrpContext so the initial wait can't fail.
  1188. //
  1189. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  1190. //
  1191. // We will only acquire the Vcb to perform the dirnotify task. The dirnotify
  1192. // package will provide synchronization between this operation and cleanup.
  1193. // We need the Vcb to synchronize with any rename or link operations underway.
  1194. //
  1195. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  1196. try {
  1197. //
  1198. // If the Link count is zero on this Fcb then complete this request
  1199. // with STATUS_DELETE_PENDING.
  1200. //
  1201. if (Scb->Fcb->LinkCount == 0) {
  1202. NtfsRaiseStatus( IrpContext, STATUS_DELETE_PENDING, NULL, NULL );
  1203. }
  1204. ViewIndex = BooleanFlagOn( Scb->ScbState, SCB_STATE_VIEW_INDEX );
  1205. //
  1206. // If we need to verify traverse access for this caller then allocate and
  1207. // capture the subject context to pass to the dir notify package. That
  1208. // package will be responsible for deallocating it.
  1209. //
  1210. if (FlagOn( Ccb->Flags, CCB_FLAG_TRAVERSE_CHECK )) {
  1211. //
  1212. // We only use the subject context for directories
  1213. //
  1214. if (!ViewIndex) {
  1215. SubjectContext = NtfsAllocatePool( PagedPool,
  1216. sizeof( SECURITY_SUBJECT_CONTEXT ));
  1217. FreeSubjectContext = TRUE;
  1218. SeCaptureSubjectContext( SubjectContext );
  1219. FreeSubjectContext = FALSE;
  1220. }
  1221. CallBack = NtfsNotifyTraverseCheck;
  1222. }
  1223. //
  1224. // Call the Fsrtl package to process the request. We cast the
  1225. // unicode strings to ansi strings as the dir notify package
  1226. // only deals with memory matching.
  1227. //
  1228. if (ViewIndex) {
  1229. //
  1230. // View indices use different values for the overloaded inputs
  1231. // to FsRtlNotifyFilterChangeDirectory.
  1232. //
  1233. FsRtlNotifyFilterChangeDirectory( Vcb->NotifySync,
  1234. &Vcb->ViewIndexNotifyList,
  1235. Ccb,
  1236. NULL,
  1237. WatchTree,
  1238. FALSE,
  1239. CompletionFilter,
  1240. Irp,
  1241. CallBack,
  1242. (PSECURITY_SUBJECT_CONTEXT) Scb->Fcb,
  1243. NULL );
  1244. } else {
  1245. FsRtlNotifyFilterChangeDirectory( Vcb->NotifySync,
  1246. &Vcb->DirNotifyList,
  1247. Ccb,
  1248. (PSTRING) &Scb->ScbType.Index.NormalizedName,
  1249. WatchTree,
  1250. FALSE,
  1251. CompletionFilter,
  1252. Irp,
  1253. CallBack,
  1254. SubjectContext,
  1255. NULL );
  1256. }
  1257. Status = STATUS_PENDING;
  1258. if (!FlagOn( Ccb->Flags, CCB_FLAG_DIR_NOTIFY )) {
  1259. SetFlag( Ccb->Flags, CCB_FLAG_DIR_NOTIFY );
  1260. if (ViewIndex) {
  1261. InterlockedIncrement( &Vcb->ViewIndexNotifyCount );
  1262. } else {
  1263. InterlockedIncrement( &Vcb->NotifyCount );
  1264. }
  1265. }
  1266. } finally {
  1267. DebugUnwind( NtfsNotifyChangeDirectory );
  1268. NtfsReleaseVcb( IrpContext, Vcb );
  1269. //
  1270. // Since the dir notify package is holding the Irp, we discard the
  1271. // the IrpContext.
  1272. //
  1273. if (!AbnormalTermination()) {
  1274. NtfsCompleteRequest( IrpContext, NULL, 0 );
  1275. } else if (FreeSubjectContext) {
  1276. NtfsFreePool( SubjectContext );
  1277. }
  1278. }
  1279. //
  1280. // And return to our caller
  1281. //
  1282. DebugTrace( -1, Dbg, ("NtfsNotifyChangeDirectory -> %08lx\n", Status) );
  1283. return Status;
  1284. }