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.

1525 lines
46 KiB

  1. /*++
  2. Copyright (c) 1989-2000 Microsoft Corporation
  3. Module Name:
  4. DirCtrl.c
  5. Abstract:
  6. This module implements the File Directory Control routines for Fat called
  7. by the dispatch driver.
  8. // @@BEGIN_DDKSPLIT
  9. Author:
  10. Gary Kimura [GaryKi] 28-Dec-1989
  11. Revision History:
  12. // @@END_DDKSPLIT
  13. --*/
  14. #include "FatProcs.h"
  15. //
  16. // The Bug check file id for this module
  17. //
  18. #define BugCheckFileId (FAT_BUG_CHECK_DIRCTRL)
  19. //
  20. // The local debug trace level
  21. //
  22. #define Dbg (DEBUG_TRACE_DIRCTRL)
  23. WCHAR Fat8QMdot3QM[12] = { DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM,
  24. L'.', DOS_QM, DOS_QM, DOS_QM};
  25. //
  26. // Local procedure prototypes
  27. //
  28. NTSTATUS
  29. FatQueryDirectory (
  30. IN PIRP_CONTEXT IrpContext,
  31. IN PIRP Irp
  32. );
  33. VOID
  34. FatGetDirTimes(
  35. PIRP_CONTEXT IrpContext,
  36. PDIRENT Dirent,
  37. PFILE_DIRECTORY_INFORMATION DirInfo
  38. );
  39. NTSTATUS
  40. FatNotifyChangeDirectory (
  41. IN PIRP_CONTEXT IrpContext,
  42. IN PIRP Irp
  43. );
  44. #ifdef ALLOC_PRAGMA
  45. #pragma alloc_text(PAGE, FatCommonDirectoryControl)
  46. #pragma alloc_text(PAGE, FatFsdDirectoryControl)
  47. #pragma alloc_text(PAGE, FatNotifyChangeDirectory)
  48. #pragma alloc_text(PAGE, FatQueryDirectory)
  49. #pragma alloc_text(PAGE, FatGetDirTimes)
  50. #endif
  51. NTSTATUS
  52. FatFsdDirectoryControl (
  53. IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
  54. IN PIRP Irp
  55. )
  56. /*++
  57. Routine Description:
  58. This routine implements the FSD part of directory control
  59. Arguments:
  60. VolumeDeviceObject - Supplies the volume device object where the
  61. file exists
  62. Irp - Supplies the Irp being processed
  63. Return Value:
  64. NTSTATUS - The FSD status for the IRP
  65. --*/
  66. {
  67. NTSTATUS Status;
  68. PIRP_CONTEXT IrpContext = NULL;
  69. BOOLEAN TopLevel;
  70. DebugTrace(+1, Dbg, "FatFsdDirectoryControl\n", 0);
  71. //
  72. // Call the common directory Control routine, with blocking allowed if
  73. // synchronous
  74. //
  75. FsRtlEnterFileSystem();
  76. TopLevel = FatIsIrpTopLevel( Irp );
  77. try {
  78. IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
  79. Status = FatCommonDirectoryControl( IrpContext, Irp );
  80. } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
  81. //
  82. // We had some trouble trying to perform the requested
  83. // operation, so we'll abort the I/O request with
  84. // the error status that we get back from the
  85. // execption code
  86. //
  87. Status = FatProcessException( IrpContext, Irp, GetExceptionCode() );
  88. }
  89. if (TopLevel) { IoSetTopLevelIrp( NULL ); }
  90. FsRtlExitFileSystem();
  91. //
  92. // And return to our caller
  93. //
  94. DebugTrace(-1, Dbg, "FatFsdDirectoryControl -> %08lx\n", Status);
  95. UNREFERENCED_PARAMETER( VolumeDeviceObject );
  96. return Status;
  97. }
  98. NTSTATUS
  99. FatCommonDirectoryControl (
  100. IN PIRP_CONTEXT IrpContext,
  101. IN PIRP Irp
  102. )
  103. /*++
  104. Routine Description:
  105. This is the common routine for doing directory control operations called
  106. by both the fsd and fsp threads
  107. Arguments:
  108. Irp - Supplies the Irp to process
  109. Return Value:
  110. NTSTATUS - The return status for the operation
  111. --*/
  112. {
  113. NTSTATUS Status;
  114. PIO_STACK_LOCATION IrpSp;
  115. //
  116. // Get a pointer to the current Irp stack location
  117. //
  118. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  119. DebugTrace(+1, Dbg, "FatCommonDirectoryControl\n", 0);
  120. DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp );
  121. DebugTrace( 0, Dbg, "MinorFunction = %08lx\n", IrpSp->MinorFunction );
  122. //
  123. // We know this is a directory control so we'll case on the
  124. // minor function, and call a internal worker routine to complete
  125. // the irp.
  126. //
  127. switch ( IrpSp->MinorFunction ) {
  128. case IRP_MN_QUERY_DIRECTORY:
  129. Status = FatQueryDirectory( IrpContext, Irp );
  130. break;
  131. case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
  132. Status = FatNotifyChangeDirectory( IrpContext, Irp );
  133. break;
  134. default:
  135. DebugTrace(0, Dbg, "Invalid Directory Control Minor Function %08lx\n", IrpSp->MinorFunction);
  136. FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
  137. Status = STATUS_INVALID_DEVICE_REQUEST;
  138. break;
  139. }
  140. DebugTrace(-1, Dbg, "FatCommonDirectoryControl -> %08lx\n", Status);
  141. return Status;
  142. }
  143. //
  144. // Local Support Routine
  145. //
  146. NTSTATUS
  147. FatQueryDirectory (
  148. IN PIRP_CONTEXT IrpContext,
  149. IN PIRP Irp
  150. )
  151. /*++
  152. Routine Description:
  153. This routine performs the query directory operation. It is responsible
  154. for either completing of enqueuing the input Irp.
  155. Arguments:
  156. Irp - Supplies the Irp to process
  157. Return Value:
  158. NTSTATUS - The return status for the operation
  159. --*/
  160. {
  161. NTSTATUS Status;
  162. PIO_STACK_LOCATION IrpSp;
  163. PVCB Vcb;
  164. PDCB Dcb;
  165. PCCB Ccb;
  166. PBCB Bcb;
  167. ULONG i;
  168. PUCHAR Buffer;
  169. CLONG UserBufferLength;
  170. PUNICODE_STRING UniArgFileName;
  171. WCHAR LongFileNameBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE];
  172. UNICODE_STRING LongFileName;
  173. FILE_INFORMATION_CLASS FileInformationClass;
  174. ULONG FileIndex;
  175. BOOLEAN RestartScan;
  176. BOOLEAN ReturnSingleEntry;
  177. BOOLEAN IndexSpecified;
  178. BOOLEAN InitialQuery;
  179. VBO CurrentVbo;
  180. BOOLEAN UpdateCcb;
  181. PDIRENT Dirent;
  182. UCHAR Fat8Dot3Buffer[12];
  183. OEM_STRING Fat8Dot3String;
  184. ULONG DiskAllocSize;
  185. ULONG NextEntry;
  186. ULONG LastEntry;
  187. PFILE_DIRECTORY_INFORMATION DirInfo;
  188. PFILE_FULL_DIR_INFORMATION FullDirInfo;
  189. PFILE_BOTH_DIR_INFORMATION BothDirInfo;
  190. PFILE_ID_FULL_DIR_INFORMATION IdFullDirInfo;
  191. PFILE_ID_BOTH_DIR_INFORMATION IdBothDirInfo;
  192. PFILE_NAMES_INFORMATION NamesInfo;
  193. //
  194. // Get the current Stack location
  195. //
  196. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  197. //
  198. // Display the input values.
  199. //
  200. DebugTrace(+1, Dbg, "FatQueryDirectory...\n", 0);
  201. DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT));
  202. DebugTrace( 0, Dbg, " Irp = %08lx\n", Irp);
  203. DebugTrace( 0, Dbg, " ->Length = %08lx\n", IrpSp->Parameters.QueryDirectory.Length);
  204. DebugTrace( 0, Dbg, " ->FileName = %08lx\n", IrpSp->Parameters.QueryDirectory.FileName);
  205. DebugTrace( 0, Dbg, " ->FileInformationClass = %08lx\n", IrpSp->Parameters.QueryDirectory.FileInformationClass);
  206. DebugTrace( 0, Dbg, " ->FileIndex = %08lx\n", IrpSp->Parameters.QueryDirectory.FileIndex);
  207. DebugTrace( 0, Dbg, " ->UserBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer);
  208. DebugTrace( 0, Dbg, " ->RestartScan = %08lx\n", FlagOn( IrpSp->Flags, SL_RESTART_SCAN ));
  209. DebugTrace( 0, Dbg, " ->ReturnSingleEntry = %08lx\n", FlagOn( IrpSp->Flags, SL_RETURN_SINGLE_ENTRY ));
  210. DebugTrace( 0, Dbg, " ->IndexSpecified = %08lx\n", FlagOn( IrpSp->Flags, SL_INDEX_SPECIFIED ));
  211. //
  212. // Reference our input parameters to make things easier
  213. //
  214. UserBufferLength = IrpSp->Parameters.QueryDirectory.Length;
  215. FileInformationClass = IrpSp->Parameters.QueryDirectory.FileInformationClass;
  216. FileIndex = IrpSp->Parameters.QueryDirectory.FileIndex;
  217. UniArgFileName = (PUNICODE_STRING) IrpSp->Parameters.QueryDirectory.FileName;
  218. RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN);
  219. ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY);
  220. IndexSpecified = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED);
  221. //
  222. // Check on the type of open. We return invalid parameter for all
  223. // but UserDirectoryOpens. Also check that the filename is a valid
  224. // UNICODE string.
  225. //
  226. if (FatDecodeFileObject( IrpSp->FileObject,
  227. &Vcb,
  228. &Dcb,
  229. &Ccb) != UserDirectoryOpen ||
  230. (UniArgFileName &&
  231. UniArgFileName->Length % sizeof(WCHAR))) {
  232. FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  233. DebugTrace(-1, Dbg, "FatQueryDirectory -> STATUS_INVALID_PARAMETER\n", 0);
  234. return STATUS_INVALID_PARAMETER;
  235. }
  236. //
  237. // Initialize the local variables.
  238. //
  239. Bcb = NULL;
  240. UpdateCcb = TRUE;
  241. Dirent = NULL;
  242. Fat8Dot3String.MaximumLength = 12;
  243. Fat8Dot3String.Buffer = Fat8Dot3Buffer;
  244. LongFileName.Length = 0;
  245. LongFileName.MaximumLength = sizeof( LongFileNameBuffer);
  246. LongFileName.Buffer = LongFileNameBuffer;
  247. InitialQuery = (BOOLEAN)((Ccb->UnicodeQueryTemplate.Buffer == NULL) &&
  248. !FlagOn(Ccb->Flags, CCB_FLAG_MATCH_ALL));
  249. Status = STATUS_SUCCESS;
  250. Irp->IoStatus.Information = 0;
  251. DiskAllocSize = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
  252. //
  253. // If this is the initial query, then grab exclusive access in
  254. // order to update the search string in the Ccb. We may
  255. // discover that we are not the initial query once we grab the Fcb
  256. // and downgrade our status.
  257. //
  258. if (InitialQuery) {
  259. if (!FatAcquireExclusiveFcb( IrpContext, Dcb )) {
  260. DebugTrace(0, Dbg, "FatQueryDirectory -> Enqueue to Fsp\n", 0);
  261. Status = FatFsdPostRequest( IrpContext, Irp );
  262. DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status);
  263. return Status;
  264. }
  265. if (Ccb->UnicodeQueryTemplate.Buffer != NULL) {
  266. InitialQuery = FALSE;
  267. FatConvertToSharedFcb( IrpContext, Dcb );
  268. }
  269. } else {
  270. if (!FatAcquireSharedFcb( IrpContext, Dcb )) {
  271. DebugTrace(0, Dbg, "FatQueryDirectory -> Enqueue to Fsp\n", 0);
  272. Status = FatFsdPostRequest( IrpContext, Irp );
  273. DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status);
  274. return Status;
  275. }
  276. }
  277. try {
  278. ULONG BaseLength;
  279. ULONG BytesConverted;
  280. //
  281. // If we are in the Fsp now because we had to wait earlier,
  282. // we must map the user buffer, otherwise we can use the
  283. // user's buffer directly.
  284. //
  285. Buffer = FatMapUserBuffer( IrpContext, Irp );
  286. //
  287. // Make sure the Dcb is still good.
  288. //
  289. FatVerifyFcb( IrpContext, Dcb );
  290. //
  291. // Determine where to start the scan. Highest priority is given
  292. // to the file index. Lower priority is the restart flag. If
  293. // neither of these is specified, then the Vbo offset field in the
  294. // Ccb is used.
  295. //
  296. if (IndexSpecified) {
  297. CurrentVbo = FileIndex + sizeof( DIRENT );
  298. } else if (RestartScan) {
  299. CurrentVbo = 0;
  300. } else {
  301. CurrentVbo = Ccb->OffsetToStartSearchFrom;
  302. }
  303. //
  304. // If this is the first try then allocate a buffer for the file
  305. // name.
  306. //
  307. if (InitialQuery) {
  308. //
  309. // If either:
  310. //
  311. // - No name was specified
  312. // - An empty name was specified
  313. // - We received a '*'
  314. // - The user specified the DOS equivolent of ????????.???
  315. //
  316. // then match all names.
  317. //
  318. if ((UniArgFileName == NULL) ||
  319. (UniArgFileName->Length == 0) ||
  320. (UniArgFileName->Buffer == NULL) ||
  321. ((UniArgFileName->Length == sizeof(WCHAR)) &&
  322. (UniArgFileName->Buffer[0] == L'*')) ||
  323. ((UniArgFileName->Length == 12*sizeof(WCHAR)) &&
  324. (RtlEqualMemory( UniArgFileName->Buffer,
  325. Fat8QMdot3QM,
  326. 12*sizeof(WCHAR) )))) {
  327. Ccb->ContainsWildCards = TRUE;
  328. SetFlag( Ccb->Flags, CCB_FLAG_MATCH_ALL );
  329. } else {
  330. BOOLEAN ExtendedName = FALSE;
  331. OEM_STRING LocalBestFit;
  332. //
  333. // First and formost, see if the name has wild cards.
  334. //
  335. Ccb->ContainsWildCards =
  336. FsRtlDoesNameContainWildCards( UniArgFileName );
  337. //
  338. // Now check to see if the name contains any extended
  339. // characters
  340. //
  341. for (i=0; i < UniArgFileName->Length / sizeof(WCHAR); i++) {
  342. if (UniArgFileName->Buffer[i] >= 0x80) {
  343. ExtendedName = TRUE;
  344. break;
  345. }
  346. }
  347. //
  348. // OK, now do the conversions we need.
  349. //
  350. if (ExtendedName) {
  351. Status = RtlUpcaseUnicodeString( &Ccb->UnicodeQueryTemplate,
  352. UniArgFileName,
  353. TRUE );
  354. if (!NT_SUCCESS(Status)) {
  355. try_return( Status );
  356. }
  357. SetFlag( Ccb->Flags, CCB_FLAG_FREE_UNICODE );
  358. //
  359. // Upcase the name and convert it to the Oem code page.
  360. //
  361. Status = RtlUpcaseUnicodeStringToCountedOemString( &LocalBestFit,
  362. UniArgFileName,
  363. TRUE );
  364. //
  365. // If this conversion failed for any reason other than
  366. // an unmappable character fail the request.
  367. //
  368. if (!NT_SUCCESS(Status)) {
  369. if (Status == STATUS_UNMAPPABLE_CHARACTER) {
  370. SetFlag( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE );
  371. } else {
  372. try_return( Status );
  373. }
  374. } else {
  375. SetFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT );
  376. }
  377. } else {
  378. PVOID Buffers;
  379. //
  380. // This case is optimized because I know I only have to
  381. // worry about a-z.
  382. //
  383. Buffers = FsRtlAllocatePoolWithTag( PagedPool,
  384. UniArgFileName->Length +
  385. UniArgFileName->Length / sizeof(WCHAR),
  386. TAG_FILENAME_BUFFER );
  387. Ccb->UnicodeQueryTemplate.Buffer = Buffers;
  388. Ccb->UnicodeQueryTemplate.Length = UniArgFileName->Length;
  389. Ccb->UnicodeQueryTemplate.MaximumLength = UniArgFileName->Length;
  390. LocalBestFit.Buffer = (PUCHAR)Buffers + UniArgFileName->Length;
  391. LocalBestFit.Length = UniArgFileName->Length / sizeof(WCHAR);
  392. LocalBestFit.MaximumLength = LocalBestFit.Length;
  393. SetFlag( Ccb->Flags, CCB_FLAG_FREE_UNICODE );
  394. for (i=0; i < UniArgFileName->Length / sizeof(WCHAR); i++) {
  395. WCHAR c = UniArgFileName->Buffer[i];
  396. LocalBestFit.Buffer[i] = (UCHAR)
  397. (Ccb->UnicodeQueryTemplate.Buffer[i] =
  398. (c < 'a' ? c : c <= 'z' ? c - ('a' - 'A') : c));
  399. }
  400. }
  401. //
  402. // At this point we now have the upcased unicode name,
  403. // and the two Oem names if they could be represented in
  404. // this code page.
  405. //
  406. // Now determine if the Oem names are legal for what we
  407. // going to try and do. Mark them as not usable is they
  408. // are not legal. Note that we can optimize extended names
  409. // since they are actually both the same string.
  410. //
  411. if (!FlagOn( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE ) &&
  412. !FatIsNameShortOemValid( IrpContext,
  413. LocalBestFit,
  414. Ccb->ContainsWildCards,
  415. FALSE,
  416. FALSE )) {
  417. if (ExtendedName) {
  418. RtlFreeOemString( &LocalBestFit );
  419. ClearFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT );
  420. }
  421. SetFlag( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE );
  422. }
  423. //
  424. // OK, now both locals oem strings correctly reflect their
  425. // usability. Now we want to load up the Ccb structure.
  426. //
  427. // Now we will branch on two paths of wheather the name
  428. // is wild or not.
  429. //
  430. if (!FlagOn( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE )) {
  431. if (Ccb->ContainsWildCards) {
  432. Ccb->OemQueryTemplate.Wild = LocalBestFit;
  433. } else {
  434. FatStringTo8dot3( IrpContext,
  435. LocalBestFit,
  436. &Ccb->OemQueryTemplate.Constant );
  437. if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT)) {
  438. RtlFreeOemString( &LocalBestFit );
  439. ClearFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT );
  440. }
  441. }
  442. }
  443. }
  444. //
  445. // We convert to shared access.
  446. //
  447. FatConvertToSharedFcb( IrpContext, Dcb );
  448. }
  449. LastEntry = 0;
  450. NextEntry = 0;
  451. switch (FileInformationClass) {
  452. case FileDirectoryInformation:
  453. BaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION,
  454. FileName[0] );
  455. break;
  456. case FileFullDirectoryInformation:
  457. BaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION,
  458. FileName[0] );
  459. break;
  460. case FileIdFullDirectoryInformation:
  461. BaseLength = FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION,
  462. FileName[0] );
  463. break;
  464. case FileNamesInformation:
  465. BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION,
  466. FileName[0] );
  467. break;
  468. case FileBothDirectoryInformation:
  469. BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION,
  470. FileName[0] );
  471. break;
  472. case FileIdBothDirectoryInformation:
  473. BaseLength = FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION,
  474. FileName[0] );
  475. break;
  476. default:
  477. try_return( Status = STATUS_INVALID_INFO_CLASS );
  478. }
  479. //
  480. // At this point we are about to enter our query loop. We have
  481. // determined the index into the directory file to begin the
  482. // search. LastEntry and NextEntry are used to index into the user
  483. // buffer. LastEntry is the last entry we've added, NextEntry is
  484. // current one we're working on. If NextEntry is non-zero, then
  485. // at least one entry was added.
  486. //
  487. while ( TRUE ) {
  488. VBO NextVbo;
  489. ULONG FileNameLength;
  490. ULONG BytesRemainingInBuffer;
  491. DebugTrace(0, Dbg, "FatQueryDirectory -> Top of loop\n", 0);
  492. //
  493. // If the user had requested only a single match and we have
  494. // returned that, then we stop at this point.
  495. //
  496. if (ReturnSingleEntry && NextEntry != 0) {
  497. try_return( Status );
  498. }
  499. //
  500. // We call FatLocateDirent to lock down the next matching dirent.
  501. //
  502. FatLocateDirent( IrpContext,
  503. Dcb,
  504. Ccb,
  505. CurrentVbo,
  506. &Dirent,
  507. &Bcb,
  508. &NextVbo,
  509. NULL,
  510. &LongFileName);
  511. //
  512. // If we didn't receive a dirent, then we are at the end of the
  513. // directory. If we have returned any files, we exit with
  514. // success, otherwise we return STATUS_NO_MORE_FILES.
  515. //
  516. if (!Dirent) {
  517. DebugTrace(0, Dbg, "FatQueryDirectory -> No dirent\n", 0);
  518. if (NextEntry == 0) {
  519. UpdateCcb = FALSE;
  520. if (InitialQuery) {
  521. Status = STATUS_NO_SUCH_FILE;
  522. } else {
  523. Status = STATUS_NO_MORE_FILES;
  524. }
  525. }
  526. try_return( Status );
  527. }
  528. //
  529. // Protect access to the user buffer with an exception handler.
  530. // Since (at our request) IO doesn't buffer these requests, we have
  531. // to guard against a user messing with the page protection and other
  532. // such trickery.
  533. //
  534. try {
  535. if (LongFileName.Length == 0) {
  536. //
  537. // Now we have an entry to return to our caller. We'll convert
  538. // the name from the form in the dirent to a <name>.<ext> form.
  539. // We'll case on the type of information requested and fill up
  540. // the user buffer if everything fits.
  541. //
  542. Fat8dot3ToString( IrpContext, Dirent, TRUE, &Fat8Dot3String );
  543. //
  544. // Determine the UNICODE length of the file name.
  545. //
  546. FileNameLength = RtlOemStringToCountedUnicodeSize(&Fat8Dot3String);
  547. //
  548. // Here are the rules concerning filling up the buffer:
  549. //
  550. // 1. The Io system garentees that there will always be
  551. // enough room for at least one base record.
  552. //
  553. // 2. If the full first record (including file name) cannot
  554. // fit, as much of the name as possible is copied and
  555. // STATUS_BUFFER_OVERFLOW is returned.
  556. //
  557. // 3. If a subsequent record cannot completely fit into the
  558. // buffer, none of it (as in 0 bytes) is copied, and
  559. // STATUS_SUCCESS is returned. A subsequent query will
  560. // pick up with this record.
  561. //
  562. BytesRemainingInBuffer = UserBufferLength - NextEntry;
  563. if ( (NextEntry != 0) &&
  564. ( (BaseLength + FileNameLength > BytesRemainingInBuffer) ||
  565. (UserBufferLength < NextEntry) ) ) {
  566. DebugTrace(0, Dbg, "Next entry won't fit\n", 0);
  567. try_return( Status = STATUS_SUCCESS );
  568. }
  569. ASSERT( BytesRemainingInBuffer >= BaseLength );
  570. //
  571. // Zero the base part of the structure.
  572. //
  573. RtlZeroMemory( &Buffer[NextEntry], BaseLength );
  574. switch ( FileInformationClass ) {
  575. //
  576. // Now fill the base parts of the strucure that are applicable.
  577. //
  578. case FileBothDirectoryInformation:
  579. case FileFullDirectoryInformation:
  580. case FileIdBothDirectoryInformation:
  581. case FileIdFullDirectoryInformation:
  582. DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file full directory information\n", 0);
  583. //
  584. // Get the Ea file length.
  585. //
  586. FullDirInfo = (PFILE_FULL_DIR_INFORMATION)&Buffer[NextEntry];
  587. //
  588. // If the EAs are corrupt, ignore the error. We don't want
  589. // to abort the directory query.
  590. //
  591. try {
  592. FatGetEaLength( IrpContext,
  593. Vcb,
  594. Dirent,
  595. &FullDirInfo->EaSize );
  596. } except(EXCEPTION_EXECUTE_HANDLER) {
  597. FatResetExceptionState( IrpContext );
  598. FullDirInfo->EaSize = 0;
  599. }
  600. case FileDirectoryInformation:
  601. DirInfo = (PFILE_DIRECTORY_INFORMATION)&Buffer[NextEntry];
  602. FatGetDirTimes( IrpContext, Dirent, DirInfo );
  603. DirInfo->EndOfFile.QuadPart = Dirent->FileSize;
  604. if (!FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) {
  605. DirInfo->AllocationSize.QuadPart =
  606. (((Dirent->FileSize + DiskAllocSize - 1) / DiskAllocSize) *
  607. DiskAllocSize );
  608. }
  609. DirInfo->FileAttributes = Dirent->Attributes != 0 ?
  610. Dirent->Attributes :
  611. FILE_ATTRIBUTE_NORMAL;
  612. DirInfo->FileIndex = NextVbo;
  613. DirInfo->FileNameLength = FileNameLength;
  614. DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String);
  615. break;
  616. case FileNamesInformation:
  617. DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file names information\n", 0);
  618. NamesInfo = (PFILE_NAMES_INFORMATION)&Buffer[NextEntry];
  619. NamesInfo->FileIndex = NextVbo;
  620. NamesInfo->FileNameLength = FileNameLength;
  621. DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String );
  622. break;
  623. default:
  624. FatBugCheck( FileInformationClass, 0, 0 );
  625. }
  626. BytesConverted = 0;
  627. Status = RtlOemToUnicodeN( (PWCH)&Buffer[NextEntry + BaseLength],
  628. BytesRemainingInBuffer - BaseLength,
  629. &BytesConverted,
  630. Fat8Dot3String.Buffer,
  631. Fat8Dot3String.Length );
  632. //
  633. // Check for the case that a single entry doesn't fit.
  634. // This should only get this far on the first entry
  635. //
  636. if (BytesConverted < FileNameLength) {
  637. ASSERT( NextEntry == 0 );
  638. Status = STATUS_BUFFER_OVERFLOW;
  639. }
  640. //
  641. // Set up the previous next entry offset
  642. //
  643. *((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry;
  644. //
  645. // And indicate how much of the user buffer we have currently
  646. // used up. We must compute this value before we long align
  647. // ourselves for the next entry
  648. //
  649. Irp->IoStatus.Information = QuadAlign( Irp->IoStatus.Information ) +
  650. BaseLength + BytesConverted;
  651. //
  652. // If something happened with the conversion, bail here.
  653. //
  654. if ( !NT_SUCCESS( Status ) ) {
  655. try_return( NOTHING );
  656. }
  657. } else {
  658. ULONG ShortNameLength;
  659. FileNameLength = LongFileName.Length;
  660. //
  661. // Here are the rules concerning filling up the buffer:
  662. //
  663. // 1. The Io system garentees that there will always be
  664. // enough room for at least one base record.
  665. //
  666. // 2. If the full first record (including file name) cannot
  667. // fit, as much of the name as possible is copied and
  668. // STATUS_BUFFER_OVERFLOW is returned.
  669. //
  670. // 3. If a subsequent record cannot completely fit into the
  671. // buffer, none of it (as in 0 bytes) is copied, and
  672. // STATUS_SUCCESS is returned. A subsequent query will
  673. // pick up with this record.
  674. //
  675. BytesRemainingInBuffer = UserBufferLength - NextEntry;
  676. if ( (NextEntry != 0) &&
  677. ( (BaseLength + FileNameLength > BytesRemainingInBuffer) ||
  678. (UserBufferLength < NextEntry) ) ) {
  679. DebugTrace(0, Dbg, "Next entry won't fit\n", 0);
  680. try_return( Status = STATUS_SUCCESS );
  681. }
  682. ASSERT( BytesRemainingInBuffer >= BaseLength );
  683. //
  684. // Zero the base part of the structure.
  685. //
  686. RtlZeroMemory( &Buffer[NextEntry], BaseLength );
  687. switch ( FileInformationClass ) {
  688. //
  689. // Now fill the base parts of the strucure that are applicable.
  690. //
  691. case FileBothDirectoryInformation:
  692. case FileIdBothDirectoryInformation:
  693. BothDirInfo = (PFILE_BOTH_DIR_INFORMATION)&Buffer[NextEntry];
  694. //
  695. // Now we have an entry to return to our caller. We'll convert
  696. // the name from the form in the dirent to a <name>.<ext> form.
  697. // We'll case on the type of information requested and fill up
  698. // the user buffer if everything fits.
  699. //
  700. Fat8dot3ToString( IrpContext, Dirent, FALSE, &Fat8Dot3String );
  701. ASSERT( Fat8Dot3String.Length <= 12 );
  702. Status = RtlOemToUnicodeN( &BothDirInfo->ShortName[0],
  703. 12*sizeof(WCHAR),
  704. &ShortNameLength,
  705. Fat8Dot3String.Buffer,
  706. Fat8Dot3String.Length );
  707. ASSERT( Status != STATUS_BUFFER_OVERFLOW );
  708. ASSERT( BothDirInfo->ShortNameLength <= 12*sizeof(WCHAR) );
  709. //
  710. // Copy the length into the dirinfo structure. Note
  711. // that the LHS below is a USHORT, so it can not
  712. // be specificed as the OUT parameter above.
  713. //
  714. BothDirInfo->ShortNameLength = (UCHAR)ShortNameLength;
  715. //
  716. // If something happened with the conversion, bail here.
  717. //
  718. if ( !NT_SUCCESS( Status ) ) {
  719. try_return( NOTHING );
  720. }
  721. case FileFullDirectoryInformation:
  722. case FileIdFullDirectoryInformation:
  723. DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file full directory information\n", 0);
  724. //
  725. // Get the Ea file length.
  726. //
  727. FullDirInfo = (PFILE_FULL_DIR_INFORMATION)&Buffer[NextEntry];
  728. //
  729. // If the EAs are corrupt, ignore the error. We don't want
  730. // to abort the directory query.
  731. //
  732. try {
  733. FatGetEaLength( IrpContext,
  734. Vcb,
  735. Dirent,
  736. &FullDirInfo->EaSize );
  737. } except(EXCEPTION_EXECUTE_HANDLER) {
  738. FatResetExceptionState( IrpContext );
  739. FullDirInfo->EaSize = 0;
  740. }
  741. case FileDirectoryInformation:
  742. DirInfo = (PFILE_DIRECTORY_INFORMATION)&Buffer[NextEntry];
  743. FatGetDirTimes( IrpContext, Dirent, DirInfo );
  744. DirInfo->EndOfFile.QuadPart = Dirent->FileSize;
  745. if (!FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) {
  746. DirInfo->AllocationSize.QuadPart = (
  747. (( Dirent->FileSize
  748. + DiskAllocSize - 1 )
  749. / DiskAllocSize )
  750. * DiskAllocSize );
  751. }
  752. DirInfo->FileAttributes = Dirent->Attributes != 0 ?
  753. Dirent->Attributes :
  754. FILE_ATTRIBUTE_NORMAL;
  755. DirInfo->FileIndex = NextVbo;
  756. DirInfo->FileNameLength = FileNameLength;
  757. DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String);
  758. break;
  759. case FileNamesInformation:
  760. DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file names information\n", 0);
  761. NamesInfo = (PFILE_NAMES_INFORMATION)&Buffer[NextEntry];
  762. NamesInfo->FileIndex = NextVbo;
  763. NamesInfo->FileNameLength = FileNameLength;
  764. DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String );
  765. break;
  766. default:
  767. FatBugCheck( FileInformationClass, 0, 0 );
  768. }
  769. BytesConverted = BytesRemainingInBuffer - BaseLength >= FileNameLength ?
  770. FileNameLength :
  771. BytesRemainingInBuffer - BaseLength;
  772. RtlCopyMemory( &Buffer[NextEntry + BaseLength],
  773. &LongFileName.Buffer[0],
  774. BytesConverted );
  775. //
  776. // Set up the previous next entry offset
  777. //
  778. *((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry;
  779. //
  780. // And indicate how much of the user buffer we have currently
  781. // used up. We must compute this value before we long align
  782. // ourselves for the next entry
  783. //
  784. Irp->IoStatus.Information += BaseLength + BytesConverted;
  785. //
  786. // Check for the case that a single entry doesn't fit.
  787. // This should only get this far on the first entry.
  788. //
  789. if (BytesConverted < FileNameLength) {
  790. ASSERT( NextEntry == 0 );
  791. try_return( Status = STATUS_BUFFER_OVERFLOW );
  792. }
  793. }
  794. //
  795. // Finish up by filling in the FileId
  796. //
  797. switch ( FileInformationClass ) {
  798. case FileIdBothDirectoryInformation:
  799. IdBothDirInfo = (PFILE_ID_BOTH_DIR_INFORMATION)&Buffer[NextEntry];
  800. IdBothDirInfo->FileId.QuadPart = FatGenerateFileIdFromDirentAndOffset( Dcb, Dirent, NextVbo );
  801. break;
  802. case FileIdFullDirectoryInformation:
  803. IdFullDirInfo = (PFILE_ID_FULL_DIR_INFORMATION)&Buffer[NextEntry];
  804. IdFullDirInfo->FileId.QuadPart = FatGenerateFileIdFromDirentAndOffset( Dcb, Dirent, NextVbo );
  805. break;
  806. default:
  807. break;
  808. }
  809. } except (EXCEPTION_EXECUTE_HANDLER) {
  810. //
  811. // We had a problem filling in the user's buffer, so stop and
  812. // fail this request. This is the only reason any exception
  813. // would have occured at this level.
  814. //
  815. Irp->IoStatus.Information = 0;
  816. UpdateCcb = FALSE;
  817. try_return( Status = GetExceptionCode());
  818. }
  819. //
  820. // Set ourselves up for the next iteration
  821. //
  822. LastEntry = NextEntry;
  823. NextEntry += (ULONG)QuadAlign(BaseLength + BytesConverted);
  824. CurrentVbo = NextVbo + sizeof( DIRENT );
  825. }
  826. try_exit: NOTHING;
  827. } finally {
  828. DebugUnwind( FatQueryDirectory );
  829. FatReleaseFcb( IrpContext, Dcb );
  830. //
  831. // Unpin data in cache if still held.
  832. //
  833. FatUnpinBcb( IrpContext, Bcb );
  834. //
  835. // Free any dynamically allocated string buffer
  836. //
  837. FatFreeStringBuffer( &LongFileName);
  838. //
  839. // Perform any cleanup. If this is the first query, then store
  840. // the filename in the Ccb if successful. Also update the
  841. // VBO index for the next search. This is done by transferring
  842. // from shared access to exclusive access and copying the
  843. // data from the local copies.
  844. //
  845. if (!AbnormalTermination()) {
  846. if (UpdateCcb) {
  847. //
  848. // Store the most recent VBO to use as a starting point for
  849. // the next search.
  850. //
  851. Ccb->OffsetToStartSearchFrom = CurrentVbo;
  852. }
  853. FatCompleteRequest( IrpContext, Irp, Status );
  854. }
  855. DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status);
  856. }
  857. return Status;
  858. }
  859. //
  860. // Local Support Routine
  861. //
  862. VOID
  863. FatGetDirTimes(
  864. PIRP_CONTEXT IrpContext,
  865. PDIRENT Dirent,
  866. PFILE_DIRECTORY_INFORMATION DirInfo
  867. )
  868. /*++
  869. Routine Description:
  870. This routine pulls the date/time information from a dirent and fills
  871. in the DirInfo structure.
  872. Arguments:
  873. Dirent - Supplies the dirent
  874. DirInfo - Supplies the target structure
  875. Return Value:
  876. VOID
  877. --*/
  878. {
  879. //
  880. // Start with the Last Write Time.
  881. //
  882. DirInfo->LastWriteTime =
  883. FatFatTimeToNtTime( IrpContext,
  884. Dirent->LastWriteTime,
  885. 0 );
  886. //
  887. // These fields are only non-zero when in Chicago mode.
  888. //
  889. if (FatData.ChicagoMode) {
  890. //
  891. // Do a quick check here for Creation and LastAccess
  892. // times that are the same as the LastWriteTime.
  893. //
  894. if (*((UNALIGNED LONG *)&Dirent->CreationTime) ==
  895. *((UNALIGNED LONG *)&Dirent->LastWriteTime)) {
  896. DirInfo->CreationTime.QuadPart =
  897. DirInfo->LastWriteTime.QuadPart +
  898. Dirent->CreationMSec * 10 * 1000 * 10;
  899. } else {
  900. //
  901. // Only do the really hard work if this field is non-zero.
  902. //
  903. if (((PUSHORT)Dirent)[8] != 0) {
  904. DirInfo->CreationTime =
  905. FatFatTimeToNtTime( IrpContext,
  906. Dirent->CreationTime,
  907. Dirent->CreationMSec );
  908. } else {
  909. ExLocalTimeToSystemTime( &FatJanOne1980,
  910. &DirInfo->CreationTime );
  911. }
  912. }
  913. //
  914. // Do a quick check for LastAccessDate.
  915. //
  916. if (*((PUSHORT)&Dirent->LastAccessDate) ==
  917. *((PUSHORT)&Dirent->LastWriteTime.Date)) {
  918. PFAT_TIME WriteTime;
  919. WriteTime = &Dirent->LastWriteTime.Time;
  920. DirInfo->LastAccessTime.QuadPart =
  921. DirInfo->LastWriteTime.QuadPart -
  922. UInt32x32To64(((WriteTime->DoubleSeconds * 2) +
  923. (WriteTime->Minute * 60) +
  924. (WriteTime->Hour * 60 * 60)),
  925. 1000 * 1000 * 10);
  926. } else {
  927. //
  928. // Only do the really hard work if this field is non-zero.
  929. //
  930. if (((PUSHORT)Dirent)[9] != 0) {
  931. DirInfo->LastAccessTime =
  932. FatFatDateToNtTime( IrpContext,
  933. Dirent->LastAccessDate );
  934. } else {
  935. ExLocalTimeToSystemTime( &FatJanOne1980,
  936. &DirInfo->LastAccessTime );
  937. }
  938. }
  939. }
  940. }
  941. //
  942. // Local Support Routine
  943. //
  944. NTSTATUS
  945. FatNotifyChangeDirectory (
  946. IN PIRP_CONTEXT IrpContext,
  947. IN PIRP Irp
  948. )
  949. /*++
  950. Routine Description:
  951. This routine performs the notify change directory operation. It is
  952. responsible for either completing of enqueuing the input Irp.
  953. Arguments:
  954. Irp - Supplies the Irp to process
  955. Return Value:
  956. NTSTATUS - The return status for the operation
  957. --*/
  958. {
  959. NTSTATUS Status;
  960. PIO_STACK_LOCATION IrpSp;
  961. PVCB Vcb;
  962. PDCB Dcb;
  963. PCCB Ccb;
  964. ULONG CompletionFilter;
  965. BOOLEAN WatchTree;
  966. BOOLEAN CompleteRequest;
  967. //
  968. // Get the current Stack location
  969. //
  970. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  971. DebugTrace(+1, Dbg, "FatNotifyChangeDirectory...\n", 0);
  972. DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT));
  973. DebugTrace( 0, Dbg, " Irp = %08lx\n", Irp);
  974. DebugTrace( 0, Dbg, " ->CompletionFilter = %08lx\n", IrpSp->Parameters.NotifyDirectory.CompletionFilter);
  975. //
  976. // Always set the wait flag in the Irp context for the original request.
  977. //
  978. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
  979. //
  980. // Assume we don't complete request.
  981. //
  982. CompleteRequest = FALSE;
  983. //
  984. // Check on the type of open. We return invalid parameter for all
  985. // but UserDirectoryOpens.
  986. //
  987. if (FatDecodeFileObject( IrpSp->FileObject,
  988. &Vcb,
  989. &Dcb,
  990. &Ccb ) != UserDirectoryOpen) {
  991. FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  992. DebugTrace(-1, Dbg, "FatQueryDirectory -> STATUS_INVALID_PARAMETER\n", 0);
  993. return STATUS_INVALID_PARAMETER;
  994. }
  995. //
  996. // Reference our input parameter to make things easier
  997. //
  998. CompletionFilter = IrpSp->Parameters.NotifyDirectory.CompletionFilter;
  999. WatchTree = BooleanFlagOn( IrpSp->Flags, SL_WATCH_TREE );
  1000. //
  1001. // Try to acquire exclusive access to the Dcb and enqueue the Irp to the
  1002. // Fsp if we didn't get access
  1003. //
  1004. if (!FatAcquireExclusiveFcb( IrpContext, Dcb )) {
  1005. DebugTrace(0, Dbg, "FatNotifyChangeDirectory -> Cannot Acquire Fcb\n", 0);
  1006. Status = FatFsdPostRequest( IrpContext, Irp );
  1007. DebugTrace(-1, Dbg, "FatNotifyChangeDirectory -> %08lx\n", Status);
  1008. return Status;
  1009. }
  1010. try {
  1011. //
  1012. // Make sure the Fcb is still good
  1013. //
  1014. FatVerifyFcb( IrpContext, Dcb );
  1015. //
  1016. // We need the full name.
  1017. //
  1018. FatSetFullFileNameInFcb( IrpContext, Dcb );
  1019. //
  1020. // If the file is marked as DELETE_PENDING then complete this
  1021. // request immediately.
  1022. //
  1023. if (FlagOn( Dcb->FcbState, FCB_STATE_DELETE_ON_CLOSE )) {
  1024. FatRaiseStatus( IrpContext, STATUS_DELETE_PENDING );
  1025. }
  1026. //
  1027. // Call the Fsrtl package to process the request.
  1028. //
  1029. FsRtlNotifyFullChangeDirectory( Vcb->NotifySync,
  1030. &Vcb->DirNotifyList,
  1031. Ccb,
  1032. (PSTRING)&Dcb->FullFileName,
  1033. WatchTree,
  1034. FALSE,
  1035. CompletionFilter,
  1036. Irp,
  1037. NULL,
  1038. NULL );
  1039. Status = STATUS_PENDING;
  1040. CompleteRequest = TRUE;
  1041. } finally {
  1042. DebugUnwind( FatNotifyChangeDirectory );
  1043. FatReleaseFcb( IrpContext, Dcb );
  1044. //
  1045. // If the dir notify package is holding the Irp, we discard the
  1046. // the IrpContext.
  1047. //
  1048. if (CompleteRequest) {
  1049. FatCompleteRequest( IrpContext, FatNull, 0 );
  1050. }
  1051. DebugTrace(-1, Dbg, "FatNotifyChangeDirectory -> %08lx\n", Status);
  1052. }
  1053. return Status;
  1054. }