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

1857 lines
59 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( 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 = 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) && (UniFileName->Buffer[1] != L'.'))) {
  460. try_return( Status = STATUS_OBJECT_NAME_INVALID );
  461. }
  462. }
  463. FileNameLength = (USHORT)IrpSp->Parameters.QueryDirectory.FileName->Length;
  464. FileNameBuffer = NtfsAllocatePool(PagedPool, SizeOfFileName + FileNameLength );
  465. RtlCopyMemory( FileNameBuffer->FileName,
  466. UniFileName->Buffer,
  467. FileNameLength );
  468. FileNameLength += SizeOfFileName;
  469. FileNameBuffer->ParentDirectory = Scb->Fcb->FileReference;
  470. FileNameBuffer->FileNameLength = (UCHAR)((FileNameLength - SizeOfFileName) / sizeof( WCHAR ));
  471. FileNameBuffer->Flags = 0;
  472. }
  473. //
  474. // If we already have a query buffer, deallocate this on the way
  475. // out.
  476. //
  477. if (Ccb->QueryBuffer != NULL) {
  478. //
  479. // If we have a name to resume from then override the restart
  480. // scan boolean.
  481. //
  482. if ((UnwindFileNameBuffer = FileNameBuffer) != NULL) {
  483. RestartScan = FALSE;
  484. }
  485. //
  486. // Otherwise, store this one in the Ccb.
  487. //
  488. } else {
  489. UNICODE_STRING Expression;
  490. Ccb->QueryBuffer = (PVOID)FileNameBuffer;
  491. Ccb->QueryLength = (USHORT)FileNameLength;
  492. FirstQuery = TRUE;
  493. //
  494. // If the search expression contains a wild card then remember this in
  495. // the Ccb.
  496. //
  497. Expression.MaximumLength =
  498. Expression.Length = FileNameBuffer->FileNameLength * sizeof( WCHAR );
  499. Expression.Buffer = FileNameBuffer->FileName;
  500. //
  501. // When we establish the search pattern, we must also establish
  502. // whether the user wants to see "." and "..". This code does
  503. // not necessarily have to be perfect (he said), but should be
  504. // good enough to catch the common cases. Dos does not have
  505. // perfect semantics for these cases, and the following determination
  506. // will mimic what FastFat does exactly.
  507. //
  508. if (Scb != Vcb->RootIndexScb) {
  509. static UNICODE_STRING DotString = CONSTANT_UNICODE_STRING( L"." );
  510. if (FsRtlDoesNameContainWildCards(&Expression)) {
  511. if (FsRtlIsNameInExpression( &Expression,
  512. &DotString,
  513. FALSE,
  514. NULL )) {
  515. SetFlag( Ccb->Flags, CCB_FLAG_RETURN_DOT | CCB_FLAG_RETURN_DOTDOT );
  516. }
  517. } else {
  518. if (NtfsAreNamesEqual( Vcb->UpcaseTable, &Expression, &DotString, FALSE )) {
  519. SetFlag( Ccb->Flags, CCB_FLAG_RETURN_DOT | CCB_FLAG_RETURN_DOTDOT );
  520. }
  521. }
  522. }
  523. }
  524. //
  525. // Otherwise we are just restarting the query from the Ccb.
  526. //
  527. } else {
  528. FileNameBuffer = (PFILE_NAME)Ccb->QueryBuffer;
  529. FileNameLength = Ccb->QueryLength;
  530. }
  531. Irp->IoStatus.Information = 0;
  532. //
  533. // Use a try-except to handle errors accessing the user buffer.
  534. //
  535. try {
  536. ULONG BytesToCopy;
  537. FCB_TABLE_ELEMENT Key;
  538. PFCB_TABLE_ELEMENT Entry;
  539. BOOLEAN MatchAll = FALSE;
  540. //
  541. // See if we are supposed to try to acquire an Fcb on this
  542. // resume.
  543. //
  544. if (Ccb->FcbToAcquire.LongValue != 0) {
  545. //
  546. // First we need to acquire the Vcb shared, since we will
  547. // acquire two Fcbs.
  548. //
  549. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  550. VcbAcquired = TRUE;
  551. //
  552. // Now look up the Fcb, and if it is there, reference it
  553. // and remember it.
  554. //
  555. Key.FileReference = Ccb->FcbToAcquire.FileReference;
  556. NtfsAcquireFcbTable( IrpContext, Vcb );
  557. Entry = RtlLookupElementGenericTable( &Vcb->FcbTable, &Key );
  558. if (Entry != NULL) {
  559. AcquiredFcb = Entry->Fcb;
  560. AcquiredFcb->ReferenceCount += 1;
  561. }
  562. NtfsReleaseFcbTable( IrpContext, Vcb );
  563. //
  564. // Now that it cannot go anywhere, acquire it.
  565. //
  566. if (AcquiredFcb != NULL) {
  567. NtfsAcquireSharedFcb( IrpContext, AcquiredFcb, NULL, ACQUIRE_NO_DELETE_CHECK );
  568. }
  569. //
  570. // Now that we actually acquired it, we may as well clear this
  571. // field.
  572. //
  573. Ccb->FcbToAcquire.LongValue = 0;
  574. }
  575. //
  576. // Acquire shared access to the Scb.
  577. //
  578. NtfsAcquireSharedScb( IrpContext, Scb );
  579. ScbAcquired = TRUE;
  580. //
  581. // Now that we have both files acquired, we can free the Vcb.
  582. //
  583. if (VcbAcquired) {
  584. NtfsReleaseVcb( IrpContext, Vcb );
  585. VcbAcquired = FALSE;
  586. }
  587. //
  588. // If the volume is no longer mounted, we should fail this
  589. // request. Since we have the Scb shared now, we know that
  590. // a dismount request can't sneak in.
  591. //
  592. if (FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) {
  593. try_return( Status = STATUS_VOLUME_DISMOUNTED );
  594. }
  595. //
  596. // If we are in the Fsp now because we had to wait earlier,
  597. // we must map the user buffer, otherwise we can use the
  598. // user's buffer directly.
  599. //
  600. Buffer = NtfsMapUserBuffer( Irp, NormalPagePriority );
  601. //
  602. // Check if this is the first call to query directory for this file
  603. // object. It is the first call if the enumeration context field of
  604. // the ccb is null. Also check if we are to restart the scan.
  605. //
  606. if (FirstQuery || RestartScan) {
  607. CallRestart = TRUE;
  608. NextFlag = FALSE;
  609. //
  610. // On first/restarted scan, note that we have not returned either
  611. // of these guys.
  612. //
  613. ClearFlag( Ccb->Flags, CCB_FLAG_DOT_RETURNED | CCB_FLAG_DOTDOT_RETURNED );
  614. //
  615. // Otherwise check to see if we were given a file name to restart from
  616. //
  617. } else if (UnwindFileNameBuffer != NULL) {
  618. CallRestart = TRUE;
  619. NextFlag = TRUE;
  620. //
  621. // The guy could actually be asking to return to one of the dot
  622. // file positions, so we must handle that correctly.
  623. //
  624. if ((FileNameBuffer->FileNameLength <= 2) &&
  625. (FileNameBuffer->FileName[0] == L'.')) {
  626. if (FileNameBuffer->FileNameLength == 1) {
  627. //
  628. // He wants to resume after ".", so we set to return
  629. // ".." again, and change the temporary pattern to
  630. // rewind our context to the front.
  631. //
  632. ClearFlag( Ccb->Flags, CCB_FLAG_DOTDOT_RETURNED );
  633. SetFlag( Ccb->Flags, CCB_FLAG_DOT_RETURNED );
  634. FileNameBuffer->FileName[0] = L'*';
  635. NextFlag = FALSE;
  636. } else if (FileNameBuffer->FileName[1] == L'.') {
  637. //
  638. // He wants to resume after "..", so we the change
  639. // the temporary pattern to rewind our context to the
  640. // front.
  641. //
  642. SetFlag( Ccb->Flags, CCB_FLAG_DOT_RETURNED | CCB_FLAG_DOTDOT_RETURNED );
  643. FileNameBuffer->FileName[0] =
  644. FileNameBuffer->FileName[1] = L'*';
  645. NextFlag = FALSE;
  646. }
  647. //
  648. // Always return the entry after the user's file name.
  649. //
  650. } else {
  651. SetFlag( Ccb->Flags, CCB_FLAG_DOT_RETURNED | CCB_FLAG_DOTDOT_RETURNED );
  652. }
  653. //
  654. // Otherwise we're simply continuing a previous enumeration from
  655. // where we last left off. And we always leave off one beyond the
  656. // last entry we returned.
  657. //
  658. } else {
  659. CallRestart = FALSE;
  660. NextFlag = FALSE;
  661. }
  662. //
  663. // At this point we are about to enter our query loop. We have
  664. // already decided if we need to call restart or continue when we
  665. // go after an index entry. The variables LastEntry and NextEntry are
  666. // used to index into the user buffer. LastEntry is the last entry
  667. // we added to the user buffer, and NextEntry is the current
  668. // one we're working on.
  669. //
  670. LastEntry = 0;
  671. NextEntry = 0;
  672. //
  673. // Remember if we are matching everything by checking these two common
  674. // cases.
  675. //
  676. MatchAll = (FileNameBuffer->FileName[0] == L'*')
  677. &&
  678. ((FileNameBuffer->FileNameLength == 1) ||
  679. ((FileNameBuffer->FileNameLength == 3) &&
  680. (FileNameBuffer->FileName[1] == L'.') &&
  681. (FileNameBuffer->FileName[2] == L'*')));
  682. while (TRUE) {
  683. PINDEX_ENTRY IndexEntry;
  684. PFILE_NAME NtfsFileName;
  685. PDUPLICATED_INFORMATION DupInfo;
  686. PFILE_NAME DosFileName;
  687. FILE_REFERENCE FileId;
  688. ULONG BytesRemainingInBuffer;
  689. ULONG FoundFileNameLength;
  690. struct {
  691. FILE_NAME FileName;
  692. WCHAR LastChar;
  693. } DotDotName;
  694. BOOLEAN SynchronizationError;
  695. DebugTrace( 0, Dbg, ("Top of Loop\n") );
  696. DebugTrace( 0, Dbg, ("LastEntry = %08lx\n", LastEntry) );
  697. DebugTrace( 0, Dbg, ("NextEntry = %08lx\n", NextEntry) );
  698. //
  699. // If a previous pass through the loop acquired the Fcb table then
  700. // release it now. We don't want to be holding it if we take a fault
  701. // on the directory stream. Otherwise we can get into a circular
  702. // deadlock if we need to acquire the mutex for this file while
  703. // holding the mutex for the Fcb Table.
  704. //
  705. if (FlagOn( OtherContext.Flags, INDX_CTX_FLAG_FCB_TABLE_ACQUIRED )) {
  706. NtfsReleaseFcbTable( IrpContext, IrpContext->Vcb );
  707. ClearFlag( OtherContext.Flags, INDX_CTX_FLAG_FCB_TABLE_ACQUIRED );
  708. }
  709. DosFileName = NULL;
  710. //
  711. // Lookup the next index entry. Check if we need to do the lookup
  712. // by calling restart or continue. If we do need to call restart
  713. // check to see if we have a real AnsiFileName. And set ourselves
  714. // up for subsequent iternations through the loop
  715. //
  716. if (CallRestart) {
  717. GotEntry = NtfsRestartIndexEnumeration( IrpContext,
  718. Ccb,
  719. Scb,
  720. (PVOID)FileNameBuffer,
  721. IgnoreCase,
  722. NextFlag,
  723. &IndexEntry,
  724. AcquiredFcb );
  725. CallRestart = FALSE;
  726. } else {
  727. GotEntry = NtfsContinueIndexEnumeration( IrpContext,
  728. Ccb,
  729. Scb,
  730. NextFlag,
  731. &IndexEntry );
  732. }
  733. //
  734. // Check to see if we should quit the loop because we are only
  735. // returning a single entry. We actually want to spin around
  736. // the loop top twice so that our enumeration has has us left off
  737. // at the last entry we didn't return. We know this is now our
  738. // second time though the loop if NextEntry is not zero.
  739. //
  740. if ((ReturnSingleEntry) && (NextEntry != 0)) {
  741. break;
  742. }
  743. //
  744. // Assume we won't be returning the file id.
  745. //
  746. *((PLONGLONG) &FileId) = 0;
  747. //
  748. // Assume we are to return one of the names "." or "..".
  749. // We should not search farther in the index so we set
  750. // NextFlag to FALSE.
  751. //
  752. RtlZeroMemory( &DotDotName, sizeof(DotDotName) );
  753. NtfsFileName = &DotDotName.FileName;
  754. NtfsFileName->Flags = FILE_NAME_NTFS | FILE_NAME_DOS;
  755. NtfsFileName->FileName[0] =
  756. NtfsFileName->FileName[1] = L'.';
  757. DupInfo = &Scb->Fcb->Info;
  758. NextFlag = FALSE;
  759. //
  760. // Handle "." first.
  761. //
  762. if (!FlagOn( Ccb->Flags, CCB_FLAG_DOT_RETURNED ) &&
  763. FlagOn( Ccb->Flags, CCB_FLAG_RETURN_DOT )) {
  764. FoundFileNameLength = 2;
  765. GotEntry = TRUE;
  766. SetFlag( Ccb->Flags, CCB_FLAG_DOT_RETURNED );
  767. FileId = Scb->Fcb->FileReference;
  768. //
  769. // Handle ".." next.
  770. //
  771. } else if (!FlagOn(Ccb->Flags, CCB_FLAG_DOTDOT_RETURNED) &&
  772. FlagOn(Ccb->Flags, CCB_FLAG_RETURN_DOTDOT)) {
  773. FoundFileNameLength = 4;
  774. GotEntry = TRUE;
  775. SetFlag( Ccb->Flags, CCB_FLAG_DOTDOT_RETURNED );
  776. } else {
  777. //
  778. // Compute the length of the name we found.
  779. //
  780. if (GotEntry) {
  781. FileId = IndexEntry->FileReference;
  782. NtfsFileName = (PFILE_NAME)(IndexEntry + 1);
  783. FoundFileNameLength = NtfsFileName->FileNameLength * sizeof( WCHAR );
  784. //
  785. // Verify the index entry is valid.
  786. //
  787. if (FoundFileNameLength != IndexEntry->AttributeLength - SizeOfFileName) {
  788. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  789. }
  790. DupInfo = &NtfsFileName->Info;
  791. NextFlag = TRUE;
  792. //
  793. // Don't return any system files.
  794. //
  795. if (NtfsSegmentNumber( &IndexEntry->FileReference ) < FIRST_USER_FILE_NUMBER &&
  796. NtfsProtectSystemFiles) {
  797. continue;
  798. }
  799. }
  800. }
  801. //
  802. // Now check to see if we actually got another index entry. If
  803. // we didn't then we also need to check if we never got any
  804. // or if we just ran out. If we just ran out then we break out
  805. // of the main loop and finish the Irp after the loop
  806. //
  807. if (!GotEntry) {
  808. DebugTrace( 0, Dbg, ("GotEntry is FALSE\n") );
  809. if (NextEntry == 0) {
  810. if (FirstQuery) {
  811. try_return( Status = STATUS_NO_SUCH_FILE );
  812. }
  813. try_return( Status = STATUS_NO_MORE_FILES );
  814. }
  815. break;
  816. }
  817. //
  818. // Cleanup and reinitialize context from previous loop.
  819. //
  820. NtfsReinitializeIndexContext( IrpContext, &OtherContext );
  821. //
  822. // We may have matched a Dos-Only name. If so we will save
  823. // it and go get the Ntfs name.
  824. //
  825. if (!FlagOn(NtfsFileName->Flags, FILE_NAME_NTFS) &&
  826. FlagOn(NtfsFileName->Flags, FILE_NAME_DOS)) {
  827. //
  828. // If we are returning everything, then we can skip
  829. // the Dos-Only names and save some cycles.
  830. //
  831. if (MatchAll) {
  832. continue;
  833. }
  834. DosFileName = NtfsFileName;
  835. NtfsFileName = NtfsRetrieveOtherFileName( IrpContext,
  836. Ccb,
  837. Scb,
  838. IndexEntry,
  839. &OtherContext,
  840. AcquiredFcb,
  841. &SynchronizationError );
  842. //
  843. // If we got an Ntfs name, then we need to list this entry now
  844. // iff the Ntfs name is not in the expression. If the Ntfs
  845. // name is in the expression, we can just continue and print
  846. // this name when we encounter it by the Ntfs name.
  847. //
  848. if (NtfsFileName != NULL) {
  849. if (FlagOn( Ccb->Flags, CCB_FLAG_WILDCARD_IN_EXPRESSION )) {
  850. if (NtfsFileNameIsInExpression( Vcb->UpcaseTable,
  851. (PFILE_NAME)Ccb->QueryBuffer,
  852. NtfsFileName,
  853. IgnoreCase )) {
  854. continue;
  855. }
  856. } else {
  857. if (NtfsFileNameIsEqual( Vcb->UpcaseTable,
  858. (PFILE_NAME)Ccb->QueryBuffer,
  859. NtfsFileName,
  860. IgnoreCase )) {
  861. continue;
  862. }
  863. }
  864. FoundFileNameLength = NtfsFileName->FileNameLength * sizeof( WCHAR );
  865. } else if (SynchronizationError) {
  866. if (Irp->IoStatus.Information != 0) {
  867. try_return( Status = STATUS_SUCCESS );
  868. } else {
  869. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  870. }
  871. } else {
  872. continue;
  873. }
  874. }
  875. //
  876. // Here are the rules concerning filling up the buffer:
  877. //
  878. // 1. The Io system garentees that there will always be
  879. // enough room for at least one base record.
  880. //
  881. // 2. If the full first record (including file name) cannot
  882. // fit, as much of the name as possible is copied and
  883. // STATUS_BUFFER_OVERFLOW is returned.
  884. //
  885. // 3. If a subsequent record cannot completely fit into the
  886. // buffer, none of it (as in 0 bytes) is copied, and
  887. // STATUS_SUCCESS is returned. A subsequent query will
  888. // pick up with this record.
  889. //
  890. BytesRemainingInBuffer = UserBufferLength - NextEntry;
  891. if ((NextEntry != 0) &&
  892. ((BaseLength + FoundFileNameLength > BytesRemainingInBuffer) ||
  893. (UserBufferLength < NextEntry))) {
  894. DebugTrace( 0, Dbg, ("Next entry won't fit\n") );
  895. try_return( Status = STATUS_SUCCESS );
  896. }
  897. ASSERT( BytesRemainingInBuffer >= BaseLength );
  898. //
  899. // Zero the base part of the structure.
  900. //
  901. AccessingUserBuffer = TRUE;
  902. RtlZeroMemory( &Buffer[NextEntry], BaseLength );
  903. AccessingUserBuffer = FALSE;
  904. //
  905. // Now we have an entry to return to our caller. we'll
  906. // case on the type of information requested and fill up the
  907. // user buffer if everything fits
  908. //
  909. switch (FileInformationClass) {
  910. case FileIdFullDirectoryInformation:
  911. AccessingUserBuffer = TRUE;
  912. ((PFILE_ID_FULL_DIR_INFORMATION)&Buffer[NextEntry])->FileId.QuadPart = *((PLONGLONG) &FileId);
  913. AccessingUserBuffer = FALSE;
  914. goto FillFullDirectoryInformation;
  915. case FileIdBothDirectoryInformation:
  916. AccessingUserBuffer = TRUE;
  917. ((PFILE_ID_BOTH_DIR_INFORMATION)&Buffer[NextEntry])->FileId.QuadPart = *((PLONGLONG) &FileId);
  918. AccessingUserBuffer = FALSE;
  919. // Fall thru
  920. case FileBothDirectoryInformation:
  921. BothDirInfo = (PFILE_BOTH_DIR_INFORMATION)&Buffer[NextEntry];
  922. //
  923. // If this is not also a Dos name, and the Ntfs flag is set
  924. // (meaning there is a separate Dos name), then call the
  925. // routine to get the short name, if we do not already have
  926. // it from above.
  927. //
  928. if (!FlagOn( NtfsFileName->Flags, FILE_NAME_DOS ) &&
  929. FlagOn( NtfsFileName->Flags, FILE_NAME_NTFS )) {
  930. if (DosFileName == NULL) {
  931. DosFileName = NtfsRetrieveOtherFileName( IrpContext,
  932. Ccb,
  933. Scb,
  934. IndexEntry,
  935. &OtherContext,
  936. AcquiredFcb,
  937. &SynchronizationError );
  938. }
  939. if (DosFileName != NULL) {
  940. //
  941. // Verify this is a legal length short name - Note we only do partial
  942. // verification checks on index buffers which is why we have to
  943. // check here.
  944. //
  945. if (DosFileName->FileNameLength * sizeof( WCHAR ) > sizeof( BothDirInfo->ShortName )) {
  946. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, NULL );
  947. }
  948. AccessingUserBuffer = TRUE;
  949. BothDirInfo->ShortNameLength = DosFileName->FileNameLength * sizeof( WCHAR );
  950. RtlCopyMemory( BothDirInfo->ShortName,
  951. DosFileName->FileName,
  952. BothDirInfo->ShortNameLength );
  953. } else if (SynchronizationError) {
  954. if (Irp->IoStatus.Information != 0) {
  955. try_return( Status = STATUS_SUCCESS );
  956. } else {
  957. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  958. }
  959. }
  960. }
  961. // Fallthru
  962. case FileFullDirectoryInformation:
  963. FillFullDirectoryInformation:
  964. DebugTrace( 0, Dbg, ("Getting file full Unicode directory information\n") );
  965. FullDirInfo = (PFILE_FULL_DIR_INFORMATION)&Buffer[NextEntry];
  966. //
  967. // EAs and reparse points cannot both be in a file at the same
  968. // time. We return different information for each case.
  969. //
  970. AccessingUserBuffer = TRUE;
  971. if (FlagOn( DupInfo->FileAttributes, FILE_ATTRIBUTE_REPARSE_POINT)) {
  972. FullDirInfo->EaSize = DupInfo->ReparsePointTag;
  973. } else {
  974. FullDirInfo->EaSize = DupInfo->PackedEaSize;
  975. //
  976. // Add 4 bytes for the CbListHeader.
  977. //
  978. if (DupInfo->PackedEaSize != 0) {
  979. FullDirInfo->EaSize += 4;
  980. }
  981. }
  982. // Fallthru
  983. case FileDirectoryInformation:
  984. DebugTrace( 0, Dbg, ("Getting file Unicode directory information\n") );
  985. DirInfo = (PFILE_DIRECTORY_INFORMATION)&Buffer[NextEntry];
  986. AccessingUserBuffer = TRUE;
  987. DirInfo->CreationTime.QuadPart = DupInfo->CreationTime;
  988. DirInfo->LastAccessTime.QuadPart = DupInfo->LastAccessTime;
  989. DirInfo->LastWriteTime.QuadPart = DupInfo->LastModificationTime;
  990. DirInfo->ChangeTime.QuadPart = DupInfo->LastChangeTime;
  991. DirInfo->FileAttributes = DupInfo->FileAttributes & FILE_ATTRIBUTE_VALID_FLAGS;
  992. if (IsDirectory( DupInfo ) || IsViewIndex( DupInfo )) {
  993. DirInfo->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
  994. }
  995. if (DirInfo->FileAttributes == 0) {
  996. DirInfo->FileAttributes = FILE_ATTRIBUTE_NORMAL;
  997. }
  998. DirInfo->FileNameLength = FoundFileNameLength;
  999. DirInfo->EndOfFile.QuadPart = DupInfo->FileSize;
  1000. DirInfo->AllocationSize.QuadPart = DupInfo->AllocatedLength;
  1001. break;
  1002. case FileNamesInformation:
  1003. DebugTrace( 0, Dbg, ("Getting file Unicode names information\n") );
  1004. AccessingUserBuffer = TRUE;
  1005. NamesInfo = (PFILE_NAMES_INFORMATION)&Buffer[NextEntry];
  1006. NamesInfo->FileNameLength = FoundFileNameLength;
  1007. break;
  1008. default:
  1009. try_return( Status = STATUS_INVALID_INFO_CLASS );
  1010. }
  1011. //
  1012. // Compute how many bytes we can copy. This should only be less
  1013. // than the file name length if we are only returning a single
  1014. // entry.
  1015. //
  1016. if (BytesRemainingInBuffer >= BaseLength + FoundFileNameLength) {
  1017. BytesToCopy = FoundFileNameLength;
  1018. } else {
  1019. BytesToCopy = BytesRemainingInBuffer - BaseLength;
  1020. Status = STATUS_BUFFER_OVERFLOW;
  1021. }
  1022. ASSERT( AccessingUserBuffer );
  1023. RtlCopyMemory( &Buffer[NextEntry + BaseLength],
  1024. NtfsFileName->FileName,
  1025. BytesToCopy );
  1026. //
  1027. // If/when we actually emit a record for the Fcb acquired,
  1028. // then we can release that file now. Note we do not just
  1029. // do it on the first time through the loop, because some of
  1030. // our callers back up a bit when they give us the resume point.
  1031. //
  1032. if ((AcquiredFcb != NULL) &&
  1033. (DupInfo != &Scb->Fcb->Info) &&
  1034. NtfsEqualMftRef(&IndexEntry->FileReference, &Ccb->FcbToAcquire.FileReference)) {
  1035. //
  1036. // Now look up the Fcb, and if it is there, reference it
  1037. // and remember it.
  1038. //
  1039. // It is pretty inconvenient here to see if the ReferenceCount
  1040. // goes to zero and try to do a TearDown, we do not have the
  1041. // right resources. Note that the window is small, and the Fcb
  1042. // will go away if either someone opens the file again, someone
  1043. // tries to delete the directory, or someone tries to lock the
  1044. // volume.
  1045. //
  1046. NtfsAcquireFcbTable( IrpContext, Vcb );
  1047. AcquiredFcb->ReferenceCount -= 1;
  1048. NtfsReleaseFcbTable( IrpContext, Vcb );
  1049. NtfsReleaseFcb( IrpContext, AcquiredFcb );
  1050. AcquiredFcb = NULL;
  1051. }
  1052. //
  1053. // Set up the previous next entry offset
  1054. //
  1055. *((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry;
  1056. AccessingUserBuffer = FALSE;
  1057. //
  1058. // And indicate how much of the user buffer we have currently
  1059. // used up. We must compute this value before we long align
  1060. // ourselves for the next entry. This is the point where we
  1061. // quad-align the length of the previous entry.
  1062. //
  1063. Irp->IoStatus.Information = QuadAlign( Irp->IoStatus.Information) +
  1064. BaseLength + BytesToCopy;
  1065. //
  1066. // If we weren't able to copy the whole name, then we bail here.
  1067. //
  1068. if (!NT_SUCCESS( Status )) {
  1069. try_return( Status );
  1070. }
  1071. //
  1072. // Set ourselves up for the next iteration
  1073. //
  1074. LastEntry = NextEntry;
  1075. NextEntry += (ULONG)QuadAlign( BaseLength + BytesToCopy );
  1076. }
  1077. //
  1078. // At this point we've successfully filled up some of the buffer so
  1079. // now is the time to set our status to success.
  1080. //
  1081. Status = STATUS_SUCCESS;
  1082. } except( (!FsRtlIsNtstatusExpected( GetExceptionCode() ) && AccessingUserBuffer) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
  1083. NtfsRaiseStatus( IrpContext, STATUS_INVALID_USER_BUFFER, NULL, NULL );
  1084. }
  1085. try_exit:
  1086. //
  1087. // Abort transaction on error by raising.
  1088. //
  1089. NtfsCleanupTransaction( IrpContext, Status, FALSE );
  1090. //
  1091. // Set the last access flag in the Fcb if the caller
  1092. // didn't set it explicitly.
  1093. //
  1094. if (!FlagOn( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS_TIME ) &&
  1095. !FlagOn( NtfsData.Flags, NTFS_FLAGS_DISABLE_LAST_ACCESS )) {
  1096. NtfsGetCurrentTime( IrpContext, Scb->Fcb->CurrentLastAccess );
  1097. SetFlag( Scb->Fcb->InfoFlags, FCB_INFO_UPDATE_LAST_ACCESS );
  1098. }
  1099. } finally {
  1100. DebugUnwind( NtfsQueryDirectory );
  1101. if (VcbAcquired) {
  1102. NtfsReleaseVcb( IrpContext, Vcb );
  1103. }
  1104. NtfsCleanupIndexContext( IrpContext, &OtherContext );
  1105. if (AcquiredFcb != NULL) {
  1106. //
  1107. // Now look up the Fcb, and if it is there, reference it
  1108. // and remember it.
  1109. //
  1110. // It is pretty inconvenient here to see if the ReferenceCount
  1111. // goes to zero and try to do a TearDown, we do not have the
  1112. // right resources. Note that the window is small, and the Fcb
  1113. // will go away if either someone opens the file again, someone
  1114. // tries to delete the directory, or someone tries to lock the
  1115. // volume.
  1116. //
  1117. NtfsAcquireFcbTable( IrpContext, Vcb );
  1118. AcquiredFcb->ReferenceCount -= 1;
  1119. NtfsReleaseFcbTable( IrpContext, Vcb );
  1120. NtfsReleaseFcb( IrpContext, AcquiredFcb );
  1121. }
  1122. if (ScbAcquired) {
  1123. NtfsReleaseScb( IrpContext, Scb );
  1124. }
  1125. NtfsCleanupAfterEnumeration( IrpContext, Ccb );
  1126. if (CcbAcquired) {
  1127. NtfsReleaseIndexCcb( Scb, Ccb );
  1128. }
  1129. if (!AbnormalTermination()) {
  1130. NtfsCompleteRequest( IrpContext, Irp, Status );
  1131. }
  1132. if (UnwindFileNameBuffer != NULL) {
  1133. NtfsFreePool(UnwindFileNameBuffer);
  1134. }
  1135. }
  1136. //
  1137. // And return to our caller
  1138. //
  1139. DebugTrace( -1, Dbg, ("NtfsQueryDirectory -> %08lx\n", Status) );
  1140. return Status;
  1141. }
  1142. //
  1143. // Local Support Routine
  1144. //
  1145. NTSTATUS
  1146. NtfsNotifyChangeDirectory (
  1147. IN PIRP_CONTEXT IrpContext,
  1148. IN PIRP Irp,
  1149. IN PVCB Vcb,
  1150. IN PSCB Scb,
  1151. IN PCCB Ccb
  1152. )
  1153. /*++
  1154. Routine Description:
  1155. This routine performs the notify change directory operation. It is
  1156. responsible for either completing or enqueuing the input Irp.
  1157. Arguments:
  1158. Irp - Supplies the Irp to process
  1159. Vcb - Supplies its Vcb
  1160. Scb - Supplies its Scb
  1161. Ccb - Supplies its Ccb
  1162. Return Value:
  1163. NTSTATUS - The return status for the operation
  1164. --*/
  1165. {
  1166. NTSTATUS Status;
  1167. PIO_STACK_LOCATION IrpSp;
  1168. ULONG CompletionFilter;
  1169. PSECURITY_SUBJECT_CONTEXT SubjectContext = NULL;
  1170. PCHECK_FOR_TRAVERSE_ACCESS CallBack = NULL;
  1171. BOOLEAN WatchTree;
  1172. BOOLEAN ViewIndex;
  1173. BOOLEAN FreeSubjectContext = FALSE;
  1174. BOOLEAN SetNotifyCounts = FALSE;
  1175. ASSERT_IRP_CONTEXT( IrpContext );
  1176. ASSERT_IRP( Irp );
  1177. ASSERT_VCB( Vcb );
  1178. ASSERT_CCB( Ccb );
  1179. ASSERT_SCB( Scb );
  1180. PAGED_CODE();
  1181. //
  1182. // Get the current Stack location
  1183. //
  1184. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  1185. DebugTrace( +1, Dbg, ("NtfsNotifyChangeDirectory...\n") );
  1186. DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
  1187. DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
  1188. DebugTrace( 0, Dbg, (" ->CompletionFilter = %08lx\n", IrpSp->Parameters.NotifyDirectory.CompletionFilter) );
  1189. DebugTrace( 0, Dbg, (" ->WatchTree = %08lx\n", FlagOn( IrpSp->Flags, SL_WATCH_TREE )) );
  1190. DebugTrace( 0, Dbg, ("Vcb = %08lx\n", Vcb) );
  1191. DebugTrace( 0, Dbg, ("Ccb = %08lx\n", Ccb) );
  1192. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  1193. //
  1194. // Reference our input parameter to make things easier
  1195. //
  1196. CompletionFilter = IrpSp->Parameters.NotifyDirectory.CompletionFilter;
  1197. WatchTree = BooleanFlagOn( IrpSp->Flags, SL_WATCH_TREE );
  1198. //
  1199. // Always set the wait bit in the IrpContext so the initial wait can't fail.
  1200. //
  1201. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  1202. //
  1203. // We will only acquire the Vcb to perform the dirnotify task. The dirnotify
  1204. // package will provide synchronization between this operation and cleanup.
  1205. // We need the Vcb to synchronize with any rename or link operations underway.
  1206. //
  1207. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  1208. try {
  1209. //
  1210. // If the Link count is zero on this Fcb then complete this request
  1211. // with STATUS_DELETE_PENDING.
  1212. //
  1213. if (Scb->Fcb->LinkCount == 0) {
  1214. NtfsRaiseStatus( IrpContext, STATUS_DELETE_PENDING, NULL, NULL );
  1215. }
  1216. ViewIndex = BooleanFlagOn( Scb->ScbState, SCB_STATE_VIEW_INDEX );
  1217. //
  1218. // If we need to verify traverse access for this caller then allocate and
  1219. // capture the subject context to pass to the dir notify package. That
  1220. // package will be responsible for deallocating it.
  1221. //
  1222. if (FlagOn( Ccb->Flags, CCB_FLAG_TRAVERSE_CHECK )) {
  1223. //
  1224. // We only use the subject context for directories
  1225. //
  1226. if (!ViewIndex) {
  1227. SubjectContext = NtfsAllocatePool( PagedPool,
  1228. sizeof( SECURITY_SUBJECT_CONTEXT ));
  1229. FreeSubjectContext = TRUE;
  1230. SeCaptureSubjectContext( SubjectContext );
  1231. FreeSubjectContext = FALSE;
  1232. }
  1233. CallBack = NtfsNotifyTraverseCheck;
  1234. }
  1235. //
  1236. // Update the notify counts and setup for cleanup processing before
  1237. // we hand off the irp
  1238. //
  1239. if (!FlagOn( Ccb->Flags, CCB_FLAG_DIR_NOTIFY )) {
  1240. SetFlag( Ccb->Flags, CCB_FLAG_DIR_NOTIFY );
  1241. if (ViewIndex) {
  1242. InterlockedIncrement( &Vcb->ViewIndexNotifyCount );
  1243. } else {
  1244. InterlockedIncrement( &Vcb->NotifyCount );
  1245. }
  1246. SetNotifyCounts = TRUE;
  1247. }
  1248. //
  1249. // Call the Fsrtl package to process the request. We cast the
  1250. // unicode strings to ansi strings as the dir notify package
  1251. // only deals with memory matching.
  1252. //
  1253. if (ViewIndex) {
  1254. //
  1255. // View indices use different values for the overloaded inputs
  1256. // to FsRtlNotifyFilterChangeDirectory.
  1257. //
  1258. FsRtlNotifyFilterChangeDirectory( Vcb->NotifySync,
  1259. &Vcb->ViewIndexNotifyList,
  1260. Ccb,
  1261. NULL,
  1262. WatchTree,
  1263. FALSE,
  1264. CompletionFilter,
  1265. Irp,
  1266. CallBack,
  1267. (PSECURITY_SUBJECT_CONTEXT) Scb->Fcb,
  1268. NULL );
  1269. } else {
  1270. FsRtlNotifyFilterChangeDirectory( Vcb->NotifySync,
  1271. &Vcb->DirNotifyList,
  1272. Ccb,
  1273. (PSTRING) &Scb->ScbType.Index.NormalizedName,
  1274. WatchTree,
  1275. FALSE,
  1276. CompletionFilter,
  1277. Irp,
  1278. CallBack,
  1279. SubjectContext,
  1280. NULL );
  1281. }
  1282. //
  1283. // We no longer own the irp at this point and can't safely touch the
  1284. // scb/ccb etc. anymore since everything might be gone now
  1285. //
  1286. Status = STATUS_PENDING;
  1287. } finally {
  1288. DebugUnwind( NtfsNotifyChangeDirectory );
  1289. NtfsReleaseVcb( IrpContext, Vcb );
  1290. //
  1291. // Since the dir notify package is holding the Irp, we discard the
  1292. // the IrpContext.
  1293. //
  1294. if (!AbnormalTermination()) {
  1295. NtfsCompleteRequest( IrpContext, NULL, 0 );
  1296. } else {
  1297. //
  1298. // Unroll any notify counts we added on exceptions
  1299. //
  1300. if (SetNotifyCounts) {
  1301. ClearFlag( Ccb->Flags, CCB_FLAG_DIR_NOTIFY );
  1302. if (ViewIndex) {
  1303. InterlockedDecrement( &Vcb->ViewIndexNotifyCount );
  1304. } else {
  1305. InterlockedDecrement( &Vcb->NotifyCount );
  1306. }
  1307. }
  1308. if (FreeSubjectContext) {
  1309. NtfsFreePool( SubjectContext );
  1310. }
  1311. }
  1312. }
  1313. //
  1314. // And return to our caller
  1315. //
  1316. DebugTrace( -1, Dbg, ("NtfsNotifyChangeDirectory -> %08lx\n", Status) );
  1317. return Status;
  1318. }