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.

2108 lines
62 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 Udfs called
  7. by the Fsd/Fsp dispatch drivers.
  8. // @@BEGIN_DDKSPLIT
  9. Author:
  10. Dan Lovinger [DanLo] 27-Nov-1996
  11. Revision History:
  12. Tom Jolly [TomJolly] 1-March-2000 UDF 2.01 support
  13. // @@END_DDKSPLIT
  14. --*/
  15. #include "UdfProcs.h"
  16. //
  17. // The Bug check file id for this module
  18. //
  19. #define BugCheckFileId (UDFS_BUG_CHECK_DIRCTRL)
  20. //
  21. // The local debug trace level
  22. //
  23. #define Dbg (UDFS_DEBUG_LEVEL_DIRCTRL)
  24. //
  25. // Local structures
  26. //
  27. //
  28. // The following is used for the more complete enumeration required in the DirectoryControl path
  29. // and encapsulates the structures for enumerating both directories and ICBs, as well as converted
  30. // data from the ICB.
  31. //
  32. typedef struct _COMPOUND_DIR_ENUM_CONTEXT {
  33. //
  34. // Standard enumeration contexts. For this enumeration we walk the directory and lift the
  35. // associated ICB for each entry.
  36. //
  37. DIR_ENUM_CONTEXT DirContext;
  38. ICB_SEARCH_CONTEXT IcbContext;
  39. //
  40. // Timestamps converted from the ICB into NT-native form.
  41. //
  42. TIMESTAMP_BUNDLE Timestamps;
  43. //
  44. // File index corresponding to the current position in the enumeration.
  45. //
  46. LARGE_INTEGER FileIndex;
  47. } COMPOUND_DIR_ENUM_CONTEXT, *PCOMPOUND_DIR_ENUM_CONTEXT;
  48. //
  49. // Local macros
  50. //
  51. //
  52. // Constants defining the space of FileIndices for directory enumeration.
  53. //
  54. //
  55. // The virtual (synthesized) file indices
  56. //
  57. #define UDF_FILE_INDEX_VIRTUAL_SELF 0
  58. //
  59. // The file index where the physical directory entries begin
  60. //
  61. #define UDF_FILE_INDEX_PHYSICAL 1
  62. //
  63. // Provide initialization and cleanup for compound enumeration contexts.
  64. //
  65. INLINE
  66. VOID
  67. UdfInitializeCompoundDirContext (
  68. IN PIRP_CONTEXT IrpContext,
  69. IN PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext
  70. )
  71. {
  72. UdfInitializeDirContext( IrpContext, &CompoundDirContext->DirContext );
  73. UdfFastInitializeIcbContext( IrpContext, &CompoundDirContext->IcbContext );
  74. RtlZeroMemory( &CompoundDirContext->Timestamps, sizeof( TIMESTAMP_BUNDLE ));
  75. CompoundDirContext->FileIndex.QuadPart = 0;
  76. }
  77. INLINE
  78. VOID
  79. UdfCleanupCompoundDirContext (
  80. IN PIRP_CONTEXT IrpContext,
  81. IN PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext
  82. )
  83. {
  84. UdfCleanupDirContext( IrpContext, &CompoundDirContext->DirContext );
  85. UdfCleanupIcbContext( IrpContext, &CompoundDirContext->IcbContext );
  86. }
  87. //
  88. // UDF directories are unsorted (UDF 1.0.1 2.3.5.3) and do not contain self
  89. // entries. For directory enumeration we must provide a way for a restart to
  90. // occur at a random entry (SL_INDEX_SPECIFIED), but the key used is only
  91. // 32bits. Since the directory is unsorted, the filename is unsuitable for
  92. // quickly finding a restart point (even assuming that it was sorted,
  93. // discovering a directory entry is still not fast). Additionally, we must
  94. // synthesize the self-entry. So, here is how we map the space of file
  95. // indices to directory entries:
  96. //
  97. // File Index Directory Entry
  98. //
  99. // 0 self ('.')
  100. // 1 at byte offset 0 in the stream
  101. // N at byte offset N-1 in the stream
  102. //
  103. // The highest 32bit FileIndex returned will be stashed in the Ccb.
  104. //
  105. // For FileIndex > 2^32, we will return FileIndex 0 in the query structure.
  106. // On a restart, we will notice a FileIndex of zero and use the saved high
  107. // 32bit FileIndex as the starting point for a linear scan to find the named
  108. // directory entry in the restart request. In this way we only penalize the
  109. // improbable case of a directory stream > 2^32 bytes.
  110. //
  111. // The following inline routines assist with this mapping.
  112. //
  113. INLINE
  114. LONGLONG
  115. UdfFileIndexToPhysicalOffset(
  116. LONGLONG FileIndex
  117. )
  118. {
  119. return FileIndex - UDF_FILE_INDEX_PHYSICAL;
  120. }
  121. INLINE
  122. LONGLONG
  123. UdfPhysicalOffsetToFileIndex(
  124. LONGLONG PhysicalOffset
  125. )
  126. {
  127. return PhysicalOffset + UDF_FILE_INDEX_PHYSICAL;
  128. }
  129. INLINE
  130. BOOLEAN
  131. UdfIsFileIndexVirtual(
  132. LONGLONG FileIndex
  133. )
  134. {
  135. return FileIndex < UDF_FILE_INDEX_PHYSICAL;
  136. }
  137. //
  138. // Local support routines
  139. //
  140. NTSTATUS
  141. UdfQueryDirectory (
  142. IN PIRP_CONTEXT IrpContext,
  143. IN PIRP Irp,
  144. IN PIO_STACK_LOCATION IrpSp,
  145. IN PFCB Fcb,
  146. IN PCCB Ccb
  147. );
  148. NTSTATUS
  149. UdfNotifyChangeDirectory (
  150. IN PIRP_CONTEXT IrpContext,
  151. IN PIRP Irp,
  152. IN PIO_STACK_LOCATION IrpSp,
  153. IN PCCB Ccb
  154. );
  155. NTSTATUS
  156. UdfInitializeEnumeration (
  157. IN PIRP_CONTEXT IrpContext,
  158. IN PIO_STACK_LOCATION IrpSp,
  159. IN PFCB Fcb,
  160. IN OUT PCCB Ccb,
  161. IN OUT PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext,
  162. OUT PBOOLEAN ReturnNextEntry,
  163. OUT PBOOLEAN ReturnSingleEntry,
  164. OUT PBOOLEAN InitialQuery
  165. );
  166. BOOLEAN
  167. UdfEnumerateIndex (
  168. IN PIRP_CONTEXT IrpContext,
  169. IN PCCB Ccb,
  170. IN OUT PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext,
  171. IN BOOLEAN ReturnNextEntry
  172. );
  173. VOID
  174. UdfLookupFileEntryInEnumeration (
  175. IN PIRP_CONTEXT IrpContext,
  176. IN PFCB Fcb,
  177. IN PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext
  178. );
  179. BOOLEAN
  180. UdfLookupInitialFileIndex (
  181. IN PIRP_CONTEXT IrpContext,
  182. IN PFCB Fcb,
  183. IN PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext,
  184. IN PLONGLONG InitialIndex
  185. );
  186. BOOLEAN
  187. UdfLookupNextFileIndex (
  188. IN PIRP_CONTEXT IrpContext,
  189. IN PFCB Fcb,
  190. IN PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext
  191. );
  192. #ifdef ALLOC_PRAGMA
  193. #pragma alloc_text(PAGE, UdfCommonDirControl)
  194. #pragma alloc_text(PAGE, UdfEnumerateIndex)
  195. #pragma alloc_text(PAGE, UdfInitializeEnumeration)
  196. #pragma alloc_text(PAGE, UdfLookupFileEntryInEnumeration)
  197. #pragma alloc_text(PAGE, UdfLookupInitialFileIndex)
  198. #pragma alloc_text(PAGE, UdfLookupNextFileIndex)
  199. #pragma alloc_text(PAGE, UdfNotifyChangeDirectory)
  200. #pragma alloc_text(PAGE, UdfQueryDirectory)
  201. #endif
  202. NTSTATUS
  203. UdfCommonDirControl (
  204. IN PIRP_CONTEXT IrpContext,
  205. IN PIRP Irp
  206. )
  207. /*++
  208. Routine Description:
  209. This routine is the entry point for the directory control operations. These
  210. are directory enumerations and directory notify calls. We verify the
  211. user's handle is for a directory and then call the appropriate routine.
  212. Arguments:
  213. Irp - Irp for this request.
  214. Return Value:
  215. NTSTATUS - Status returned from the lower level routines.
  216. --*/
  217. {
  218. NTSTATUS Status;
  219. PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
  220. PFCB Fcb;
  221. PCCB Ccb;
  222. PAGED_CODE();
  223. //
  224. // Decode the user file object and fail this request if it is not
  225. // a user directory.
  226. //
  227. if (UdfDecodeFileObject( IrpSp->FileObject,
  228. &Fcb,
  229. &Ccb ) != UserDirectoryOpen) {
  230. UdfCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  231. return STATUS_INVALID_PARAMETER;
  232. }
  233. //
  234. // We know this is a directory control so we'll case on the
  235. // minor function, and call a internal worker routine to complete
  236. // the irp.
  237. //
  238. switch (IrpSp->MinorFunction) {
  239. case IRP_MN_QUERY_DIRECTORY:
  240. Status = UdfQueryDirectory( IrpContext, Irp, IrpSp, Fcb, Ccb );
  241. break;
  242. case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
  243. Status = UdfNotifyChangeDirectory( IrpContext, Irp, IrpSp, Ccb );
  244. break;
  245. default:
  246. UdfCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
  247. Status = STATUS_INVALID_DEVICE_REQUEST;
  248. break;
  249. }
  250. return Status;
  251. }
  252. //
  253. // Local support routines
  254. //
  255. NTSTATUS
  256. UdfQueryDirectory (
  257. IN PIRP_CONTEXT IrpContext,
  258. IN PIRP Irp,
  259. IN PIO_STACK_LOCATION IrpSp,
  260. IN PFCB Fcb,
  261. IN PCCB Ccb
  262. )
  263. /*++
  264. Routine Description:
  265. This routine performs the query directory operation. It is responsible
  266. for either completing of enqueuing the input Irp. We store the state of the
  267. search in the Ccb.
  268. Arguments:
  269. Irp - Supplies the Irp to process
  270. IrpSp - Stack location for this Irp.
  271. Fcb - Fcb for this directory.
  272. Ccb - Ccb for this directory open.
  273. Return Value:
  274. NTSTATUS - The return status for the operation
  275. --*/
  276. {
  277. NTSTATUS Status = STATUS_SUCCESS;
  278. ULONG Information = 0;
  279. ULONG LastEntry = 0;
  280. ULONG NextEntry = 0;
  281. ULONG FileNameBytes;
  282. ULONG BytesConverted;
  283. LARGE_INTEGER PreviousFileIndex;
  284. COMPOUND_DIR_ENUM_CONTEXT CompoundDirContext;
  285. PNSR_FID ThisFid;
  286. PICBFILE ThisFe;
  287. BOOLEAN InitialQuery;
  288. BOOLEAN ReturnNextEntry;
  289. BOOLEAN ReturnSingleEntry;
  290. BOOLEAN Found;
  291. BOOLEAN EasCorrupt;
  292. PCHAR UserBuffer;
  293. ULONG BytesRemainingInBuffer;
  294. ULONG BaseLength;
  295. PFILE_BOTH_DIR_INFORMATION DirInfo;
  296. PFILE_NAMES_INFORMATION NamesInfo;
  297. PFILE_ID_FULL_DIR_INFORMATION IdFullDirInfo;
  298. PFILE_ID_BOTH_DIR_INFORMATION IdBothDirInfo;
  299. FILE_INFORMATION_CLASS InfoClass = IrpSp->Parameters.QueryDirectory.FileInformationClass;
  300. PAGED_CODE();
  301. DebugTrace(( 0, Dbg, "UdfQueryDirectory\n" ));
  302. //
  303. // Check if we support this search mode. Also remember the size of the base part of
  304. // each of these structures.
  305. //
  306. switch ( InfoClass) {
  307. case FileDirectoryInformation:
  308. BaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION,
  309. FileName[0] );
  310. break;
  311. case FileFullDirectoryInformation:
  312. BaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION,
  313. FileName[0] );
  314. break;
  315. case FileIdFullDirectoryInformation:
  316. BaseLength = FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION,
  317. FileName[0] );
  318. break;
  319. case FileNamesInformation:
  320. BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION,
  321. FileName[0] );
  322. break;
  323. case FileBothDirectoryInformation:
  324. BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION,
  325. FileName[0] );
  326. break;
  327. case FileIdBothDirectoryInformation:
  328. BaseLength = FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION,
  329. FileName[0] );
  330. break;
  331. default:
  332. UdfCompleteRequest( IrpContext, Irp, STATUS_INVALID_INFO_CLASS );
  333. return STATUS_INVALID_INFO_CLASS;
  334. }
  335. //
  336. // Get the user buffer.
  337. //
  338. UdfMapUserBuffer( IrpContext, &UserBuffer);
  339. //
  340. // Initialize our search context.
  341. //
  342. UdfInitializeCompoundDirContext( IrpContext, &CompoundDirContext );
  343. //
  344. // Acquire the directory.
  345. //
  346. UdfAcquireFileShared( IrpContext, Fcb );
  347. //
  348. // Use a try-finally to facilitate cleanup.
  349. //
  350. try {
  351. //
  352. // Verify the Fcb is still good.
  353. //
  354. UdfVerifyFcbOperation( IrpContext, Fcb );
  355. //
  356. // Start by getting the initial state for the enumeration. This will set up the Ccb with
  357. // the initial search parameters and let us know the starting offset in the directory
  358. // to search.
  359. //
  360. Status = UdfInitializeEnumeration( IrpContext,
  361. IrpSp,
  362. Fcb,
  363. Ccb,
  364. &CompoundDirContext,
  365. &ReturnNextEntry,
  366. &ReturnSingleEntry,
  367. &InitialQuery );
  368. if (!NT_SUCCESS( Status )) {
  369. try_leave( Status );
  370. }
  371. //
  372. // At this point we are about to enter our query loop. We have
  373. // determined the index into the directory file to begin the
  374. // search. LastEntry and NextEntry are used to index into the user
  375. // buffer. LastEntry is the last entry we've added, NextEntry is
  376. // current one we're working on. If NextEntry is non-zero, then
  377. // at least one entry was added.
  378. //
  379. while (TRUE) {
  380. //
  381. // If the user had requested only a single match and we have
  382. // returned that, then we stop at this point. We update the Ccb with
  383. // the status based on the last entry returned.
  384. //
  385. if ((NextEntry != 0) && ReturnSingleEntry) {
  386. try_leave( Status );
  387. }
  388. //
  389. // We try to locate the next matching dirent. Our search if based on a starting
  390. // dirent offset, whether we should return the current or next entry, whether
  391. // we should be doing a short name search and finally whether we should be
  392. // checking for a version match.
  393. //
  394. PreviousFileIndex = CompoundDirContext.FileIndex;
  395. try {
  396. Found = UdfEnumerateIndex( IrpContext, Ccb, &CompoundDirContext, ReturnNextEntry );
  397. }
  398. except (((0 != NextEntry) &&
  399. ((GetExceptionCode() == STATUS_FILE_CORRUPT_ERROR) ||
  400. (GetExceptionCode() == STATUS_CRC_ERROR)))
  401. ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
  402. DebugTrace((0, Dbg, "UdfQueryDirectory - Corrupt. Returning buffer so far, setting back enumeration\n"));
  403. //
  404. // We encountered corruption in the directory. We will swallow this
  405. // error since we have already placed some previous entries in the user
  406. // buffer, and raise it when we're called again. This is
  407. // to return as much as possible in the case of corrupt directories,
  408. // particularly discs which are padded wrong at the end of the dir.
  409. //
  410. ReturnNextEntry = TRUE;
  411. //
  412. // Point to the previous Fid, so that we will advance again to the point
  413. // of corruption on next call (we only do bounds checking on advance, not
  414. // when restarting *at* a particular entry).
  415. //
  416. CompoundDirContext.FileIndex = PreviousFileIndex;
  417. try_leave( Status = STATUS_SUCCESS);
  418. }
  419. //
  420. // Initialize the value for the next search.
  421. //
  422. ReturnNextEntry = TRUE;
  423. //
  424. // If we didn't receive a dirent, then we are at the end of the
  425. // directory. If we have returned any files, we exit with
  426. // success, otherwise we return STATUS_NO_MORE_FILES.
  427. //
  428. if (!Found) {
  429. if (NextEntry == 0) {
  430. Status = STATUS_NO_MORE_FILES;
  431. if (InitialQuery) {
  432. Status = STATUS_NO_SUCH_FILE;
  433. }
  434. }
  435. try_leave( Status );
  436. }
  437. //
  438. // Remember the dirent/file entry for the file we just found.
  439. //
  440. ThisFid = CompoundDirContext.DirContext.Fid;
  441. //
  442. // Here are the rules concerning filling up the buffer:
  443. //
  444. // 1. The Io system garentees that there will always be
  445. // enough room for at least one base record.
  446. //
  447. // 2. If the full first record (including file name) cannot
  448. // fit, as much of the name as possible is copied and
  449. // STATUS_BUFFER_OVERFLOW is returned.
  450. //
  451. // 3. If a subsequent record cannot completely fit into the
  452. // buffer, none of it (as in 0 bytes) is copied, and
  453. // STATUS_SUCCESS is returned. A subsequent query will
  454. // pick up with this record.
  455. //
  456. //
  457. // We can look directly at the dirent that we found.
  458. //
  459. FileNameBytes = CompoundDirContext.DirContext.CaseObjectName.Length;
  460. //
  461. // If the slot for the next entry would be beyond the length of the
  462. // user's buffer just exit (we know we've returned at least one entry
  463. // already). This will happen when we align the pointer past the end.
  464. //
  465. if (NextEntry > IrpSp->Parameters.QueryDirectory.Length) {
  466. ReturnNextEntry = FALSE;
  467. try_leave( Status = STATUS_SUCCESS );
  468. }
  469. //
  470. // Compute the number of bytes remaining in the buffer. Round this
  471. // down to a WCHAR boundary so we can copy full characters.
  472. //
  473. BytesRemainingInBuffer = IrpSp->Parameters.QueryDirectory.Length - NextEntry;
  474. ClearFlag( BytesRemainingInBuffer, 1 );
  475. //
  476. // If this won't fit and we have returned a previous entry then just
  477. // return STATUS_SUCCESS.
  478. //
  479. if ((BaseLength + FileNameBytes) > BytesRemainingInBuffer) {
  480. //
  481. // If we already found an entry then just exit.
  482. //
  483. if (NextEntry != 0) {
  484. ReturnNextEntry = FALSE;
  485. try_leave( Status = STATUS_SUCCESS );
  486. }
  487. //
  488. // Reduce the FileNameBytes to just fit in the buffer.
  489. //
  490. FileNameBytes = BytesRemainingInBuffer - BaseLength;
  491. //
  492. // Use a status code of STATUS_BUFFER_OVERFLOW. Also set
  493. // ReturnSingleEntry so that we will exit the loop at the top.
  494. //
  495. Status = STATUS_BUFFER_OVERFLOW;
  496. ReturnSingleEntry = TRUE;
  497. }
  498. //
  499. // Zero and initialize the base part of the current entry.
  500. //
  501. SafeZeroMemory( IrpContext, Add2Ptr( UserBuffer, NextEntry, PVOID ), BaseLength );
  502. //
  503. // Most query types require that we lookup the FE referenced by the FID
  504. // to extract attribute information. Do this outside the user buffer exception
  505. // handler block to avoid the risk of masking real driver errors.
  506. //
  507. EasCorrupt = FALSE;
  508. ThisFe = NULL;
  509. if (InfoClass != FileNamesInformation) {
  510. //
  511. // We will swallow certain corruption errors here in the interest of
  512. // allowing users to access other objects in the directory. The
  513. // errors will be reported later if the user attempts to open the corrupt
  514. // object.
  515. //
  516. try {
  517. UdfLookupFileEntryInEnumeration( IrpContext,
  518. Fcb,
  519. &CompoundDirContext );
  520. //
  521. // Directly reference the file entry we just looked up.
  522. //
  523. ThisFe = (PICBFILE) CompoundDirContext.IcbContext.Active.View;
  524. //
  525. // Now go gather all of the timestamps for this guy.
  526. //
  527. UdfUpdateTimestampsFromIcbContext ( IrpContext,
  528. &CompoundDirContext.IcbContext,
  529. &CompoundDirContext.Timestamps );
  530. }
  531. except (UdfQueryDirExceptionFilter( GetExceptionInformation())) {
  532. //
  533. // The currently mapped ICB will have been left in IcbContext->Current,
  534. // and we could look at it and pull out timestamps / filesizes,
  535. // but it could be complete trash, so we'll just zero these fields
  536. // in this dir record.
  537. //
  538. DebugTrace(( 0, Dbg, "Ignoring corrupt FE (referenced by FID in dir FCB 0x%p) during dir enum\n", Fcb));
  539. //
  540. // We either failed verification of the core FE fields, or looking
  541. // up the EAs for timestamps. Either way, the EAs are definitely dead
  542. // which means the create time is invalid
  543. //
  544. EasCorrupt = TRUE;
  545. IrpContext->ExceptionStatus = STATUS_SUCCESS;
  546. }
  547. }
  548. //
  549. // Protect access to the user buffer with an exception handler.
  550. // Since (at our request) IO doesn't buffer these requests, we have
  551. // to guard against a user messing with the page protection and other
  552. // such trickery.
  553. //
  554. try {
  555. //
  556. // Now we have an entry to return to our caller. We'll case on the type of
  557. // information requested and fill up the user buffer if everything fits.
  558. //
  559. switch (InfoClass) {
  560. case FileBothDirectoryInformation:
  561. case FileFullDirectoryInformation:
  562. case FileIdBothDirectoryInformation:
  563. case FileIdFullDirectoryInformation:
  564. case FileDirectoryInformation:
  565. DirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_BOTH_DIR_INFORMATION );
  566. //
  567. // We should already have looked up the FE, unless it was corrupt. If we
  568. // actually have an FE here that means that the core content verified ok,
  569. // so pull out the relevant information.
  570. //
  571. if (NULL != ThisFe) {
  572. DirInfo->LastWriteTime =
  573. DirInfo->ChangeTime = CompoundDirContext.Timestamps.ModificationTime;
  574. DirInfo->LastAccessTime = CompoundDirContext.Timestamps.AccessTime;
  575. if (!EasCorrupt) {
  576. DirInfo->CreationTime = CompoundDirContext.Timestamps.CreationTime;
  577. }
  578. else {
  579. DirInfo->CreationTime = UdfCorruptFileTime;
  580. }
  581. //
  582. // Set the attributes and sizes separately for directories and
  583. // files.
  584. //
  585. if (ThisFe->Icbtag.FileType == ICBTAG_FILE_T_DIRECTORY) {
  586. DirInfo->EndOfFile.QuadPart = DirInfo->AllocationSize.QuadPart = 0;
  587. SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_DIRECTORY );
  588. } else {
  589. DirInfo->EndOfFile.QuadPart = ThisFe->InfoLength;
  590. DirInfo->AllocationSize.QuadPart = LlBlockAlign( Fcb->Vcb, ThisFe->InfoLength );
  591. }
  592. }
  593. else {
  594. //
  595. // FE is corrupt. Fill in arbirary but valid times.
  596. //
  597. DirInfo->CreationTime =
  598. DirInfo->ChangeTime =
  599. DirInfo->LastWriteTime =
  600. DirInfo->LastAccessTime = UdfCorruptFileTime;
  601. }
  602. //
  603. // All Cdrom files are readonly. We also copy the existence
  604. // bit to the hidden attribute, assuming that synthesized FIDs
  605. // are never hidden.
  606. //
  607. SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_READONLY );
  608. if (ThisFid && FlagOn( ThisFid->Flags, NSR_FID_F_HIDDEN )) {
  609. SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_HIDDEN );
  610. }
  611. //
  612. // The file index for real file indices > 2^32 is zero. When asked to
  613. // restart at an index of zero, we will know to use a stashed starting
  614. // point to beging to search, by name, for the correct restart point.
  615. //
  616. if (CompoundDirContext.FileIndex.HighPart == 0) {
  617. DirInfo->FileIndex = CompoundDirContext.FileIndex.LowPart;
  618. } else {
  619. DirInfo->FileIndex = 0;
  620. }
  621. DirInfo->FileNameLength = FileNameBytes;
  622. break;
  623. case FileNamesInformation:
  624. NamesInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_NAMES_INFORMATION );
  625. if (CompoundDirContext.FileIndex.HighPart == 0) {
  626. NamesInfo->FileIndex = CompoundDirContext.FileIndex.LowPart;
  627. } else {
  628. NamesInfo->FileIndex = 0;
  629. }
  630. NamesInfo->FileNameLength = FileNameBytes;
  631. break;
  632. }
  633. //
  634. // Fill in the FileId
  635. //
  636. switch (InfoClass) {
  637. case FileIdBothDirectoryInformation:
  638. IdBothDirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_ID_BOTH_DIR_INFORMATION );
  639. UdfSetFidFromFidAndFe( IdBothDirInfo->FileId, ThisFid, ThisFe );
  640. break;
  641. case FileIdFullDirectoryInformation:
  642. IdFullDirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_ID_FULL_DIR_INFORMATION );
  643. UdfSetFidFromFidAndFe( IdFullDirInfo->FileId, ThisFid, ThisFe );
  644. break;
  645. default:
  646. break;
  647. }
  648. //
  649. // Now copy as much of the name as possible.
  650. //
  651. if (FileNameBytes != 0) {
  652. //
  653. // This is a Unicode name, we can copy the bytes directly.
  654. //
  655. RtlCopyMemory( Add2Ptr( UserBuffer, NextEntry + BaseLength, PVOID ),
  656. CompoundDirContext.DirContext.ObjectName.Buffer,
  657. FileNameBytes );
  658. }
  659. //
  660. // Fill in the short name if we got STATUS_SUCCESS. The short name
  661. // may already be in the file context, otherwise we will check
  662. // whether the long name is 8.3. Special case the self and parent
  663. // directory names.
  664. //
  665. if ((Status == STATUS_SUCCESS) &&
  666. (InfoClass == FileBothDirectoryInformation ||
  667. InfoClass == FileIdBothDirectoryInformation) &&
  668. FlagOn( CompoundDirContext.DirContext.Flags, DIR_CONTEXT_FLAG_SEEN_NONCONSTANT )) {
  669. //
  670. // If we already have the short name then copy into the user's buffer.
  671. //
  672. if (CompoundDirContext.DirContext.ShortObjectName.Length != 0) {
  673. RtlCopyMemory( DirInfo->ShortName,
  674. CompoundDirContext.DirContext.ShortObjectName.Buffer,
  675. CompoundDirContext.DirContext.ShortObjectName.Length );
  676. DirInfo->ShortNameLength = (CCHAR) CompoundDirContext.DirContext.ShortObjectName.Length;
  677. //
  678. // If the short name length is currently zero then check if
  679. // the long name is not 8.3. We can copy the short name in
  680. // unicode form directly into the caller's buffer.
  681. //
  682. } else {
  683. if (!UdfIs8dot3Name( IrpContext,
  684. CompoundDirContext.DirContext.ObjectName )) {
  685. UNICODE_STRING ShortName;
  686. ShortName.Buffer = DirInfo->ShortName;
  687. ShortName.MaximumLength = BYTE_COUNT_8_DOT_3;
  688. UdfGenerate8dot3Name( IrpContext,
  689. &CompoundDirContext.DirContext.PureObjectName,
  690. &ShortName );
  691. DirInfo->ShortNameLength = (CCHAR) ShortName.Length;
  692. }
  693. }
  694. }
  695. //
  696. // Update the information with the number of bytes stored in the
  697. // buffer. We quad-align the existing buffer to add any necessary
  698. // pad bytes.
  699. //
  700. Information = NextEntry + BaseLength + FileNameBytes;
  701. //
  702. // Go back to the previous entry and fill in the update to this entry.
  703. //
  704. *(Add2Ptr( UserBuffer, LastEntry, PULONG )) = NextEntry - LastEntry;
  705. //
  706. // Set up our variables for the next dirent.
  707. //
  708. InitialQuery = FALSE;
  709. LastEntry = NextEntry;
  710. NextEntry = QuadAlign( Information );
  711. }
  712. except (!FsRtlIsNtstatusExpected(GetExceptionCode()) ?
  713. EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
  714. //
  715. // We must have had a problem filling in the user's buffer, so stop
  716. // and fail this request.
  717. //
  718. Information = 0;
  719. try_leave( Status = GetExceptionCode());
  720. }
  721. }
  722. }
  723. finally {
  724. if (!AbnormalTermination() && !NT_ERROR( Status )) {
  725. //
  726. // Update the Ccb to show the current state of the enumeration.
  727. //
  728. UdfLockFcb( IrpContext, Fcb );
  729. Ccb->CurrentFileIndex = CompoundDirContext.FileIndex.QuadPart;
  730. //
  731. // Update our notion of a high 32bit file index. We only do this once to avoid the hit
  732. // of thrashing the Fcb mutex to do this for every entry. If it is ever neccesary to use
  733. // this information, the difference of a few dozen entries from the optimal pick-up point
  734. // will be trivial.
  735. //
  736. if (CompoundDirContext.FileIndex.HighPart == 0 &&
  737. CompoundDirContext.FileIndex.LowPart > Ccb->HighestReturnableFileIndex) {
  738. Ccb->HighestReturnableFileIndex = CompoundDirContext.FileIndex.LowPart;
  739. }
  740. //
  741. // Mark in the CCB whether or not to skip the current entry on next call
  742. // (if we returned it in the current buffer).
  743. //
  744. ClearFlag( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT );
  745. if (ReturnNextEntry) {
  746. SetFlag( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT );
  747. }
  748. UdfUnlockFcb( IrpContext, Fcb );
  749. }
  750. //
  751. // Cleanup our search context.
  752. //
  753. UdfCleanupCompoundDirContext( IrpContext, &CompoundDirContext );
  754. //
  755. // Release the Fcb.
  756. //
  757. UdfReleaseFile( IrpContext, Fcb );
  758. }
  759. DebugTrace(( 0, Dbg, "UdfQueryDirectory -> %x\n", Status ));
  760. //
  761. // Complete the request here.
  762. //
  763. Irp->IoStatus.Information = Information;
  764. UdfCompleteRequest( IrpContext, Irp, Status );
  765. return Status;
  766. }
  767. //
  768. // Local support routines
  769. //
  770. NTSTATUS
  771. UdfNotifyChangeDirectory (
  772. IN PIRP_CONTEXT IrpContext,
  773. IN PIRP Irp,
  774. IN PIO_STACK_LOCATION IrpSp,
  775. IN PCCB Ccb
  776. )
  777. /*++
  778. Routine Description:
  779. This routine performs the notify change directory operation. It is
  780. responsible for either completing of enqueuing the input Irp. Although there
  781. will never be a notify signalled on a readonly disk we still support this call.
  782. We have already checked that this is not an OpenById handle.
  783. Arguments:
  784. Irp - Supplies the Irp to process
  785. IrpSp - Io stack location for this request.
  786. Ccb - Handle to the directory being watched.
  787. Return Value:
  788. NTSTATUS - STATUS_PENDING, any other error will raise.
  789. --*/
  790. {
  791. PAGED_CODE();
  792. //
  793. // Always set the wait bit in the IrpContext so the initial wait can't fail.
  794. //
  795. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
  796. //
  797. // Acquire the Vcb shared.
  798. //
  799. UdfAcquireVcbShared( IrpContext, IrpContext->Vcb, FALSE );
  800. //
  801. // Use a try-finally to facilitate cleanup.
  802. //
  803. try {
  804. //
  805. // Verify the Vcb.
  806. //
  807. UdfVerifyVcb( IrpContext, IrpContext->Vcb );
  808. //
  809. // Call the Fsrtl package to process the request. We cast the
  810. // unicode strings to ansi strings as the dir notify package
  811. // only deals with memory matching.
  812. //
  813. FsRtlNotifyFullChangeDirectory( IrpContext->Vcb->NotifySync,
  814. &IrpContext->Vcb->DirNotifyList,
  815. Ccb,
  816. (PSTRING) &IrpSp->FileObject->FileName,
  817. BooleanFlagOn( IrpSp->Flags, SL_WATCH_TREE ),
  818. FALSE,
  819. IrpSp->Parameters.NotifyDirectory.CompletionFilter,
  820. Irp,
  821. NULL,
  822. NULL );
  823. } finally {
  824. //
  825. // Release the Vcb.
  826. //
  827. UdfReleaseVcb( IrpContext, IrpContext->Vcb );
  828. }
  829. //
  830. // Cleanup the IrpContext.
  831. //
  832. UdfCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
  833. return STATUS_PENDING;
  834. }
  835. //
  836. // Local support routine
  837. //
  838. NTSTATUS
  839. UdfInitializeEnumeration (
  840. IN PIRP_CONTEXT IrpContext,
  841. IN PIO_STACK_LOCATION IrpSp,
  842. IN PFCB Fcb,
  843. IN OUT PCCB Ccb,
  844. IN OUT PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext,
  845. OUT PBOOLEAN ReturnNextEntry,
  846. OUT PBOOLEAN ReturnSingleEntry,
  847. OUT PBOOLEAN InitialQuery
  848. )
  849. /*++
  850. Routine Description:
  851. This routine is called to initialize the enumeration variables and structures.
  852. We look at the state of a previous enumeration from the Ccb as well as any
  853. input values from the user. On exit we will position the DirContext at
  854. a file in the directory and let the caller know whether this entry or the
  855. next entry should be returned.
  856. Arguments:
  857. IrpSp - Irp stack location for this request.
  858. Fcb - Fcb for this directory.
  859. Ccb - Ccb for the directory handle.
  860. CompoundDirContext - Context to use for this enumeration.
  861. ReturnNextEntry - Address to store whether we should return the entry at
  862. the context position or the next entry.
  863. ReturnSingleEntry - Address to store whether we should only return
  864. a single entry.
  865. InitialQuery - Address to store whether this is the first enumeration
  866. query on this handle.
  867. Return Value:
  868. None.
  869. --*/
  870. {
  871. NTSTATUS Status;
  872. PUNICODE_STRING FileName;
  873. UNICODE_STRING SearchExpression;
  874. PUNICODE_STRING RestartName = NULL;
  875. ULONG CcbFlags;
  876. LONGLONG FileIndex;
  877. ULONG HighFileIndex;
  878. BOOLEAN KnownIndex;
  879. BOOLEAN Found;
  880. PAGED_CODE();
  881. //
  882. // Check inputs.
  883. //
  884. ASSERT_IRP_CONTEXT( IrpContext );
  885. ASSERT_FCB_INDEX( Fcb );
  886. ASSERT_CCB( Ccb );
  887. //
  888. // If this is the initial query then build a search expression from the input
  889. // file name.
  890. //
  891. if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) {
  892. FileName = IrpSp->Parameters.QueryDirectory.FileName;
  893. CcbFlags = 0;
  894. //
  895. // If the filename is not specified or is a single '*' then we will
  896. // match all names.
  897. //
  898. if ((FileName == NULL) ||
  899. (FileName->Buffer == NULL) ||
  900. (FileName->Length == 0) ||
  901. ((FileName->Length == sizeof( WCHAR )) &&
  902. (FileName->Buffer[0] == L'*'))) {
  903. SetFlag( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL );
  904. SearchExpression.Length =
  905. SearchExpression.MaximumLength = 0;
  906. SearchExpression.Buffer = NULL;
  907. //
  908. // Otherwise build the name from the name in the stack location.
  909. // This involves checking for wild card characters and upcasing the
  910. // string if this is a case-insensitive search.
  911. //
  912. } else {
  913. //
  914. // The name better have at least one character.
  915. //
  916. if (FileName->Length == 0) {
  917. UdfRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER );
  918. }
  919. //
  920. // Check for wildcards in the separate components.
  921. //
  922. if (FsRtlDoesNameContainWildCards( FileName)) {
  923. SetFlag( CcbFlags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD );
  924. }
  925. //
  926. // Now create the search expression to store in the Ccb.
  927. //
  928. SearchExpression.Buffer = FsRtlAllocatePoolWithTag( UdfPagedPool,
  929. FileName->Length,
  930. TAG_ENUM_EXPRESSION );
  931. SearchExpression.MaximumLength = FileName->Length;
  932. //
  933. // Either copy the name directly or perform the upcase.
  934. //
  935. if (FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE )) {
  936. Status = RtlUpcaseUnicodeString( &SearchExpression,
  937. FileName,
  938. FALSE );
  939. //
  940. // This should never fail.
  941. //
  942. ASSERT( Status == STATUS_SUCCESS );
  943. } else {
  944. RtlCopyMemory( SearchExpression.Buffer,
  945. FileName->Buffer,
  946. FileName->Length );
  947. }
  948. SearchExpression.Length = FileName->Length;
  949. }
  950. //
  951. // But we do not want to return the constant "." and ".." entries for
  952. // the root directory, for consistency with the rest of Microsoft's
  953. // filesystems.
  954. //
  955. if (Fcb == Fcb->Vcb->RootIndexFcb) {
  956. SetFlag( CcbFlags, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY );
  957. }
  958. //
  959. // Now lock the Fcb in order to update the Ccb with the inital
  960. // enumeration values.
  961. //
  962. UdfLockFcb( IrpContext, Fcb );
  963. //
  964. // Check again that this is the initial search.
  965. //
  966. if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) {
  967. //
  968. // Update the values in the Ccb.
  969. //
  970. Ccb->CurrentFileIndex = 0;
  971. Ccb->SearchExpression = SearchExpression;
  972. //
  973. // Set the appropriate flags in the Ccb.
  974. //
  975. SetFlag( Ccb->Flags, CcbFlags | CCB_FLAG_ENUM_INITIALIZED );
  976. //
  977. // Otherwise cleanup any buffer allocated here.
  978. //
  979. } else {
  980. if (!FlagOn( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL )) {
  981. UdfFreePool( &SearchExpression.Buffer );
  982. }
  983. }
  984. //
  985. // Otherwise lock the Fcb so we can read the current enumeration values.
  986. //
  987. } else {
  988. UdfLockFcb( IrpContext, Fcb );
  989. }
  990. //
  991. // Capture the current state of the enumeration.
  992. //
  993. // If the user specified an index then use his offset. We always
  994. // return the next entry in this case. If no name is specified,
  995. // then we can't perform the restart.
  996. //
  997. if (FlagOn( IrpSp->Flags, SL_INDEX_SPECIFIED ) &&
  998. IrpSp->Parameters.QueryDirectory.FileName != NULL) {
  999. KnownIndex = FALSE;
  1000. FileIndex = IrpSp->Parameters.QueryDirectory.FileIndex;
  1001. RestartName = IrpSp->Parameters.QueryDirectory.FileName;
  1002. *ReturnNextEntry = TRUE;
  1003. //
  1004. // We will use the highest file index reportable to the caller as a
  1005. // starting point as required if we cannot directly land at the
  1006. // specified location.
  1007. //
  1008. HighFileIndex = Ccb->HighestReturnableFileIndex;
  1009. //
  1010. // If we are restarting the scan then go from the self entry.
  1011. //
  1012. } else if (FlagOn( IrpSp->Flags, SL_RESTART_SCAN )) {
  1013. KnownIndex = TRUE;
  1014. FileIndex = 0;
  1015. *ReturnNextEntry = FALSE;
  1016. //
  1017. // Otherwise use the values from the Ccb.
  1018. //
  1019. } else {
  1020. KnownIndex = TRUE;
  1021. FileIndex = Ccb->CurrentFileIndex;
  1022. *ReturnNextEntry = BooleanFlagOn( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT );
  1023. }
  1024. //
  1025. // Unlock the Fcb.
  1026. //
  1027. UdfUnlockFcb( IrpContext, Fcb );
  1028. //
  1029. // We have the starting offset in the directory and whether to return
  1030. // that entry or the next. If we are at the beginning of the directory
  1031. // and are returning that entry, then tell our caller this is the
  1032. // initial query.
  1033. //
  1034. *InitialQuery = FALSE;
  1035. if ((FileIndex == 0) &&
  1036. !(*ReturnNextEntry)) {
  1037. *InitialQuery = TRUE;
  1038. }
  1039. //
  1040. // Determine the offset in the stream to position the context and
  1041. // whether this offset is known to be a file offset.
  1042. //
  1043. // If this offset is known to be safe then go ahead and position the
  1044. // context. This handles the cases where the offset is the beginning
  1045. // of the stream, the offset is from a previous search or this is the
  1046. // initial query.
  1047. //
  1048. if (KnownIndex) {
  1049. Found = UdfLookupInitialFileIndex( IrpContext, Fcb, CompoundDirContext, &FileIndex );
  1050. ASSERT( Found );
  1051. //
  1052. // Avoid a raise in UdfUpdateDirNames if we're re-starting from a CCB index
  1053. // after the parent entry, but in a new call to querydirectory (new DirContext)
  1054. //
  1055. if (1 <= FileIndex) {
  1056. SetFlag( CompoundDirContext->DirContext.Flags, DIR_CONTEXT_FLAG_SEEN_PARENT );
  1057. }
  1058. //
  1059. // Try to directly jump to the specified file index. Otherwise we walk through
  1060. // the directory from the beginning (or the saved highest known offset if that is
  1061. // useful) until we reach the entry which contains this offset.
  1062. //
  1063. } else {
  1064. //
  1065. // We need to handle the special case of a restart from a synthesized
  1066. // entry - this is the one time where the restart index can be zero
  1067. // without requiring us to search above the 2^32 byte mark.
  1068. //
  1069. if (UdfFullCompareNames( IrpContext,
  1070. RestartName,
  1071. &UdfUnicodeDirectoryNames[SELF_ENTRY] ) == EqualTo) {
  1072. FileIndex = UDF_FILE_INDEX_VIRTUAL_SELF;
  1073. Found = UdfLookupInitialFileIndex( IrpContext, Fcb, CompoundDirContext, &FileIndex );
  1074. ASSERT( Found );
  1075. //
  1076. // We are restarting from a physical entry. If the restart index is zero, we were
  1077. // unable to inform the caller as to the "real" file index due to the dispartity
  1078. // between the ULONG FileIndex in the return structures and the LONGLONG offsets
  1079. // possible in directory streams. In this case, we will go as high as we were able
  1080. // to inform the caller of and search linearly from that point forward.
  1081. //
  1082. // It is also possible (realistic? unknown) that the restart index is somewhere in the
  1083. // middle of an entry and we won't find anything useable. In this case we try to find
  1084. // the entry which contains this index, using it as the real restart point.
  1085. //
  1086. } else {
  1087. //
  1088. // See if we need the high water mark.
  1089. //
  1090. if (FileIndex == 0) {
  1091. //
  1092. // We know that this is good.
  1093. //
  1094. FileIndex = Max( Ccb->HighestReturnableFileIndex, UDF_FILE_INDEX_PHYSICAL );;
  1095. KnownIndex = TRUE;
  1096. }
  1097. //
  1098. // The file index is now useful, falling into two cases
  1099. //
  1100. // 1) KnownIndex == FALSE - searching by index
  1101. // 2) KnownIndex == TRUE - searching by name
  1102. //
  1103. // Go set up our inquiry.
  1104. //
  1105. Found = UdfLookupInitialFileIndex( IrpContext, Fcb, CompoundDirContext, &FileIndex );
  1106. if (KnownIndex) {
  1107. //
  1108. // Walk forward to discover an entry named per the caller's expectation.
  1109. //
  1110. do {
  1111. UdfUpdateDirNames( IrpContext,
  1112. &CompoundDirContext->DirContext,
  1113. BooleanFlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE ));
  1114. if (UdfFullCompareNames( IrpContext,
  1115. &CompoundDirContext->DirContext.CaseObjectName,
  1116. RestartName ) == EqualTo) {
  1117. break;
  1118. }
  1119. Found = UdfLookupNextFileIndex( IrpContext, Fcb, CompoundDirContext );
  1120. } while (Found);
  1121. } else if (!Found) {
  1122. LONGLONG LastFileIndex;
  1123. //
  1124. // Perform the search for the entry by index from the beginning of the physical directory.
  1125. //
  1126. LastFileIndex = UDF_FILE_INDEX_PHYSICAL;
  1127. Found = UdfLookupInitialFileIndex( IrpContext, Fcb, CompoundDirContext, &LastFileIndex );
  1128. ASSERT( Found );
  1129. //
  1130. // Keep walking through the directory until we run out of
  1131. // entries or we find an entry which ends beyond the input
  1132. // index value (index search case) or corresponds to the
  1133. // name we are looking for (name search case).
  1134. //
  1135. do {
  1136. //
  1137. // If we have passed the index value then exit.
  1138. //
  1139. if (CompoundDirContext->FileIndex.QuadPart > FileIndex) {
  1140. Found = FALSE;
  1141. break;
  1142. }
  1143. //
  1144. // Remember the current position in case we need to go back.
  1145. //
  1146. LastFileIndex = CompoundDirContext->FileIndex.QuadPart;
  1147. //
  1148. // Exit if the next entry is beyond the desired index value.
  1149. //
  1150. if (LastFileIndex + ISONsrFidSize( CompoundDirContext->DirContext.Fid ) > FileIndex) {
  1151. break;
  1152. }
  1153. Found = UdfLookupNextFileIndex( IrpContext, Fcb, CompoundDirContext );
  1154. } while (Found);
  1155. //
  1156. // If we didn't find the entry then go back to the last known entry.
  1157. //
  1158. if (!Found) {
  1159. UdfCleanupDirContext( IrpContext, &CompoundDirContext->DirContext );
  1160. UdfInitializeDirContext( IrpContext, &CompoundDirContext->DirContext );
  1161. Found = UdfLookupInitialFileIndex( IrpContext, Fcb, CompoundDirContext, &LastFileIndex );
  1162. ASSERT( Found );
  1163. }
  1164. }
  1165. }
  1166. }
  1167. //
  1168. // Only update the dirent name if we will need it for some reason.
  1169. // Don't update this name if we are returning the next entry, and
  1170. // don't update it if it was already done.
  1171. //
  1172. if (!(*ReturnNextEntry) &&
  1173. CompoundDirContext->DirContext.PureObjectName.Buffer == NULL) {
  1174. //
  1175. // If the caller specified an index that corresponds to a
  1176. // deleted file, they are trying to be tricky. Don't let them.
  1177. //
  1178. if (CompoundDirContext->DirContext.Fid &&
  1179. FlagOn( CompoundDirContext->DirContext.Fid->Flags, NSR_FID_F_DELETED )) {
  1180. return STATUS_INVALID_PARAMETER;
  1181. }
  1182. //
  1183. // Update the names in the dirent.
  1184. //
  1185. UdfUpdateDirNames( IrpContext,
  1186. &CompoundDirContext->DirContext,
  1187. BooleanFlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE ));
  1188. }
  1189. //
  1190. // Look at the flag in the IrpSp indicating whether to return just
  1191. // one entry.
  1192. //
  1193. *ReturnSingleEntry = FALSE;
  1194. if (FlagOn( IrpSp->Flags, SL_RETURN_SINGLE_ENTRY )) {
  1195. *ReturnSingleEntry = TRUE;
  1196. }
  1197. return STATUS_SUCCESS;
  1198. }
  1199. //
  1200. // Local support routine
  1201. //
  1202. BOOLEAN
  1203. UdfEnumerateIndex (
  1204. IN PIRP_CONTEXT IrpContext,
  1205. IN PCCB Ccb,
  1206. IN OUT PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext,
  1207. IN BOOLEAN ReturnNextEntry
  1208. )
  1209. /*++
  1210. Routine Description:
  1211. This routine is the worker routine for index enumeration. We are positioned
  1212. at some dirent in the directory and will either return the first match
  1213. at that point or look to the next entry. The Ccb contains details about
  1214. the type of matching to do.
  1215. Arguments:
  1216. Ccb - Ccb for this directory handle.
  1217. CompoundDirContext - context already positioned at some entry in the directory.
  1218. ReturnNextEntry - Indicates if we are returning this entry or should start
  1219. with the next entry.
  1220. Return Value:
  1221. BOOLEAN - TRUE if next entry is found, FALSE otherwise.
  1222. --*/
  1223. {
  1224. BOOLEAN Found = FALSE;
  1225. PDIR_ENUM_CONTEXT DirContext;
  1226. PAGED_CODE();
  1227. //
  1228. // Check inputs.
  1229. //
  1230. ASSERT_IRP_CONTEXT( IrpContext );
  1231. ASSERT_CCB( Ccb );
  1232. //
  1233. // Directly reference the directory enumeration context for convenience.
  1234. //
  1235. DirContext = &CompoundDirContext->DirContext;
  1236. //
  1237. // Loop until we find a match or exaust the directory.
  1238. //
  1239. while (TRUE) {
  1240. //
  1241. // Move to the next entry unless we want to consider the current
  1242. // entry.
  1243. //
  1244. if (ReturnNextEntry) {
  1245. if (!UdfLookupNextFileIndex( IrpContext, Ccb->Fcb, CompoundDirContext )) {
  1246. break;
  1247. }
  1248. if (FlagOn( DirContext->Fid->Flags, NSR_FID_F_DELETED )) {
  1249. continue;
  1250. }
  1251. UdfUpdateDirNames( IrpContext,
  1252. DirContext,
  1253. BooleanFlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE ));
  1254. } else {
  1255. ReturnNextEntry = TRUE;
  1256. }
  1257. //
  1258. // Don't bother if we have a constant entry and are ignoring them.
  1259. //
  1260. if (!FlagOn( DirContext->Flags, DIR_CONTEXT_FLAG_SEEN_NONCONSTANT ) &&
  1261. FlagOn( Ccb->Flags, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY )) {
  1262. continue;
  1263. }
  1264. //
  1265. // If we match all names then return to our caller.
  1266. //
  1267. if (FlagOn( Ccb->Flags, CCB_FLAG_ENUM_MATCH_ALL )) {
  1268. DirContext->ShortObjectName.Length = 0;
  1269. Found = TRUE;
  1270. break;
  1271. }
  1272. //
  1273. // Check if the long name matches the search expression.
  1274. //
  1275. if (UdfIsNameInExpression( IrpContext,
  1276. &DirContext->CaseObjectName,
  1277. &Ccb->SearchExpression,
  1278. BooleanFlagOn( Ccb->Flags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD ))) {
  1279. //
  1280. // Let our caller know we found an entry.
  1281. //
  1282. DirContext->ShortObjectName.Length = 0;
  1283. Found = TRUE;
  1284. break;
  1285. }
  1286. //
  1287. // The long name didn't match so we need to check for a
  1288. // possible short name match. There is no match if the
  1289. // long name is one of the constant entries or already
  1290. // is 8dot3.
  1291. //
  1292. if (!(!FlagOn( DirContext->Flags, DIR_CONTEXT_FLAG_SEEN_NONCONSTANT ) ||
  1293. UdfIs8dot3Name( IrpContext,
  1294. DirContext->CaseObjectName ))) {
  1295. //
  1296. // Allocate the shortname if it isn't already done.
  1297. //
  1298. if (DirContext->ShortObjectName.Buffer == NULL) {
  1299. DirContext->ShortObjectName.Buffer = FsRtlAllocatePoolWithTag( UdfPagedPool,
  1300. BYTE_COUNT_8_DOT_3,
  1301. TAG_SHORT_FILE_NAME );
  1302. DirContext->ShortObjectName.MaximumLength = BYTE_COUNT_8_DOT_3;
  1303. }
  1304. UdfGenerate8dot3Name( IrpContext,
  1305. &DirContext->PureObjectName,
  1306. &DirContext->ShortObjectName );
  1307. //
  1308. // Check if this name matches.
  1309. //
  1310. if (UdfIsNameInExpression( IrpContext,
  1311. &DirContext->ShortObjectName,
  1312. &Ccb->SearchExpression,
  1313. BooleanFlagOn( Ccb->Flags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD ))) {
  1314. //
  1315. // Let our caller know we found an entry.
  1316. //
  1317. Found = TRUE;
  1318. break;
  1319. }
  1320. }
  1321. }
  1322. return Found;
  1323. }
  1324. //
  1325. // Local support routine
  1326. //
  1327. VOID
  1328. UdfLookupFileEntryInEnumeration (
  1329. IN PIRP_CONTEXT IrpContext,
  1330. IN PFCB Fcb,
  1331. IN PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext
  1332. )
  1333. /*++
  1334. Routine Description:
  1335. This routine retrieves the file entry associated with the current location in
  1336. the enumeration of a compound directory context.
  1337. Arguments:
  1338. Fcb - the directory being enumerated.
  1339. CompoundDirContext - a corresponding context for the enumeration.
  1340. Return Value:
  1341. None. Status may be raised on discovery of corruption.
  1342. --*/
  1343. {
  1344. PNSR_FID Fid;
  1345. PICBFILE Fe;
  1346. ULONG Length;
  1347. Fid = CompoundDirContext->DirContext.Fid;
  1348. //
  1349. // Figure out where the ICB we want is.
  1350. //
  1351. if (UdfIsFileIndexVirtual( CompoundDirContext->FileIndex.QuadPart )) {
  1352. //
  1353. // Synthesize! We only have to synthesize the self entry. The name is already done,
  1354. // so the remaining work is trivial.
  1355. //
  1356. ASSERT( Fid == NULL );
  1357. //
  1358. // Lift the FE corresponding to this directory
  1359. //
  1360. UdfCleanupIcbContext( IrpContext, &CompoundDirContext->IcbContext );
  1361. UdfInitializeIcbContextFromFcb( IrpContext,
  1362. &CompoundDirContext->IcbContext,
  1363. Fcb );
  1364. Length = Fcb->RootExtentLength;
  1365. } else {
  1366. //
  1367. // Lift the FE corresponding to this FID.
  1368. //
  1369. ASSERT( Fid != NULL );
  1370. UdfCleanupIcbContext( IrpContext, &CompoundDirContext->IcbContext );
  1371. UdfInitializeIcbContext( IrpContext,
  1372. &CompoundDirContext->IcbContext,
  1373. Fcb->Vcb,
  1374. DESTAG_ID_NSR_FILE,
  1375. Fid->Icb.Start.Partition,
  1376. Fid->Icb.Start.Lbn,
  1377. BlockSize( IrpContext->Vcb) );
  1378. Length = Fid->Icb.Length.Length;
  1379. }
  1380. //
  1381. // Retrieve the ICB for inspection.
  1382. //
  1383. UdfLookupActiveIcb( IrpContext,
  1384. &CompoundDirContext->IcbContext,
  1385. Length);
  1386. Fe = (PICBFILE) CompoundDirContext->IcbContext.Active.View;
  1387. //
  1388. // Perform some basic verification that the FE is of the proper type and that
  1389. // FID and FE agree as to the type of the object. We explicitly check that
  1390. // a legal filesystem-level FE type is discovered, even though we don't support
  1391. // them in other paths. Note that we leave the IcbContext->IcbType as FILE even
  1392. // though we may have picked up an extended file entry.
  1393. //
  1394. if (((Fe->Destag.Ident != DESTAG_ID_NSR_FILE) &&
  1395. ((Fe->Destag.Ident != DESTAG_ID_NSR_EXT_FILE) || (!UdfExtendedFEAllowed( IrpContext->Vcb)))) ||
  1396. (((Fid && FlagOn( Fid->Flags, NSR_FID_F_DIRECTORY )) ||
  1397. Fid == NULL) &&
  1398. Fe->Icbtag.FileType != ICBTAG_FILE_T_DIRECTORY) ||
  1399. (Fe->Icbtag.FileType != ICBTAG_FILE_T_FILE &&
  1400. Fe->Icbtag.FileType != ICBTAG_FILE_T_DIRECTORY &&
  1401. Fe->Icbtag.FileType != ICBTAG_FILE_T_BLOCK_DEV &&
  1402. Fe->Icbtag.FileType != ICBTAG_FILE_T_CHAR_DEV &&
  1403. Fe->Icbtag.FileType != ICBTAG_FILE_T_FIFO &&
  1404. Fe->Icbtag.FileType != ICBTAG_FILE_T_C_ISSOCK &&
  1405. Fe->Icbtag.FileType != ICBTAG_FILE_T_PATHLINK &&
  1406. Fe->Icbtag.FileType != ICBTAG_FILE_T_REALTIME)
  1407. ) {
  1408. UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  1409. }
  1410. }
  1411. //
  1412. // Local support routine
  1413. //
  1414. BOOLEAN
  1415. UdfLookupInitialFileIndex (
  1416. IN PIRP_CONTEXT IrpContext,
  1417. IN PFCB Fcb,
  1418. IN PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext,
  1419. IN PLONGLONG InitialIndex
  1420. )
  1421. /*++
  1422. Routine Description:
  1423. This routine begins the enumeration of a directory by setting the context
  1424. at the first avaliable virtual directory entry.
  1425. Arguments:
  1426. Fcb - the directory being enumerated.
  1427. CompoundDirContext - a corresponding context for the enumeration.
  1428. InitialIndex - an optional starting file index to base the enumeration.
  1429. Return Value:
  1430. TRUE will be returned if a valid entry is found at this offset, FALSE otherwise.
  1431. --*/
  1432. {
  1433. LONGLONG DirOffset;
  1434. if (UdfIsFileIndexVirtual( *InitialIndex )) {
  1435. //
  1436. // We only synthesize a single virtual directory entry. Position the context
  1437. // at the virtual self entry.
  1438. //
  1439. CompoundDirContext->FileIndex.QuadPart = UDF_FILE_INDEX_VIRTUAL_SELF;
  1440. return TRUE;
  1441. }
  1442. CompoundDirContext->FileIndex.QuadPart = *InitialIndex;
  1443. //
  1444. // Find the base offset in the directory and look it up.
  1445. //
  1446. DirOffset = UdfFileIndexToPhysicalOffset( *InitialIndex );
  1447. return UdfLookupInitialDirEntry( IrpContext,
  1448. Fcb,
  1449. &CompoundDirContext->DirContext,
  1450. &DirOffset );
  1451. }
  1452. //
  1453. // Local support routine
  1454. //
  1455. BOOLEAN
  1456. UdfLookupNextFileIndex (
  1457. IN PIRP_CONTEXT IrpContext,
  1458. IN PFCB Fcb,
  1459. IN PCOMPOUND_DIR_ENUM_CONTEXT CompoundDirContext
  1460. )
  1461. /*++
  1462. Routine Description:
  1463. This routine advances the enumeration of a virtual directory by one entry.
  1464. Arguments:
  1465. Fcb - the directory being enumerated.
  1466. CompoundDirContext - a corresponding context for the enumeration.
  1467. Return Value:
  1468. BOOLEAN True if another Fid is avaliable, False if we are at the end.
  1469. --*/
  1470. {
  1471. ULONG Advance;
  1472. BOOLEAN Result;
  1473. //
  1474. // Advance from the synthesized to the physical directory.
  1475. //
  1476. if (UdfIsFileIndexVirtual( CompoundDirContext->FileIndex.QuadPart )) {
  1477. Result = UdfLookupInitialDirEntry( IrpContext,
  1478. Fcb,
  1479. &CompoundDirContext->DirContext,
  1480. NULL );
  1481. if (Result) {
  1482. CompoundDirContext->FileIndex.QuadPart = UDF_FILE_INDEX_PHYSICAL;
  1483. }
  1484. return Result;
  1485. }
  1486. Advance = ISONsrFidSize( CompoundDirContext->DirContext.Fid );
  1487. //
  1488. // Advance to the next entry in this directory.
  1489. //
  1490. Result = UdfLookupNextDirEntry( IrpContext, Fcb, &CompoundDirContext->DirContext );
  1491. if (Result) {
  1492. CompoundDirContext->FileIndex.QuadPart += Advance;
  1493. }
  1494. return Result;
  1495. }