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.

1262 lines
35 KiB

  1. /*++
  2. Copyright (c) 1996-2000 Microsoft Corporation
  3. Module Name:
  4. DirSup.c
  5. Abstract:
  6. This module implements the support for walking across on-disk directory
  7. structures.
  8. // @@BEGIN_DDKSPLIT
  9. Author:
  10. Dan Lovinger [DanLo] 11-Jun-1996
  11. Revision History:
  12. // @@END_DDKSPLIT
  13. --*/
  14. #include "UdfProcs.h"
  15. //
  16. // The Bug check file id for this module
  17. //
  18. #define BugCheckFileId (UDFS_BUG_CHECK_DIRSUP)
  19. //
  20. // The local debug trace level
  21. //
  22. #define Dbg (UDFS_DEBUG_LEVEL_DIRSUP)
  23. //
  24. // Local support routines.
  25. //
  26. BOOLEAN
  27. UdfLookupDirEntryPostProcessing (
  28. IN PIRP_CONTEXT IrpContext,
  29. IN PFCB Fcb,
  30. IN PDIR_ENUM_CONTEXT DirContext,
  31. IN BOOLEAN ReturnError
  32. );
  33. #ifdef ALLOC_PRAGMA
  34. #pragma alloc_text(PAGE, UdfCleanupDirContext)
  35. #pragma alloc_text(PAGE, UdfFindDirEntry)
  36. #pragma alloc_text(PAGE, UdfInitializeDirContext)
  37. #pragma alloc_text(PAGE, UdfLookupDirEntryPostProcessing)
  38. #pragma alloc_text(PAGE, UdfLookupInitialDirEntry)
  39. #pragma alloc_text(PAGE, UdfLookupNextDirEntry)
  40. #pragma alloc_text(PAGE, UdfUpdateDirNames)
  41. #endif
  42. VOID
  43. UdfInitializeDirContext (
  44. IN PIRP_CONTEXT IrpContext,
  45. IN PDIR_ENUM_CONTEXT DirContext
  46. )
  47. /*++
  48. Routine Description:
  49. This routine initializes a directory enumeartion context.
  50. Call this exactly once in the lifetime of a context.
  51. Arguments:
  52. DirContext - a context to initialize
  53. Return Value:
  54. None.
  55. --*/
  56. {
  57. //
  58. // Check inputs.
  59. //
  60. ASSERT_IRP_CONTEXT( IrpContext );
  61. //
  62. // Provide defaults for fields, nothing too special.
  63. //
  64. RtlZeroMemory( DirContext, sizeof(DIR_ENUM_CONTEXT) );
  65. }
  66. VOID
  67. UdfCleanupDirContext (
  68. IN PIRP_CONTEXT IrpContext,
  69. IN PDIR_ENUM_CONTEXT DirContext
  70. )
  71. /*++
  72. Routine Description:
  73. This routine cleans up a directory enumeration context for reuse.
  74. Arguments:
  75. DirContext - a context to clean.
  76. Return Value:
  77. None.
  78. --*/
  79. {
  80. PAGED_CODE();
  81. //
  82. // Check input.
  83. //
  84. ASSERT_IRP_CONTEXT( IrpContext );
  85. //
  86. // Dump the allocation we store the triple of names in.
  87. //
  88. UdfFreePool( &DirContext->NameBuffer );
  89. //
  90. // And the short name.
  91. //
  92. UdfFreePool( &DirContext->ShortObjectName.Buffer );
  93. //
  94. // Unpin the view.
  95. //
  96. UdfUnpinData( IrpContext, &DirContext->Bcb );
  97. //
  98. // Free a buffered Fid that may remain.
  99. //
  100. if (FlagOn( DirContext->Flags, DIR_CONTEXT_FLAG_FID_BUFFERED )) {
  101. UdfFreePool( &DirContext->Fid );
  102. }
  103. //
  104. // Zero everything else out.
  105. //
  106. RtlZeroMemory( DirContext, sizeof( DIR_ENUM_CONTEXT ) );
  107. }
  108. BOOLEAN
  109. UdfLookupInitialDirEntry (
  110. IN PIRP_CONTEXT IrpContext,
  111. IN PFCB Fcb,
  112. IN PDIR_ENUM_CONTEXT DirContext,
  113. IN PLONGLONG InitialOffset OPTIONAL
  114. )
  115. /*++
  116. Routine Description:
  117. This routine begins the enumeration of a directory by setting the context
  118. at the first avaliable directory entry.
  119. Arguments:
  120. Fcb - the directory being enumerated.
  121. DirContext - a corresponding context for the enumeration.
  122. InitialOffset - an optional starting byte offset to base the enumeration.
  123. Return Value:
  124. If InitialOffset is unspecified, TRUE will always be returned. Failure will result
  125. in a raised status indicating corruption.
  126. If InitialOffset is specified, TRUE will be returned if a valid entry is found at this
  127. offset, FALSE otherwise.
  128. --*/
  129. {
  130. BOOLEAN Result;
  131. PAGED_CODE();
  132. //
  133. // Check inputs.
  134. //
  135. ASSERT_IRP_CONTEXT( IrpContext );
  136. ASSERT_FCB_INDEX( Fcb );
  137. //
  138. // Create the internal stream if it isn't already in place.
  139. //
  140. if (Fcb->FileObject == NULL) {
  141. UdfCreateInternalStream( IrpContext, Fcb->Vcb, Fcb );
  142. }
  143. //
  144. // Reset the flags.
  145. //
  146. DirContext->Flags = 0;
  147. if (InitialOffset) {
  148. //
  149. // If we are beginning in the middle of the stream, adjust the sanity check flags.
  150. //
  151. if (*InitialOffset != 0) {
  152. DirContext->Flags = DIR_CONTEXT_FLAG_SEEN_NONCONSTANT | DIR_CONTEXT_FLAG_SEEN_PARENT;
  153. }
  154. //
  155. // Now set up the range we will map. This is constrained by the size of a cache view.
  156. //
  157. DirContext->BaseOffset.QuadPart = GenericTruncate( *InitialOffset, VACB_MAPPING_GRANULARITY );
  158. DirContext->ViewOffset = (ULONG) GenericOffset( *InitialOffset, VACB_MAPPING_GRANULARITY );
  159. } else {
  160. //
  161. // Map at the beginning.
  162. //
  163. DirContext->BaseOffset.QuadPart = 0;
  164. DirContext->ViewOffset = 0;
  165. }
  166. //
  167. // Contain the view length by the size of the stream and map.
  168. //
  169. DirContext->ViewLength = VACB_MAPPING_GRANULARITY;
  170. if (DirContext->BaseOffset.QuadPart + DirContext->ViewLength > Fcb->FileSize.QuadPart) {
  171. DirContext->ViewLength = (ULONG) (Fcb->FileSize.QuadPart - DirContext->BaseOffset.QuadPart);
  172. }
  173. UdfUnpinData( IrpContext, &DirContext->Bcb );
  174. CcMapData( Fcb->FileObject,
  175. &DirContext->BaseOffset,
  176. DirContext->ViewLength,
  177. TRUE,
  178. &DirContext->Bcb,
  179. &DirContext->View );
  180. DirContext->Fid = Add2Ptr( DirContext->View, DirContext->ViewOffset, PNSR_FID );
  181. //
  182. // The state of the context is now valid. Tail off into our common post-processor
  183. // to finish the work.
  184. //
  185. return UdfLookupDirEntryPostProcessing( IrpContext,
  186. Fcb,
  187. DirContext,
  188. (BOOLEAN) (InitialOffset != NULL));
  189. }
  190. BOOLEAN
  191. UdfLookupNextDirEntry (
  192. IN PIRP_CONTEXT IrpContext,
  193. IN PFCB Fcb,
  194. IN PDIR_ENUM_CONTEXT DirContext
  195. )
  196. /*++
  197. Routine Description:
  198. This routine advances the enumeration of a directory by one entry.
  199. Arguments:
  200. Fcb - the directory being enumerated.
  201. DirContext - a corresponding context for the enumeration.
  202. Return Value:
  203. BOOLEAN True if another Fid is avaliable, False if we are at the end.
  204. --*/
  205. {
  206. PAGED_CODE();
  207. //
  208. // Check inputs.
  209. //
  210. ASSERT_IRP_CONTEXT( IrpContext );
  211. ASSERT_FCB_INDEX( Fcb );
  212. //
  213. // If we have reached the end, stop.
  214. //
  215. if (DirContext->BaseOffset.QuadPart + DirContext->NextFidOffset == Fcb->FileSize.QuadPart) {
  216. return FALSE;
  217. }
  218. //
  219. // If the previous Fid was buffered, dismantle it now.
  220. //
  221. if (FlagOn( DirContext->Flags, DIR_CONTEXT_FLAG_FID_BUFFERED )) {
  222. ClearFlag( DirContext->Flags, DIR_CONTEXT_FLAG_FID_BUFFERED );
  223. UdfFreePool( &DirContext->Fid );
  224. }
  225. //
  226. // Move the pointers based on the knowledge generated in the previous iteration.
  227. //
  228. DirContext->ViewOffset = DirContext->NextFidOffset;
  229. DirContext->Fid = Add2Ptr( DirContext->View, DirContext->ViewOffset, PNSR_FID );
  230. //
  231. // The state of the context is now valid. Tail off into our common post-processor
  232. // to finish the work.
  233. //
  234. return UdfLookupDirEntryPostProcessing( IrpContext,
  235. Fcb,
  236. DirContext,
  237. FALSE );
  238. }
  239. VOID
  240. UdfUpdateDirNames (
  241. IN PIRP_CONTEXT IrpContext,
  242. IN PDIR_ENUM_CONTEXT DirContext,
  243. IN BOOLEAN IgnoreCase
  244. )
  245. /*++
  246. Routine Description:
  247. This routine fills in the non-short names of a directory enumeration context
  248. for the Fid currently referenced.
  249. Arguments:
  250. DirContext - a corresponding context to fill in.
  251. IgnoreCase - whether the caller wants to be insensitive to case.
  252. Return Value:
  253. None.
  254. --*/
  255. {
  256. PUCHAR NameDstring;
  257. BOOLEAN ContainsIllegal;
  258. USHORT NameLength;
  259. USHORT RequiredBufferLength;
  260. USHORT PresentLength;
  261. PAGED_CODE();
  262. //
  263. // Check input.
  264. //
  265. ASSERT_IRP_CONTEXT( IrpContext );
  266. DebugTrace(( +1, Dbg, "UdfUpdateDirNames\n" ));
  267. //
  268. // Handle the case of the self directory entry.
  269. //
  270. if (DirContext->Fid == NULL) {
  271. //
  272. // Simply synthesize
  273. //
  274. //
  275. // It doesn't hurt to be pedantic about initialization, so do it all.
  276. //
  277. DirContext->PureObjectName.Length =
  278. DirContext->CaseObjectName.Length =
  279. DirContext->ObjectName.Length = UdfUnicodeDirectoryNames[SELF_ENTRY].Length;
  280. DirContext->PureObjectName.MaximumLength =
  281. DirContext->CaseObjectName.MaximumLength =
  282. DirContext->ObjectName.MaximumLength = UdfUnicodeDirectoryNames[SELF_ENTRY].MaximumLength;
  283. DirContext->PureObjectName.Buffer =
  284. DirContext->CaseObjectName.Buffer =
  285. DirContext->ObjectName.Buffer = UdfUnicodeDirectoryNames[SELF_ENTRY].Buffer;
  286. //
  287. // All done.
  288. //
  289. DebugTrace(( 0, Dbg, "Self Entry case\n" ));
  290. DebugTrace(( -1, Dbg, "UdfUpdateDirNames -> VOID\n" ));
  291. return;
  292. }
  293. //
  294. // Handle the case of the parent directory entry.
  295. //
  296. if (FlagOn( DirContext->Fid->Flags, NSR_FID_F_PARENT )) {
  297. //
  298. // Parent entries must occur at the front of the directory and
  299. // have a fid length of zero (13346 4/14.4.4).
  300. //
  301. if (FlagOn( DirContext->Flags, DIR_CONTEXT_FLAG_SEEN_NONCONSTANT ) ||
  302. DirContext->Fid->FileIDLen != 0) {
  303. UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  304. }
  305. //
  306. // Note that we have seen the parent entry.
  307. //
  308. SetFlag( DirContext->Flags, DIR_CONTEXT_FLAG_SEEN_PARENT );
  309. //
  310. // It doesn't hurt to be pedantic about initialization, so do it all.
  311. //
  312. DirContext->PureObjectName.Length =
  313. DirContext->CaseObjectName.Length =
  314. DirContext->ObjectName.Length = UdfUnicodeDirectoryNames[PARENT_ENTRY].Length;
  315. DirContext->PureObjectName.MaximumLength =
  316. DirContext->CaseObjectName.MaximumLength =
  317. DirContext->ObjectName.MaximumLength = UdfUnicodeDirectoryNames[PARENT_ENTRY].MaximumLength;
  318. DirContext->PureObjectName.Buffer =
  319. DirContext->CaseObjectName.Buffer =
  320. DirContext->ObjectName.Buffer = UdfUnicodeDirectoryNames[PARENT_ENTRY].Buffer;
  321. //
  322. // All done.
  323. //
  324. DebugTrace(( 0, Dbg, "Parent Entry case\n" ));
  325. DebugTrace(( -1, Dbg, "UdfUpdateDirNames -> VOID\n" ));
  326. return;
  327. }
  328. //
  329. // We now know that we will need to convert the name in a real FID, so figure out where
  330. // it sits in the descriptor.
  331. //
  332. NameDstring = Add2Ptr( DirContext->Fid, ISONsrFidConstantSize + DirContext->Fid->ImpUseLen, PUCHAR );
  333. //
  334. // Every directory must record a parent entry.
  335. //
  336. if (!FlagOn( DirContext->Flags, DIR_CONTEXT_FLAG_SEEN_PARENT)) {
  337. UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  338. }
  339. //
  340. // Note that we are proceeding into the non-constant portion of a directory.
  341. //
  342. SetFlag( DirContext->Flags, DIR_CONTEXT_FLAG_SEEN_NONCONSTANT );
  343. //
  344. // Make sure the dstring is good CS0
  345. //
  346. UdfCheckLegalCS0Dstring( IrpContext,
  347. NameDstring,
  348. DirContext->Fid->FileIDLen,
  349. 0,
  350. FALSE );
  351. //
  352. // Don't bother allocating tiny buffers - always make sure we get enough for an 8.3 name.
  353. //
  354. RequiredBufferLength =
  355. NameLength = Max( BYTE_COUNT_8_DOT_3, UdfCS0DstringUnicodeSize( IrpContext,
  356. NameDstring,
  357. DirContext->Fid->FileIDLen) );
  358. //
  359. // Illegality is both actual illegal characters and too many characters.
  360. //
  361. ContainsIllegal = (!UdfCS0DstringIsLegalFileName( NameDstring, DirContext->Fid->FileIDLen ) ||
  362. (NameLength / sizeof( WCHAR )) > MAX_LEN);
  363. //
  364. // If we're illegal, we will need more characters to hold the uniqifying stamp.
  365. //
  366. if (ContainsIllegal) {
  367. RequiredBufferLength = (NameLength += (CRC_LEN * sizeof(WCHAR)));
  368. }
  369. //
  370. // If we need to build a case insensitive name, need more space.
  371. //
  372. if (IgnoreCase) {
  373. RequiredBufferLength += NameLength;
  374. }
  375. //
  376. // If we need to render the names due to illegal characters, more space again.
  377. //
  378. if (ContainsIllegal) {
  379. RequiredBufferLength += NameLength;
  380. } else {
  381. //
  382. // Make sure the names aren't seperated. If more illegal names are found we can
  383. // resplit the buffer but until then avoid the expense of having to copy bytes
  384. // ... odds are that illegal characters are going to be a rarish occurance.
  385. //
  386. DirContext->PureObjectName.Buffer = DirContext->ObjectName.Buffer;
  387. }
  388. //
  389. // We expect the name lengths and hence buffer size to be multiple of WCHAR
  390. //
  391. ASSERT( 0 == (RequiredBufferLength & 1));
  392. DebugTrace(( 0, Dbg,
  393. "Ob %s%sneeds %d bytes (%d byte chunks), have %d\n",
  394. (IgnoreCase? "Ic " : ""),
  395. (ContainsIllegal? "Ci " : ""),
  396. RequiredBufferLength,
  397. NameLength,
  398. DirContext->AllocLength ));
  399. //
  400. // Check if we need more space for the names. We will need more if the name size is greater
  401. // than the maximum we can currently store, or if we have stumbled across illegal characters
  402. // and the current Pure name is not seperated from the exposed Object name.
  403. //
  404. // Note that IgnoreCase remains constant across usage of a context so we don't have to wonder
  405. // if it has been seperated from the ObjectName - it'll always be correct.
  406. //
  407. if ((NameLength > DirContext->ObjectName.MaximumLength) ||
  408. (ContainsIllegal && (DirContext->ObjectName.Buffer == DirContext->PureObjectName.Buffer))) {
  409. USHORT DividedBufferLength = 0;
  410. DebugTrace(( 0, Dbg, "Resizing buffers\n" ));
  411. //
  412. // Figure out if we can break up the current allocation in a different way before falling
  413. // back to a new allocation. Ensure we use even byte size chunks, or else we can land
  414. // up with alignment faults on IA64.
  415. //
  416. if (DirContext->AllocLength >= RequiredBufferLength) {
  417. DividedBufferLength = (DirContext->AllocLength / (1 +
  418. (IgnoreCase? 1 : 0) +
  419. (ContainsIllegal? 1 : 0))) & ~(USHORT)1;
  420. }
  421. if (DividedBufferLength >= NameLength) {
  422. //
  423. // So we can still use the current allocation, re-divided.
  424. //
  425. DirContext->PureObjectName.MaximumLength =
  426. DirContext->CaseObjectName.MaximumLength =
  427. DirContext->ObjectName.MaximumLength = DividedBufferLength;
  428. DebugTrace(( 0, Dbg,
  429. "... by resplit into %d byte chunks\n",
  430. DirContext->ObjectName.MaximumLength ));
  431. //
  432. // Set the buffer pointers up. Required adjustment will occur below.
  433. //
  434. DirContext->PureObjectName.Buffer =
  435. DirContext->CaseObjectName.Buffer =
  436. DirContext->ObjectName.Buffer = DirContext->NameBuffer;
  437. } else {
  438. DebugTrace(( 0, Dbg, "... by allocating new pool\n" ));
  439. //
  440. // Oh well, no choice but to fall back into the pool. Drop our previous hunk.
  441. //
  442. UdfFreePool( &DirContext->NameBuffer );
  443. DirContext->AllocLength = 0;
  444. //
  445. // The names share an allocation for efficiency.
  446. //
  447. DirContext->PureObjectName.MaximumLength =
  448. DirContext->CaseObjectName.MaximumLength =
  449. DirContext->ObjectName.MaximumLength = NameLength;
  450. DirContext->NameBuffer =
  451. DirContext->PureObjectName.Buffer =
  452. DirContext->CaseObjectName.Buffer =
  453. DirContext->ObjectName.Buffer = FsRtlAllocatePoolWithTag( UdfPagedPool,
  454. RequiredBufferLength,
  455. TAG_FILE_NAME );
  456. DirContext->AllocLength = RequiredBufferLength;
  457. }
  458. //
  459. // In the presence of the "as appropriate" names, adjust the buffer locations. Note
  460. // that ObjectName.Buffer is always the base of the allocated space.
  461. //
  462. if (IgnoreCase) {
  463. DirContext->CaseObjectName.Buffer = Add2Ptr( DirContext->ObjectName.Buffer,
  464. DirContext->ObjectName.MaximumLength,
  465. PWCHAR );
  466. }
  467. if (ContainsIllegal) {
  468. DirContext->PureObjectName.Buffer = Add2Ptr( DirContext->CaseObjectName.Buffer,
  469. DirContext->CaseObjectName.MaximumLength,
  470. PWCHAR );
  471. }
  472. }
  473. ASSERT( RequiredBufferLength <= DirContext->AllocLength );
  474. //
  475. // Convert the dstring.
  476. //
  477. UdfConvertCS0DstringToUnicode( IrpContext,
  478. NameDstring,
  479. DirContext->Fid->FileIDLen,
  480. 0,
  481. &DirContext->PureObjectName );
  482. //
  483. // If illegal characters were present, run the name through the UDF transmogrifier.
  484. //
  485. if (ContainsIllegal) {
  486. UdfRenderNameToLegalUnicode( IrpContext,
  487. &DirContext->PureObjectName,
  488. &DirContext->ObjectName );
  489. //
  490. // The ObjectName is the same as the PureObjectName.
  491. //
  492. } else {
  493. DirContext->ObjectName.Length = DirContext->PureObjectName.Length;
  494. }
  495. //
  496. // Upcase the result if required.
  497. //
  498. if (IgnoreCase) {
  499. UdfUpcaseName( IrpContext,
  500. &DirContext->ObjectName,
  501. &DirContext->CaseObjectName );
  502. }
  503. DebugTrace(( -1, Dbg, "UdfUpdateDirNames -> VOID\n" ));
  504. return;
  505. }
  506. BOOLEAN
  507. UdfFindDirEntry (
  508. IN PIRP_CONTEXT IrpContext,
  509. IN PFCB Fcb,
  510. IN PUNICODE_STRING Name,
  511. IN BOOLEAN IgnoreCase,
  512. IN BOOLEAN ShortName,
  513. IN PDIR_ENUM_CONTEXT DirContext
  514. )
  515. /*++
  516. Routine Description:
  517. This routine walks the directory specified for an entry which matches the input
  518. criteria.
  519. Arguments:
  520. Fcb - the directory to search
  521. Name - name to search for
  522. IgnoreCase - whether this search should be case-insensitive (Name will already
  523. be upcased)
  524. ShortName - whether the name should be searched for according to short name rules
  525. DirContext - context structure to use and return results in
  526. Return Value:
  527. BOOLEAN True if a matching directory entry is being returned, False otherwise.
  528. --*/
  529. {
  530. PUNICODE_STRING MatchName;
  531. PAGED_CODE();
  532. //
  533. // Check inputs.
  534. //
  535. ASSERT_IRP_CONTEXT( IrpContext );
  536. ASSERT_FCB_INDEX( Fcb );
  537. DebugTrace(( +1, Dbg,
  538. "UdfFindDirEntry, Fcb=%08x Name=\"%wZ\" Ignore=%u Short=%u, DC=%08x\n",
  539. Fcb,
  540. Name,
  541. IgnoreCase,
  542. ShortName,
  543. DirContext ));
  544. //
  545. // Depending on the kind of search we are performing a different flavor of the found name
  546. // wil be used in the comparison.
  547. //
  548. if (ShortName) {
  549. MatchName = &DirContext->ShortObjectName;
  550. } else {
  551. MatchName = &DirContext->CaseObjectName;
  552. }
  553. //
  554. // Go get the first entry.
  555. //
  556. UdfLookupInitialDirEntry( IrpContext,
  557. Fcb,
  558. DirContext,
  559. NULL );
  560. //
  561. // Now loop looking for a good match.
  562. //
  563. do {
  564. //
  565. // If it is deleted, we obviously aren't interested in it.
  566. //
  567. if (FlagOn( DirContext->Fid->Flags, NSR_FID_F_DELETED )) {
  568. continue;
  569. }
  570. UdfUpdateDirNames( IrpContext,
  571. DirContext,
  572. IgnoreCase );
  573. //
  574. // If this is a constant entry, just keep going.
  575. //
  576. if (!FlagOn( DirContext->Flags, DIR_CONTEXT_FLAG_SEEN_NONCONSTANT )) {
  577. continue;
  578. }
  579. DebugTrace(( 0, Dbg,
  580. "\"%wZ\" (pure \"%wZ\") @ +%08x\n",
  581. &DirContext->ObjectName,
  582. &DirContext->PureObjectName,
  583. DirContext->ViewOffset ));
  584. //
  585. // If we are searching for generated shortnames, a small subset of the names
  586. // in the directory are actually candidates for a match. Go get the name.
  587. //
  588. if (ShortName) {
  589. //
  590. // Now, only if this Fid's name is non 8.3 is it neccesary to work with it.
  591. //
  592. if (!UdfIs8dot3Name( IrpContext, DirContext->ObjectName )) {
  593. //
  594. // Allocate the shortname if it isn't already done.
  595. //
  596. if (DirContext->ShortObjectName.Buffer == NULL) {
  597. DirContext->ShortObjectName.Buffer = FsRtlAllocatePoolWithTag( UdfPagedPool,
  598. BYTE_COUNT_8_DOT_3,
  599. TAG_SHORT_FILE_NAME );
  600. DirContext->ShortObjectName.MaximumLength = BYTE_COUNT_8_DOT_3;
  601. }
  602. UdfGenerate8dot3Name( IrpContext,
  603. &DirContext->PureObjectName,
  604. &DirContext->ShortObjectName );
  605. DebugTrace(( 0, Dbg,
  606. "built shortname \"%wZ\"\n", &DirContext->ShortObjectName ));
  607. } else {
  608. //
  609. // As an 8.3 name already, this name will not have caused us to have to generate
  610. // a short name, so it can't be the case that the caller is looking for it.
  611. //
  612. continue;
  613. }
  614. }
  615. if (UdfFullCompareNames( IrpContext,
  616. MatchName,
  617. Name ) == EqualTo) {
  618. //
  619. // Got a match, so give it up.
  620. //
  621. DebugTrace(( 0, Dbg, "HIT\n" ));
  622. DebugTrace(( -1, Dbg, "UdfFindDirEntry -> TRUE\n" ));
  623. return TRUE;
  624. }
  625. } while ( UdfLookupNextDirEntry( IrpContext,
  626. Fcb,
  627. DirContext ));
  628. //
  629. // No match was found.
  630. //
  631. DebugTrace(( -1, Dbg, "UdfFindDirEntry -> FALSE\n" ));
  632. return FALSE;
  633. }
  634. //
  635. // Local support routine
  636. //
  637. BOOLEAN
  638. UdfLookupDirEntryPostProcessing (
  639. IN PIRP_CONTEXT IrpContext,
  640. IN PFCB Fcb,
  641. IN PDIR_ENUM_CONTEXT DirContext,
  642. IN BOOLEAN ReturnError
  643. )
  644. /*++
  645. Routine Description:
  646. This routine is the core engine of directory stream enumeration. It receives
  647. a context which has been advanced and does the integrity checks and final
  648. extraction of the Fid with respect to file cache granularity restrictions.
  649. NOTE: we assume that a Fid cannot span a cache view. The maximum size of a
  650. Fid is just over 32k, so this is a good and likely permanent assumption.
  651. Arguments:
  652. Fcb - the directory being enumerated.
  653. DirContext - a corresponding context for the enumeration.
  654. ReturnError - whether errors should be returned (or raised)
  655. Return Value:
  656. BOOLEAN according to the successful extraction of the Fid. If ReturnError is
  657. FALSE, then failure will result in a raised status.
  658. --*/
  659. {
  660. BOOLEAN Result = TRUE;
  661. PNSR_FID FidBufferC = NULL;
  662. PNSR_FID FidBuffer = NULL;
  663. PNSR_FID FidC;
  664. PNSR_FID Fid;
  665. ULONG FidSize;
  666. ULONG FidBytesInPreviousView = 0;
  667. PAGED_CODE();
  668. //
  669. // Check inputs.
  670. //
  671. ASSERT_IRP_CONTEXT( IrpContext );
  672. ASSERT_FCB_INDEX( Fcb );
  673. try {
  674. //
  675. // First check that the stream can contain another FID.
  676. //
  677. if (DirContext->BaseOffset.QuadPart +
  678. DirContext->ViewOffset +
  679. ISONsrFidConstantSize > Fcb->FileSize.QuadPart) {
  680. DebugTrace(( 0, Dbg,
  681. "UdfLookupDirEntryPostProcessing: DC %p, constant header overlaps end of dir\n",
  682. DirContext ));
  683. try_leave( Result = FALSE );
  684. }
  685. //
  686. // We now build up the constant portion of the FID for use. It may be the case that
  687. // this spans a view boundary and must be buffered, or is entirely in the next view
  688. // and we simply need to advance.
  689. //
  690. if (GenericTruncatePtr( Add2Ptr( DirContext->Fid, ISONsrFidConstantSize - 1, PUCHAR ), VACB_MAPPING_GRANULARITY ) !=
  691. DirContext->View) {
  692. FidBytesInPreviousView = GenericRemainderPtr( DirContext->Fid, VACB_MAPPING_GRANULARITY );
  693. //
  694. // Only buffer if there are really bytes in the previous view.
  695. //
  696. if (FidBytesInPreviousView) {
  697. FidC =
  698. FidBufferC = FsRtlAllocatePoolWithTag( UdfPagedPool,
  699. ISONsrFidConstantSize,
  700. TAG_FID_BUFFER );
  701. RtlCopyMemory( FidBufferC,
  702. DirContext->Fid,
  703. FidBytesInPreviousView );
  704. }
  705. //
  706. // Now advance into the next view to pick up the rest.
  707. //
  708. DirContext->BaseOffset.QuadPart += VACB_MAPPING_GRANULARITY;
  709. DirContext->ViewOffset = 0;
  710. //
  711. // Contain the view length by the size of the stream and map.
  712. //
  713. DirContext->ViewLength = VACB_MAPPING_GRANULARITY;
  714. if (DirContext->BaseOffset.QuadPart + DirContext->ViewLength > Fcb->FileSize.QuadPart) {
  715. DirContext->ViewLength = (ULONG) (Fcb->FileSize.QuadPart - DirContext->BaseOffset.QuadPart);
  716. }
  717. UdfUnpinData( IrpContext, &DirContext->Bcb );
  718. CcMapData( Fcb->FileObject,
  719. &DirContext->BaseOffset,
  720. DirContext->ViewLength,
  721. TRUE,
  722. &DirContext->Bcb,
  723. &DirContext->View );
  724. //
  725. // We are guaranteed that this much lies in the stream. Build the rest of the
  726. // constant header.
  727. //
  728. if (FidBytesInPreviousView) {
  729. RtlCopyMemory( Add2Ptr( FidBufferC, FidBytesInPreviousView, PUCHAR ),
  730. DirContext->View,
  731. ISONsrFidConstantSize - FidBytesInPreviousView );
  732. //
  733. // In fact, this FID is perfectly aligned to the front of this view. No buffering
  734. // is required, and we just set the FID pointer.
  735. //
  736. } else {
  737. DirContext->Fid = DirContext->View;
  738. }
  739. }
  740. //
  741. // If no buffering was required, we can use the cache directly.
  742. //
  743. if (!FidBytesInPreviousView) {
  744. FidC = DirContext->Fid;
  745. }
  746. //
  747. // Now we can check that the Fid data lies within the stream bounds and is sized
  748. // within a logical block (per UDF). This will complete the size-wise integrity
  749. // verification.
  750. //
  751. if (((DirContext->BaseOffset.QuadPart +
  752. DirContext->ViewOffset -
  753. FidBytesInPreviousView +
  754. ISONsrFidSize( FidC ) > Fcb->FileSize.QuadPart) &&
  755. DebugTrace(( 0, Dbg,
  756. "UdfLookupDirEntryPostProcessing: DC %p, FID (FidC %p, FBIPV %u) overlaps end of dir\n",
  757. DirContext,
  758. FidC,
  759. FidBytesInPreviousView )))
  760. ||
  761. (ISONsrFidSize( FidC ) > BlockSize( Fcb->Vcb ) &&
  762. DebugTrace(( 0, Dbg,
  763. "UdfLookupDirEntryPostProcessing: DC %p, FID (FidC %p) larger than a logical block\n",
  764. DirContext,
  765. FidC )))) {
  766. try_leave( Result = FALSE );
  767. }
  768. //
  769. // Final Fid rollup.
  770. //
  771. //
  772. // The Fid may span a view boundary and should be buffered. If we already buffered, we know
  773. // we have to do this.
  774. //
  775. if (FidBytesInPreviousView ||
  776. GenericTruncatePtr( Add2Ptr( DirContext->Fid, ISONsrFidSize( FidC ) - 1, PUCHAR ), VACB_MAPPING_GRANULARITY ) !=
  777. DirContext->View) {
  778. Fid =
  779. FidBuffer = FsRtlAllocatePoolWithTag( UdfPagedPool,
  780. ISONsrFidSize( FidC ),
  781. TAG_FID_BUFFER );
  782. //
  783. // Pull the fidsize out now in case we're still pointing to the cache (ie. no
  784. // buffering was required for fixed portion) but are about to change the mapping
  785. // below (need to buffer for variable portion).
  786. //
  787. FidSize = ISONsrFidSize( FidC);
  788. //
  789. // If we already buffered and advanced for the header, just prefill
  790. // the final Fid buffer with the bytes that are now unavaliable.
  791. //
  792. if (FidBytesInPreviousView) {
  793. RtlCopyMemory( FidBuffer,
  794. FidBufferC,
  795. FidBytesInPreviousView );
  796. } else {
  797. //
  798. // Buffer and advance the view.
  799. //
  800. FidBytesInPreviousView = GenericRemainderPtr( DirContext->Fid, VACB_MAPPING_GRANULARITY );
  801. RtlCopyMemory( FidBuffer,
  802. DirContext->Fid,
  803. FidBytesInPreviousView );
  804. //
  805. // Now advance into the next view to pick up the rest.
  806. //
  807. DirContext->BaseOffset.QuadPart += VACB_MAPPING_GRANULARITY;
  808. DirContext->ViewOffset = 0;
  809. //
  810. // Contain the view length by the size of the stream and map.
  811. //
  812. DirContext->ViewLength = VACB_MAPPING_GRANULARITY;
  813. if (DirContext->BaseOffset.QuadPart + DirContext->ViewLength > Fcb->FileSize.QuadPart) {
  814. DirContext->ViewLength = (ULONG) (Fcb->FileSize.QuadPart - DirContext->BaseOffset.QuadPart);
  815. }
  816. UdfUnpinData( IrpContext, &DirContext->Bcb );
  817. CcMapData( Fcb->FileObject,
  818. &DirContext->BaseOffset,
  819. DirContext->ViewLength,
  820. TRUE,
  821. &DirContext->Bcb,
  822. &DirContext->View );
  823. }
  824. //
  825. // We are guaranteed that this much lies in the stream.
  826. //
  827. RtlCopyMemory( Add2Ptr( FidBuffer, FidBytesInPreviousView, PUCHAR ),
  828. DirContext->View,
  829. FidSize - FidBytesInPreviousView );
  830. } else {
  831. Fid = DirContext->Fid;
  832. }
  833. //
  834. // We finally have the whole Fid safely extracted from the cache, so the
  835. // integrity check is now the last step before success. For simplicity's
  836. // sake we trust the Lbn field.
  837. //
  838. Result = UdfVerifyDescriptor( IrpContext,
  839. &Fid->Destag,
  840. DESTAG_ID_NSR_FID,
  841. ISONsrFidSize( Fid ),
  842. Fid->Destag.Lbn,
  843. ReturnError );
  844. //
  845. // Prepare to return a buffered Fid.
  846. //
  847. if (FidBuffer && Result) {
  848. SetFlag( DirContext->Flags, DIR_CONTEXT_FLAG_FID_BUFFERED );
  849. DirContext->Fid = FidBuffer;
  850. FidBuffer = NULL;
  851. }
  852. } finally {
  853. UdfFreePool( &FidBuffer );
  854. UdfFreePool( &FidBufferC );
  855. }
  856. if (!ReturnError && !Result) {
  857. UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  858. }
  859. //
  860. // On success update the next Fid information in the context.
  861. // Note that we must drop in a hint as to where the next Fid
  862. // will be found so that the next advance will know how much
  863. // of a buffered Fid isn't in this view.
  864. //
  865. if (Result) {
  866. DirContext->NextFidOffset = DirContext->ViewOffset +
  867. ISONsrFidSize( Fid );
  868. if (FidBytesInPreviousView) {
  869. DirContext->NextFidOffset -= FidBytesInPreviousView;
  870. }
  871. }
  872. return Result;
  873. }