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.

1096 lines
29 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. dir.c
  5. Abstract:
  6. This module implements the file directory routines for the mailslot
  7. file system by the dispatch driver.
  8. Author:
  9. Manny Weiser (mannyw) 1-Feb-1991
  10. Revision History:
  11. --*/
  12. #include "mailslot.h"
  13. //
  14. // Local debug trace level
  15. //
  16. #define Dbg (DEBUG_TRACE_DIR)
  17. NTSTATUS
  18. MsCommonDirectoryControl (
  19. IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
  20. IN PIRP Irp
  21. );
  22. NTSTATUS
  23. MsQueryDirectory (
  24. IN PROOT_DCB RootDcb,
  25. IN PROOT_DCB_CCB Ccb,
  26. IN PIRP Irp
  27. );
  28. NTSTATUS
  29. MsNotifyChangeDirectory (
  30. IN PROOT_DCB RootDcb,
  31. IN PROOT_DCB_CCB Ccb,
  32. IN PIRP Irp
  33. );
  34. #ifdef ALLOC_PRAGMA
  35. #pragma alloc_text( PAGE, MsCommonDirectoryControl )
  36. #pragma alloc_text( PAGE, MsFsdDirectoryControl )
  37. #pragma alloc_text( PAGE, MsQueryDirectory )
  38. #endif
  39. NTSTATUS
  40. MsFsdDirectoryControl (
  41. IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
  42. IN PIRP Irp
  43. )
  44. /*++
  45. Routine Description:
  46. This routine is the FSD routine that handles directory control
  47. functions (i.e., query and notify).
  48. Arguments:
  49. MsfsDeviceObject - Supplies the device object for the directory function.
  50. Irp - Supplies the IRP to process.
  51. Return Value:
  52. NTSTATUS - The result status.
  53. --*/
  54. {
  55. NTSTATUS status;
  56. PAGED_CODE();
  57. DebugTrace(+1, Dbg, "MsFsdDirectoryControl\n", 0);
  58. //
  59. // Call the common direcotry control routine.
  60. //
  61. FsRtlEnterFileSystem();
  62. status = MsCommonDirectoryControl( MsfsDeviceObject, Irp );
  63. FsRtlExitFileSystem();
  64. //
  65. // Return to the caller.
  66. //
  67. DebugTrace(-1, Dbg, "MsFsdDirectoryControl -> %08lx\n", status );
  68. return status;
  69. }
  70. VOID
  71. MsFlushNotifyForFile (
  72. IN PDCB Dcb,
  73. IN PFILE_OBJECT FileObject
  74. )
  75. /*++
  76. Routine Description:
  77. This routine checks the notify queues of a DCB and completes any
  78. outstanding IRPS that match the given file object. This is used at cleanup time.
  79. Arguments:
  80. Dcb - Supplies the DCB to check for outstanding notify IRPs.
  81. FileObject - File object that IRP must be associated with.
  82. Return Value:
  83. None.
  84. --*/
  85. {
  86. PLIST_ENTRY Links;
  87. PIRP Irp;
  88. KIRQL OldIrql;
  89. PLIST_ENTRY Head;
  90. PIO_STACK_LOCATION IrpSp;
  91. LIST_ENTRY CompletionList;
  92. Head = &Dcb->Specific.Dcb.NotifyFullQueue;
  93. InitializeListHead (&CompletionList);
  94. KeAcquireSpinLock (&Dcb->Specific.Dcb.SpinLock, &OldIrql);
  95. Links = Head->Flink;
  96. while (1) {
  97. if (Links == Head) {
  98. //
  99. // We are at the end of this queue.
  100. //
  101. if (Head == &Dcb->Specific.Dcb.NotifyFullQueue) {
  102. Head = &Dcb->Specific.Dcb.NotifyPartialQueue;
  103. Links = Head->Flink;
  104. if (Links == Head) {
  105. break;
  106. }
  107. } else {
  108. break;
  109. }
  110. }
  111. Irp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry );
  112. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  113. //
  114. // If this IRP is for the matching file object then remove and save for completion
  115. //
  116. if (IrpSp->FileObject == FileObject) {
  117. Links = Links->Flink;
  118. RemoveEntryList (&Irp->Tail.Overlay.ListEntry);
  119. //
  120. // Remove cancel routine and detect if its already started running
  121. //
  122. if (IoSetCancelRoutine (Irp, NULL)) {
  123. //
  124. // Cancel isn't active and won't become active.
  125. //
  126. InsertTailList (&CompletionList, &Irp->Tail.Overlay.ListEntry);
  127. } else {
  128. //
  129. // Cancel is already active but is stalled before lock acquire. Initialize the
  130. // list head so the second remove is a noop. This is a rare case.
  131. //
  132. InitializeListHead (&Irp->Tail.Overlay.ListEntry);
  133. }
  134. } else {
  135. Links = Links->Flink;
  136. }
  137. }
  138. KeReleaseSpinLock (&Dcb->Specific.Dcb.SpinLock, OldIrql);
  139. while (!IsListEmpty (&CompletionList)) {
  140. Links = RemoveHeadList (&CompletionList);
  141. Irp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry );
  142. MsCompleteRequest( Irp, STATUS_CANCELLED );
  143. }
  144. return;
  145. }
  146. VOID
  147. MsCheckForNotify (
  148. IN PDCB Dcb,
  149. IN BOOLEAN CheckAllOutstandingIrps,
  150. IN NTSTATUS FinalStatus
  151. )
  152. /*++
  153. Routine Description:
  154. This routine checks the notify queues of a DCB and completes any
  155. outstanding IRPS.
  156. Note that the caller of this procedure must guarantee that the DCB
  157. is acquired for exclusive access.
  158. Arguments:
  159. Dcb - Supplies the DCB to check for outstanding notify IRPs.
  160. CheckAllOutstandingIrps - Indicates if only the NotifyFullQueue should be
  161. checked. If TRUE then all notify queues are checked, and if FALSE
  162. then only the NotifyFullQueue is checked.
  163. Return Value:
  164. None.
  165. --*/
  166. {
  167. PLIST_ENTRY links;
  168. PIRP irp;
  169. KIRQL OldIrql;
  170. PLIST_ENTRY Head;
  171. //
  172. // We'll always signal the notify full queue entries. They want
  173. // to be notified if every any change is made to a directory.
  174. //
  175. Head = &Dcb->Specific.Dcb.NotifyFullQueue;
  176. KeAcquireSpinLock (&Dcb->Specific.Dcb.SpinLock, &OldIrql);
  177. while (1) {
  178. links = RemoveHeadList (Head);
  179. if (links == Head) {
  180. //
  181. // This queue is empty. See if we need to skip to another.
  182. //
  183. if (Head == &Dcb->Specific.Dcb.NotifyFullQueue && CheckAllOutstandingIrps) {
  184. Head = &Dcb->Specific.Dcb.NotifyPartialQueue;
  185. links = RemoveHeadList (Head);
  186. if (links == Head) {
  187. break;
  188. }
  189. } else {
  190. break;
  191. }
  192. }
  193. //
  194. // Remove the Irp from the head of the queue, and complete it
  195. // with a success status.
  196. //
  197. irp = CONTAINING_RECORD( links, IRP, Tail.Overlay.ListEntry );
  198. //
  199. // Remove cancel routine and detect if its already started running
  200. //
  201. if (IoSetCancelRoutine (irp, NULL)) {
  202. //
  203. // Cancel isn't active and won't become active. Release the spinlock for the complete.
  204. //
  205. KeReleaseSpinLock (&Dcb->Specific.Dcb.SpinLock, OldIrql);
  206. MsCompleteRequest( irp, FinalStatus );
  207. KeAcquireSpinLock (&Dcb->Specific.Dcb.SpinLock, &OldIrql);
  208. } else {
  209. //
  210. // Cancel is already active but is stalled before lock acquire. Initialize the
  211. // list head so the second remove is a noop. This is a rare case.
  212. //
  213. InitializeListHead (&irp->Tail.Overlay.ListEntry);
  214. }
  215. }
  216. KeReleaseSpinLock (&Dcb->Specific.Dcb.SpinLock, OldIrql);
  217. return;
  218. }
  219. NTSTATUS
  220. MsCommonDirectoryControl (
  221. IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
  222. IN PIRP Irp
  223. )
  224. /*++
  225. Routine Description:
  226. This routine does the common code for directory control functions.
  227. Arguments:
  228. MsfsDeviceObject - Supplies the mailslot device object.
  229. Irp - Supplies the Irp being processed.
  230. Return Value:
  231. NTSTATUS - The return status for the operation
  232. --*/
  233. {
  234. NTSTATUS status;
  235. PIO_STACK_LOCATION irpSp;
  236. NODE_TYPE_CODE nodeTypeCode;
  237. PROOT_DCB_CCB ccb;
  238. PROOT_DCB rootDcb;
  239. PAGED_CODE();
  240. //
  241. // Get the current stack location
  242. //
  243. irpSp = IoGetCurrentIrpStackLocation( Irp );
  244. DebugTrace(+1, Dbg, "CommonDirectoryControl...\n", 0);
  245. DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
  246. //
  247. // Decode the file object to figure out who we are. If the result
  248. // is not the root DCB then its an illegal parameter.
  249. //
  250. if ((nodeTypeCode = MsDecodeFileObject( irpSp->FileObject,
  251. (PVOID *)&rootDcb,
  252. (PVOID *)&ccb )) == NTC_UNDEFINED) {
  253. DebugTrace(0, Dbg, "Not a directory\n", 0);
  254. MsCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
  255. status = STATUS_INVALID_PARAMETER;
  256. DebugTrace(-1, Dbg, "CommonDirectoryControl -> %08lx\n", status );
  257. return status;
  258. }
  259. if (nodeTypeCode != MSFS_NTC_ROOT_DCB) {
  260. DebugTrace(0, Dbg, "Not a directory\n", 0);
  261. MsDereferenceNode( &rootDcb->Header );
  262. MsCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
  263. status = STATUS_INVALID_PARAMETER;
  264. DebugTrace(-1, Dbg, "CommonDirectoryControl -> %08lx\n", status );
  265. return status;
  266. }
  267. //
  268. // Acquire exclusive access to the root DCB.
  269. //
  270. MsAcquireExclusiveFcb( (PFCB)rootDcb );
  271. //
  272. // Check if its been cleaned up yet.
  273. //
  274. status = MsVerifyDcbCcb (ccb);
  275. if (NT_SUCCESS (status)) {
  276. //
  277. // We know this is a directory control so we'll case on the
  278. // minor function, and call the appropriate work routines.
  279. //
  280. switch (irpSp->MinorFunction) {
  281. case IRP_MN_QUERY_DIRECTORY:
  282. status = MsQueryDirectory( rootDcb, ccb, Irp );
  283. break;
  284. case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
  285. status = MsNotifyChangeDirectory( rootDcb, ccb, Irp );
  286. break;
  287. default:
  288. //
  289. // For all other minor function codes we say they're invalid
  290. // and complete the request.
  291. //
  292. DebugTrace(0, DEBUG_TRACE_ERROR, "Invalid FS Control Minor Function Code %08lx\n", irpSp->MinorFunction);
  293. status = STATUS_INVALID_DEVICE_REQUEST;
  294. break;
  295. }
  296. }
  297. MsReleaseFcb( (PFCB)rootDcb );
  298. MsDereferenceRootDcb( rootDcb );
  299. if (status != STATUS_PENDING) {
  300. MsCompleteRequest( Irp, status );
  301. }
  302. return status;
  303. }
  304. NTSTATUS
  305. MsQueryDirectory (
  306. IN PROOT_DCB RootDcb,
  307. IN PROOT_DCB_CCB Ccb,
  308. IN PIRP Irp
  309. )
  310. /*++
  311. Routine Description:
  312. This is the work routine for querying a directory.
  313. Arugments:
  314. RootDcb - Supplies the dcb being queried
  315. Ccb - Supplies the context of the caller
  316. Irp - Supplies the Irp being processed
  317. Return Value:
  318. NTSTATUS - The return status for the operation.
  319. --*/
  320. {
  321. NTSTATUS status;
  322. PIO_STACK_LOCATION irpSp;
  323. PUCHAR buffer;
  324. CLONG systemBufferLength;
  325. UNICODE_STRING fileName;
  326. ULONG fileIndex;
  327. FILE_INFORMATION_CLASS fileInformationClass;
  328. BOOLEAN restartScan;
  329. BOOLEAN returnSingleEntry;
  330. BOOLEAN indexSpecified;
  331. #if 0
  332. UNICODE_STRING unicodeString;
  333. ULONG unicodeStringLength;
  334. #endif
  335. BOOLEAN ansiStringAllocated = FALSE;
  336. static WCHAR star = L'*';
  337. BOOLEAN caseInsensitive = TRUE; //*** Make searches case insensitive
  338. ULONG currentIndex;
  339. ULONG lastEntry;
  340. ULONG nextEntry;
  341. PLIST_ENTRY links;
  342. PFCB fcb;
  343. PFILE_DIRECTORY_INFORMATION dirInfo;
  344. PFILE_NAMES_INFORMATION namesInfo;
  345. PAGED_CODE();
  346. //
  347. // Get the current stack location.
  348. //
  349. irpSp = IoGetCurrentIrpStackLocation( Irp );
  350. DebugTrace(+1, Dbg, "MsQueryDirectory\n", 0 );
  351. DebugTrace( 0, Dbg, "RootDcb = %08lx\n", (ULONG)RootDcb);
  352. DebugTrace( 0, Dbg, "Ccb = %08lx\n", (ULONG)Ccb);
  353. DebugTrace( 0, Dbg, "SystemBuffer = %08lx\n", (ULONG)Irp->AssociatedIrp.SystemBuffer);
  354. DebugTrace( 0, Dbg, "Length = %08lx\n", irpSp->Parameters.QueryDirectory.Length);
  355. DebugTrace( 0, Dbg, "FileName = %08lx\n", (ULONG)irpSp->Parameters.QueryDirectory.FileName);
  356. DebugTrace( 0, Dbg, "FileIndex = %08lx\n", irpSp->Parameters.QueryDirectory.FileIndex);
  357. DebugTrace( 0, Dbg, "FileInformationClass = %08lx\n", irpSp->Parameters.QueryDirectory.FileInformationClass);
  358. DebugTrace( 0, Dbg, "RestartScan = %08lx\n", FlagOn(irpSp->Flags, SL_RESTART_SCAN));
  359. DebugTrace( 0, Dbg, "ReturnSingleEntry = %08lx\n", FlagOn(irpSp->Flags, SL_RETURN_SINGLE_ENTRY));
  360. DebugTrace( 0, Dbg, "IndexSpecified = %08lx\n", FlagOn(irpSp->Flags, SL_INDEX_SPECIFIED));
  361. //
  362. // Make local copies of the input parameters.
  363. //
  364. systemBufferLength = irpSp->Parameters.QueryDirectory.Length;
  365. fileIndex = irpSp->Parameters.QueryDirectory.FileIndex;
  366. fileInformationClass =
  367. irpSp->Parameters.QueryDirectory.FileInformationClass;
  368. restartScan = FlagOn(irpSp->Flags, SL_RESTART_SCAN);
  369. indexSpecified = FlagOn(irpSp->Flags, SL_INDEX_SPECIFIED);
  370. returnSingleEntry = FlagOn(irpSp->Flags, SL_RETURN_SINGLE_ENTRY);
  371. if (irpSp->Parameters.QueryDirectory.FileName != NULL) {
  372. fileName = *(PUNICODE_STRING)irpSp->Parameters.QueryDirectory.FileName;
  373. //
  374. // Ensure that the name is reasonable
  375. //
  376. if( (fileName.Buffer == NULL && fileName.Length) ||
  377. FlagOn( fileName.Length, 1 ) ) {
  378. status = STATUS_OBJECT_NAME_INVALID;
  379. return status;
  380. }
  381. } else {
  382. fileName.Length = 0;
  383. fileName.Buffer = NULL;
  384. }
  385. //
  386. // Check if the CCB already has a query template attached. If it
  387. // does not already have one then we either use the string we are
  388. // given or we attach our own containing "*"
  389. //
  390. if (Ccb->QueryTemplate == NULL) {
  391. //
  392. // This is our first time calling query directory so we need
  393. // to either set the query template to the user specified string
  394. // or to "*".
  395. //
  396. if (fileName.Buffer == NULL) {
  397. DebugTrace(0, Dbg, "Set template to *\n", 0);
  398. fileName.Length = sizeof( WCHAR );
  399. fileName.Buffer = ☆
  400. }
  401. DebugTrace(0, Dbg, "Set query template -> %wZ\n", (ULONG)&fileName);
  402. //
  403. // Allocate space for the query template.
  404. //
  405. Ccb->QueryTemplate = MsAllocatePagedPoolWithQuota ( sizeof(UNICODE_STRING) + fileName.Length,
  406. 'tFsM' );
  407. if (Ccb->QueryTemplate == NULL) {
  408. status = STATUS_INSUFFICIENT_RESOURCES;
  409. return status;
  410. }
  411. //
  412. // Initialize the query template and copy over the string.
  413. //
  414. Ccb->QueryTemplate->Length = fileName.Length;
  415. Ccb->QueryTemplate->Buffer = (PWCH)((PSZ)Ccb->QueryTemplate + sizeof(UNICODE_STRING));
  416. RtlCopyMemory (Ccb->QueryTemplate->Buffer,
  417. fileName.Buffer,
  418. fileName.Length);
  419. //
  420. // Set the search to start at the beginning of the directory.
  421. //
  422. fileIndex = 0;
  423. } else {
  424. //
  425. // Check if we were given an index to start with or if we need to
  426. // restart the scan or if we should use the index that was saved in
  427. // the CCB.
  428. //
  429. if (restartScan) {
  430. fileIndex = 0;
  431. } else if (!indexSpecified) {
  432. fileIndex = Ccb->IndexOfLastCcbReturned + 1;
  433. }
  434. }
  435. //
  436. // Now we are committed to completing the Irp, we do that in
  437. // the finally clause of the following try.
  438. //
  439. try {
  440. ULONG baseLength;
  441. ULONG lengthAdded;
  442. BOOLEAN Match;
  443. //
  444. // Map the user buffer.
  445. //
  446. MsMapUserBuffer( Irp, KernelMode, (PVOID *)&buffer );
  447. //
  448. // At this point we are about to enter our query loop. We have
  449. // already decided which Fcb index we need to return. The variables
  450. // LastEntry and NextEntry are used to index into the user buffer.
  451. // LastEntry is the last entry we added to the user buffer, and
  452. // NextEntry is the current one we're working on. CurrentIndex
  453. // is the Fcb index that we are looking at next. Logically the
  454. // way the loop works is as follows.
  455. //
  456. // Scan all of the Fcb in the directory
  457. //
  458. // if the Fcb matches the query template then
  459. //
  460. // if the CurrentIndex is >= the FileIndex then
  461. //
  462. // process this fcb, and decide if we should
  463. // continue the main loop
  464. //
  465. // end if
  466. //
  467. // Increment the current index
  468. //
  469. // end if
  470. //
  471. // end scan
  472. //
  473. currentIndex = 0;
  474. lastEntry = 0;
  475. nextEntry =0;
  476. switch (fileInformationClass) {
  477. case FileDirectoryInformation:
  478. baseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION,
  479. FileName[0] );
  480. break;
  481. case FileFullDirectoryInformation:
  482. baseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION,
  483. FileName[0] );
  484. break;
  485. case FileNamesInformation:
  486. baseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION,
  487. FileName[0] );
  488. break;
  489. case FileBothDirectoryInformation:
  490. baseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION,
  491. FileName[0] );
  492. break;
  493. default:
  494. try_return( status = STATUS_INVALID_INFO_CLASS );
  495. }
  496. for (links = RootDcb->Specific.Dcb.ParentDcbQueue.Flink;
  497. links != &RootDcb->Specific.Dcb.ParentDcbQueue;
  498. links = links->Flink) {
  499. fcb = CONTAINING_RECORD(links, FCB, ParentDcbLinks);
  500. ASSERT(fcb->Header.NodeTypeCode == MSFS_NTC_FCB);
  501. DebugTrace(0, Dbg, "Top of Loop\n", 0);
  502. DebugTrace(0, Dbg, "Fcb = %08lx\n", (ULONG)fcb);
  503. DebugTrace(0, Dbg, "CurrentIndex = %08lx\n", currentIndex);
  504. DebugTrace(0, Dbg, "FileIndex = %08lx\n", fileIndex);
  505. DebugTrace(0, Dbg, "LastEntry = %08lx\n", lastEntry);
  506. DebugTrace(0, Dbg, "NextEntry = %08lx\n", nextEntry);
  507. //
  508. // Check if the Fcb represents a mailslot that is part of
  509. // our query template.
  510. //
  511. try {
  512. Match = FsRtlIsNameInExpression( Ccb->QueryTemplate,
  513. &fcb->LastFileName,
  514. caseInsensitive,
  515. NULL );
  516. } except (EXCEPTION_EXECUTE_HANDLER) {
  517. try_return (status = GetExceptionCode ());
  518. }
  519. if (Match) {
  520. //
  521. // The FCB is in the query template so now check if
  522. // this is the index we should start returning.
  523. //
  524. if (currentIndex >= fileIndex) {
  525. //
  526. // Yes it is one to return so case on the requested
  527. // information class.
  528. //
  529. ULONG bytesToCopy;
  530. ULONG bytesRemainingInBuffer;
  531. //
  532. // Here are the rules concerning filling up the buffer:
  533. //
  534. // 1. The Io system garentees that there will always be
  535. // enough room for at least one base record.
  536. //
  537. // 2. If the full first record (including file name) cannot
  538. // fit, as much of the name as possible is copied and
  539. // STATUS_BUFFER_OVERFLOW is returned.
  540. //
  541. // 3. If a subsequent record cannot completely fit into the
  542. // buffer, none of it (as in 0 bytes) is copied, and
  543. // STATUS_SUCCESS is returned. A subsequent query will
  544. // pick up with this record.
  545. //
  546. bytesRemainingInBuffer = systemBufferLength - nextEntry;
  547. if ( (nextEntry != 0) &&
  548. ( (baseLength + fcb->LastFileName.Length >
  549. bytesRemainingInBuffer) ||
  550. (systemBufferLength < nextEntry) ) ) {
  551. DebugTrace(0, Dbg, "Next entry won't fit\n", 0);
  552. try_return( status = STATUS_SUCCESS );
  553. }
  554. ASSERT( bytesRemainingInBuffer >= baseLength );
  555. //
  556. // See how much of the name we will be able to copy into
  557. // the system buffer. This also dictates out return
  558. // value.
  559. //
  560. if ( baseLength + fcb->LastFileName.Length <=
  561. bytesRemainingInBuffer ) {
  562. bytesToCopy = fcb->LastFileName.Length;
  563. status = STATUS_SUCCESS;
  564. } else {
  565. bytesToCopy = bytesRemainingInBuffer - baseLength;
  566. status = STATUS_BUFFER_OVERFLOW;
  567. }
  568. //
  569. // Note how much of buffer we are consuming and zero
  570. // the base part of the structure.
  571. //
  572. lengthAdded = baseLength + bytesToCopy;
  573. try {
  574. RtlZeroMemory( &buffer[nextEntry], baseLength );
  575. switch (fileInformationClass) {
  576. case FileBothDirectoryInformation:
  577. //
  578. // We don't need short name
  579. //
  580. DebugTrace(0, Dbg, "Getting directory full information\n", 0);
  581. case FileFullDirectoryInformation:
  582. //
  583. // We don't use EaLength, so fill in nothing here.
  584. //
  585. DebugTrace(0, Dbg, "Getting directory full information\n", 0);
  586. case FileDirectoryInformation:
  587. DebugTrace(0, Dbg, "Getting directory information\n", 0);
  588. //
  589. // The eof indicates the number of instances and
  590. // allocation size is the maximum allowed
  591. //
  592. dirInfo = (PFILE_DIRECTORY_INFORMATION)&buffer[nextEntry];
  593. dirInfo->FileAttributes = FILE_ATTRIBUTE_NORMAL;
  594. dirInfo->CreationTime = fcb->Specific.Fcb.CreationTime;
  595. dirInfo->LastAccessTime = fcb->Specific.Fcb.LastAccessTime;
  596. dirInfo->LastWriteTime = fcb->Specific.Fcb.LastModificationTime;
  597. dirInfo->ChangeTime = fcb->Specific.Fcb.LastChangeTime;
  598. dirInfo->FileNameLength = fcb->LastFileName.Length;
  599. break;
  600. case FileNamesInformation:
  601. DebugTrace(0, Dbg, "Getting names information\n", 0);
  602. namesInfo = (PFILE_NAMES_INFORMATION)&buffer[nextEntry];
  603. namesInfo->FileNameLength = fcb->LastFileName.Length;
  604. break;
  605. default:
  606. KeBugCheck( MAILSLOT_FILE_SYSTEM );
  607. }
  608. RtlCopyMemory (&buffer[nextEntry + baseLength],
  609. fcb->LastFileName.Buffer,
  610. bytesToCopy);
  611. //
  612. // Update the CCB to the index we've just used.
  613. //
  614. Ccb->IndexOfLastCcbReturned = currentIndex;
  615. //
  616. // And indicate how much of the system buffer we have
  617. // currently used up. We must compute this value before
  618. // we long align outselves for the next entry.
  619. //
  620. Irp->IoStatus.Information = nextEntry + lengthAdded;
  621. //
  622. // Setup the previous next entry offset.
  623. //
  624. *((PULONG)(&buffer[lastEntry])) = nextEntry - lastEntry;
  625. } except( EXCEPTION_EXECUTE_HANDLER ) {
  626. status = GetExceptionCode();
  627. try_return( status );
  628. }
  629. //
  630. // Check if the last entry didn't completely fit
  631. //
  632. if ( status == STATUS_BUFFER_OVERFLOW ) {
  633. try_return( NOTHING );
  634. }
  635. //
  636. // Check if we are only to return a single entry
  637. //
  638. if (returnSingleEntry) {
  639. try_return( status = STATUS_SUCCESS );
  640. }
  641. //
  642. // Set ourselves up for the next iteration
  643. //
  644. lastEntry = nextEntry;
  645. nextEntry += (ULONG)QuadAlign( lengthAdded );
  646. }
  647. //
  648. // Increment the current index by one
  649. //
  650. currentIndex += 1;
  651. }
  652. }
  653. //
  654. // At this point we've scanned the entire list of FCBs so if
  655. // the NextEntry is zero then we haven't found anything so we
  656. // will return no more files, otherwise we return success.
  657. //
  658. if (nextEntry == 0) {
  659. status = STATUS_NO_MORE_FILES;
  660. } else {
  661. status = STATUS_SUCCESS;
  662. }
  663. try_exit: NOTHING;
  664. } finally {
  665. DebugTrace(-1, Dbg, "MsQueryDirectory -> %08lx\n", status);
  666. }
  667. return status;
  668. }
  669. VOID
  670. MsNotifyChangeDirectoryCancel (
  671. IN PDEVICE_OBJECT DeviceObject,
  672. IN PIRP Irp
  673. )
  674. /*++
  675. Routine Description:
  676. This is the cancel routine for the directory notify request.
  677. Arugments:
  678. DeviceObject - Supplies the device object for the request being canceled.
  679. Irp - Supplies the Irp being canceled.
  680. Return Value:
  681. None
  682. --*/
  683. {
  684. KIRQL OldIrql;
  685. PKSPIN_LOCK pSpinLock;
  686. //
  687. // First drop the cancel spinlock. We don't use this for this path
  688. //
  689. IoReleaseCancelSpinLock (Irp->CancelIrql);
  690. //
  691. // Grab the spinlock address. Easier that tracing the pointers or assuming that there is
  692. // only one DCB
  693. //
  694. pSpinLock = Irp->Tail.Overlay.DriverContext[0];
  695. //
  696. // Acquire the spinlock protecting these queues.
  697. //
  698. KeAcquireSpinLock (pSpinLock, &OldIrql);
  699. //
  700. // Remove the entry from the list. We will always be in one of the lists or this entry has
  701. // been initializes as an empty list by one of the completion routines when it detected
  702. // this routine was active.
  703. //
  704. RemoveEntryList (&Irp->Tail.Overlay.ListEntry);
  705. KeReleaseSpinLock (pSpinLock, OldIrql);
  706. //
  707. // Complete the IRP
  708. //
  709. MsCompleteRequest( Irp, STATUS_CANCELLED );
  710. return;
  711. }
  712. NTSTATUS
  713. MsNotifyChangeDirectory (
  714. IN PROOT_DCB RootDcb,
  715. IN PROOT_DCB_CCB Ccb,
  716. IN PIRP Irp
  717. )
  718. /*++
  719. Routine Description:
  720. This is the common routine for doing the notify change directory.
  721. Arugments:
  722. RootDcb - Supplies the DCB being queried.
  723. Ccb - Supplies the context of the caller.
  724. Irp - Supplies the Irp being processed.
  725. Return Value:
  726. NTSTATUS - STATUS_PENDING or STATUS_CANCELLED
  727. --*/
  728. {
  729. PIO_STACK_LOCATION irpSp;
  730. NTSTATUS Status;
  731. KIRQL OldIrql;
  732. PLIST_ENTRY Head;
  733. //
  734. // Get the current stack location.
  735. //
  736. irpSp = IoGetCurrentIrpStackLocation( Irp );
  737. DebugTrace(+1, Dbg, "MsNotifyChangeDirectory\n", 0 );
  738. DebugTrace( 0, Dbg, "RootDcb = %p", RootDcb);
  739. DebugTrace( 0, Dbg, "Ccb = %p", Ccb);
  740. //
  741. // Mark the Irp pending.
  742. //
  743. if (irpSp->Parameters.NotifyDirectory.CompletionFilter & (~FILE_NOTIFY_CHANGE_NAME)) {
  744. Head = &RootDcb->Specific.Dcb.NotifyFullQueue;
  745. } else {
  746. Head = &RootDcb->Specific.Dcb.NotifyPartialQueue;
  747. }
  748. //
  749. // Make it easy for the cancel routine to find this spinlock
  750. //
  751. Irp->Tail.Overlay.DriverContext[0] = &RootDcb->Specific.Dcb.SpinLock;
  752. //
  753. // Acquire the spinlock protecting these queues.
  754. //
  755. KeAcquireSpinLock (&RootDcb->Specific.Dcb.SpinLock, &OldIrql);
  756. IoSetCancelRoutine (Irp, MsNotifyChangeDirectoryCancel);
  757. //
  758. // See if the IRP was already canceled before we enabled cancelation
  759. //
  760. if (Irp->Cancel &&
  761. IoSetCancelRoutine (Irp, NULL) != NULL) {
  762. KeReleaseSpinLock (&RootDcb->Specific.Dcb.SpinLock, OldIrql);
  763. Status = STATUS_CANCELLED;
  764. } else {
  765. IoMarkIrpPending( Irp );
  766. InsertTailList( Head,
  767. &Irp->Tail.Overlay.ListEntry );
  768. KeReleaseSpinLock (&RootDcb->Specific.Dcb.SpinLock, OldIrql);
  769. Status = STATUS_PENDING;
  770. }
  771. //
  772. // Return to our caller.
  773. //
  774. DebugTrace(-1, Dbg, "NotifyChangeDirectory status %X\n", Status);
  775. return Status;
  776. }