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.

1871 lines
49 KiB

  1. /*++
  2. Copyright (c) 1989-2000 Microsoft Corporation
  3. Module Name:
  4. DirSup.c
  5. Abstract:
  6. This module implements the dirent support routines for Cdfs.
  7. Directories on a CD consist of a number of contiguous sectors on
  8. the disk. File descriptors consist of one or more directory entries
  9. (dirents) within a directory. Files may contain version numbers. If
  10. present all like-named files will be ordered contiguously in the
  11. directory by decreasing version numbers. We will only return the
  12. first of these on a directory query unless the user explicitly
  13. asks for version numbers. Finally dirents will not span sector
  14. boundaries. Unused bytes at the end of a sector will be zero
  15. filled.
  16. Directory sector: Offset
  17. 2048
  18. +---------------------------------------------------------------+
  19. | | | | | | |
  20. | foo;4 | foo;4 | foo;3 | hat | zebra | Zero|
  21. | | | | | | Fill|
  22. | | final | single | | | |
  23. | | extent | extent | | | |
  24. +---------------------------------------------------------------+
  25. Dirent operations:
  26. - Position scan at known offset in directory. Dirent at this
  27. offset must exist and is valid. Used when scanning a directory
  28. from the beginning when the self entry is known to be valid.
  29. Used when positioning at the first dirent for an open
  30. file to scan the allocation information. Used when resuming
  31. a directory enumeration from a valid directory entry.
  32. - Position scan at known offset in directory. Dirent is known to
  33. start at this position but must be checked for validity.
  34. Used to read the self-directory entry.
  35. - Move to the next dirent within a directory.
  36. - Given a known starting dirent, collect all the dirents for
  37. that file. Scan will finish positioned at the last dirent
  38. for the file. We will accumulate the extent lengths to
  39. find the size of the file.
  40. - Given a known starting dirent, position the scan for the first
  41. dirent of the following file. Used when not interested in
  42. all of the details for the current file and are looking for
  43. the next file.
  44. - Update a common dirent structure with the details of the on-disk
  45. structure. This is used to smooth out the differences
  46. - Build the filename (name and version strings) out of the stream
  47. of bytes in the file name on disk. For Joliet disks we will have
  48. to convert to little endian.
  49. // @@BEGIN_DDKSPLIT
  50. Author:
  51. Brian Andrew [BrianAn] 01-July-1995
  52. Revision History:
  53. // @@END_DDKSPLIT
  54. --*/
  55. #include "CdProcs.h"
  56. //
  57. // The Bug check file id for this module
  58. //
  59. #define BugCheckFileId (CDFS_BUG_CHECK_DIRSUP)
  60. //
  61. // Local macros
  62. //
  63. //
  64. // PRAW_DIRENT
  65. // CdRawDirent (
  66. // IN PIRP_CONTEXT IrpContext,
  67. // IN PDIR_ENUM_CONTEXT DirContext
  68. // );
  69. //
  70. #define CdRawDirent(IC,DC) \
  71. Add2Ptr( (DC)->Sector, (DC)->SectorOffset, PRAW_DIRENT )
  72. //
  73. // Local support routines
  74. //
  75. ULONG
  76. CdCheckRawDirentBounds (
  77. IN PIRP_CONTEXT IrpContext,
  78. IN PDIRENT_ENUM_CONTEXT DirContext
  79. );
  80. XA_EXTENT_TYPE
  81. CdCheckForXAExtent (
  82. IN PIRP_CONTEXT IrpContext,
  83. IN PRAW_DIRENT RawDirent,
  84. IN OUT PDIRENT Dirent
  85. );
  86. #ifdef ALLOC_PRAGMA
  87. #pragma alloc_text(PAGE, CdCheckForXAExtent)
  88. #pragma alloc_text(PAGE, CdCheckRawDirentBounds)
  89. #pragma alloc_text(PAGE, CdCleanupFileContext)
  90. #pragma alloc_text(PAGE, CdFindFile)
  91. #pragma alloc_text(PAGE, CdFindDirectory)
  92. #pragma alloc_text(PAGE, CdFindFileByShortName)
  93. #pragma alloc_text(PAGE, CdLookupDirent)
  94. #pragma alloc_text(PAGE, CdLookupLastFileDirent)
  95. #pragma alloc_text(PAGE, CdLookupNextDirent)
  96. #pragma alloc_text(PAGE, CdLookupNextInitialFileDirent)
  97. #pragma alloc_text(PAGE, CdUpdateDirentFromRawDirent)
  98. #pragma alloc_text(PAGE, CdUpdateDirentName)
  99. #endif
  100. VOID
  101. CdLookupDirent (
  102. IN PIRP_CONTEXT IrpContext,
  103. IN PFCB Fcb,
  104. IN ULONG DirentOffset,
  105. OUT PDIRENT_ENUM_CONTEXT DirContext
  106. )
  107. /*++
  108. Routine Description:
  109. This routine is called to initiate a walk through a directory. We will
  110. position ourselves in the directory at offset DirentOffset. We know that
  111. a dirent begins at this boundary but may have to verify the dirent bounds.
  112. We will call this routine when looking up the first entry of a known
  113. file or verifying the self entry of a directory.
  114. Arguments:
  115. Fcb - Fcb for the directory being traversed.
  116. DirentOffset - This is our target point in the directory. We will map the
  117. page containing this entry and possibly verify the dirent bounds at
  118. this location.
  119. DirContext - This is the dirent context for this scan. We update it with
  120. the location of the dirent we found. This structure has been initialized
  121. outside of this call.
  122. Return Value:
  123. None.
  124. --*/
  125. {
  126. LONGLONG BaseOffset;
  127. PAGED_CODE();
  128. //
  129. // Initialize the offset of the first dirent we want to map.
  130. //
  131. DirContext->BaseOffset = SectorTruncate( DirentOffset );
  132. BaseOffset = DirContext->BaseOffset;
  133. DirContext->DataLength = SECTOR_SIZE;
  134. DirContext->SectorOffset = SectorOffset( DirentOffset );
  135. //
  136. // Truncate the data length if we are at the end of the file.
  137. //
  138. if (DirContext->DataLength > (Fcb->FileSize.QuadPart - BaseOffset)) {
  139. DirContext->DataLength = (ULONG) (Fcb->FileSize.QuadPart - BaseOffset);
  140. }
  141. //
  142. // Now map the data at this offset.
  143. //
  144. CcMapData( Fcb->FileObject,
  145. (PLARGE_INTEGER) &BaseOffset,
  146. DirContext->DataLength,
  147. TRUE,
  148. &DirContext->Bcb,
  149. &DirContext->Sector );
  150. //
  151. // Verify the dirent bounds.
  152. //
  153. DirContext->NextDirentOffset = CdCheckRawDirentBounds( IrpContext,
  154. DirContext );
  155. return;
  156. }
  157. BOOLEAN
  158. CdLookupNextDirent (
  159. IN PIRP_CONTEXT IrpContext,
  160. IN PFCB Fcb,
  161. IN PDIRENT_ENUM_CONTEXT CurrentDirContext,
  162. OUT PDIRENT_ENUM_CONTEXT NextDirContext
  163. )
  164. /*++
  165. Routine Description:
  166. This routine is called to find the next dirent in the directory. The
  167. current position is given and we look for the next. We leave the context
  168. for the starting position untouched and update the context for the
  169. dirent we found. The target context may already be initialized so we
  170. may already have the sector in memory.
  171. This routine will position the enumeration context for the next dirent and
  172. verify the dirent bounds.
  173. NOTE - This routine can be called with CurrentDirContext and NextDirContext
  174. pointing to the same enumeration context.
  175. Arguments:
  176. Fcb - Fcb for the directory being traversed.
  177. CurrentDirContext - This is the dirent context for this scan. We update
  178. it with the location of the dirent we found. This is currently
  179. pointing to a dirent location. The dirent bounds at this location
  180. have already been verified.
  181. NextDirContext - This is the dirent context to update with the dirent we
  182. find. This may already point to a dirent so we need to check if
  183. we are in the same sector and unmap any buffer as necessary.
  184. This dirent is left in an indeterminant state if we don't find a dirent.
  185. Return Value:
  186. BOOLEAN - TRUE if we find a location for the next dirent, FALSE otherwise.
  187. This routine can cause a raise if the directory is corrupt.
  188. --*/
  189. {
  190. LONGLONG CurrentBaseOffset = CurrentDirContext->BaseOffset;
  191. ULONG TempUlong;
  192. BOOLEAN FoundDirent = FALSE;
  193. PAGED_CODE();
  194. //
  195. // Check if a different sector is mapped. If so then move our target
  196. // enumeration context to the same sector.
  197. //
  198. if ((CurrentDirContext->BaseOffset != NextDirContext->BaseOffset) ||
  199. (NextDirContext->Bcb == NULL)) {
  200. //
  201. // Unpin the current target Bcb and map the next sector.
  202. //
  203. CdUnpinData( IrpContext, &NextDirContext->Bcb );
  204. CcMapData( Fcb->FileObject,
  205. (PLARGE_INTEGER) &CurrentBaseOffset,
  206. CurrentDirContext->DataLength,
  207. TRUE,
  208. &NextDirContext->Bcb,
  209. &NextDirContext->Sector );
  210. //
  211. // Copy the data length and sector offset.
  212. //
  213. NextDirContext->DataLength = CurrentDirContext->DataLength;
  214. NextDirContext->BaseOffset = CurrentDirContext->BaseOffset;
  215. }
  216. //
  217. // Now move to the same offset in the sector.
  218. //
  219. NextDirContext->SectorOffset = CurrentDirContext->SectorOffset;
  220. //
  221. // If the value is zero then unmap the current sector and set up
  222. // the base offset to the beginning of the next sector.
  223. //
  224. if (CurrentDirContext->NextDirentOffset == 0) {
  225. CurrentBaseOffset = NextDirContext->BaseOffset + NextDirContext->DataLength;
  226. //
  227. // Unmap the current sector. We test the value of the Bcb in the
  228. // loop below to see if we need to read in another sector.
  229. //
  230. CdUnpinData( IrpContext, &NextDirContext->Bcb );
  231. //
  232. // There is another possible dirent in the current sector. Update the
  233. // enumeration context to reflect this.
  234. //
  235. } else {
  236. NextDirContext->SectorOffset += CurrentDirContext->NextDirentOffset;
  237. }
  238. //
  239. // Now loop until we find the next possible dirent or walk off the directory.
  240. //
  241. while (TRUE) {
  242. //
  243. // If we don't currently have a sector mapped then map the
  244. // directory at the current offset.
  245. //
  246. if (NextDirContext->Bcb == NULL) {
  247. TempUlong = SECTOR_SIZE;
  248. if (TempUlong > (ULONG) (Fcb->FileSize.QuadPart - CurrentBaseOffset)) {
  249. TempUlong = (ULONG) (Fcb->FileSize.QuadPart - CurrentBaseOffset);
  250. //
  251. // If the length is zero then there is no dirent.
  252. //
  253. if (TempUlong == 0) {
  254. break;
  255. }
  256. }
  257. CcMapData( Fcb->FileObject,
  258. (PLARGE_INTEGER) &CurrentBaseOffset,
  259. TempUlong,
  260. TRUE,
  261. &NextDirContext->Bcb,
  262. &NextDirContext->Sector );
  263. NextDirContext->BaseOffset = (ULONG) CurrentBaseOffset;
  264. NextDirContext->SectorOffset = 0;
  265. NextDirContext->DataLength = TempUlong;
  266. }
  267. //
  268. // The CDFS spec allows for sectors in a directory to contain all zeroes.
  269. // In this case we need to move to the next sector. So look at the
  270. // current potential dirent for a zero length. Move to the next
  271. // dirent if length is zero.
  272. //
  273. if (*((PCHAR) CdRawDirent( IrpContext, NextDirContext )) != 0) {
  274. FoundDirent = TRUE;
  275. break;
  276. }
  277. CurrentBaseOffset = NextDirContext->BaseOffset + NextDirContext->DataLength;
  278. CdUnpinData( IrpContext, &NextDirContext->Bcb );
  279. }
  280. //
  281. // Check the dirent bounds if we found a dirent.
  282. //
  283. if (FoundDirent) {
  284. NextDirContext->NextDirentOffset = CdCheckRawDirentBounds( IrpContext,
  285. NextDirContext );
  286. }
  287. return FoundDirent;
  288. }
  289. VOID
  290. CdUpdateDirentFromRawDirent (
  291. IN PIRP_CONTEXT IrpContext,
  292. IN PFCB Fcb,
  293. IN PDIRENT_ENUM_CONTEXT DirContext,
  294. IN OUT PDIRENT Dirent
  295. )
  296. /*++
  297. Routine Description:
  298. This routine is called to safely copy the data from the dirent on disk
  299. to the in-memory dirent. The fields on disk are unaligned so we
  300. need to safely copy them to our structure.
  301. Arguments:
  302. Fcb - Fcb for the directory being scanned.
  303. DirContext - Enumeration context for the raw disk dirent.
  304. Dirent - In-memory dirent to update.
  305. Return Value:
  306. None.
  307. --*/
  308. {
  309. PRAW_DIRENT RawDirent = CdRawDirent( IrpContext, DirContext );
  310. PAGED_CODE();
  311. //
  312. // Clear all of the current state flags except the flag indicating that
  313. // we allocated a name string.
  314. //
  315. ClearFlag( Dirent->Flags, DIRENT_FLAG_NOT_PERSISTENT );
  316. //
  317. // The dirent offset is the sum of the start of the sector and the
  318. // sector offset.
  319. //
  320. Dirent->DirentOffset = DirContext->BaseOffset + DirContext->SectorOffset;
  321. //
  322. // Copy the dirent length from the raw dirent.
  323. //
  324. Dirent->DirentLength = RawDirent->DirLen;
  325. //
  326. // The starting offset on disk is computed by finding the starting
  327. // logical block and stepping over the Xar block.
  328. //
  329. CopyUchar4( &Dirent->StartingOffset, RawDirent->FileLoc );
  330. Dirent->StartingOffset += RawDirent->XarLen;
  331. //
  332. // Do a safe copy to get the data length.
  333. //
  334. CopyUchar4( &Dirent->DataLength, RawDirent->DataLen );
  335. //
  336. // Save a pointer to the time stamps.
  337. //
  338. Dirent->CdTime = RawDirent->RecordTime;
  339. //
  340. // Copy the dirent flags.
  341. //
  342. Dirent->DirentFlags = CdRawDirentFlags( IrpContext, RawDirent );
  343. //
  344. // For both the file unit and interleave skip we want to take the
  345. // logical block count.
  346. //
  347. Dirent->FileUnitSize =
  348. Dirent->InterleaveGapSize = 0;
  349. if (RawDirent->IntLeaveSize != 0) {
  350. Dirent->FileUnitSize = RawDirent->IntLeaveSize;
  351. Dirent->InterleaveGapSize = RawDirent->IntLeaveSkip;
  352. }
  353. //
  354. // Get the name length and remember a pointer to the start of the
  355. // name string. We don't do any processing on the name at this
  356. // point.
  357. //
  358. // Check that the name length is non-zero.
  359. //
  360. if (RawDirent->FileIdLen == 0) {
  361. CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  362. }
  363. Dirent->FileNameLen = RawDirent->FileIdLen;
  364. Dirent->FileName = RawDirent->FileId;
  365. //
  366. // If there are any remaining bytes at the end of the dirent then
  367. // there may be a system use area. We protect ourselves from
  368. // disks which don't pad the dirent entries correctly by using
  369. // a fudge factor of one. All system use areas must have a length
  370. // greater than one. Don't bother with the system use area
  371. // if this is a directory.
  372. //
  373. Dirent->XAAttributes = 0;
  374. Dirent->XAFileNumber = 0;
  375. Dirent->ExtentType = Form1Data;
  376. Dirent->SystemUseOffset = 0;
  377. if (!FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY ) &&
  378. (Dirent->DirentLength > ((FIELD_OFFSET( RAW_DIRENT, FileId ) + Dirent->FileNameLen) + 1))) {
  379. Dirent->SystemUseOffset = WordAlign( FIELD_OFFSET( RAW_DIRENT, FileId ) + Dirent->FileNameLen );
  380. }
  381. return;
  382. }
  383. VOID
  384. CdUpdateDirentName (
  385. IN PIRP_CONTEXT IrpContext,
  386. IN OUT PDIRENT Dirent,
  387. IN ULONG IgnoreCase
  388. )
  389. /*++
  390. Routine Description:
  391. This routine is called to update the name in the dirent with the name
  392. from the disk. We will look for the special case of the self and
  393. parent entries and also construct the Unicode name for the Joliet disk
  394. in order to work around the BigEndian on-disk structure.
  395. Arguments:
  396. Dirent - Pointer to the in-memory dirent structure.
  397. IgnoreCase - TRUE if we should build the upcased version. Otherwise we
  398. use the exact case name.
  399. Return Value:
  400. None.
  401. --*/
  402. {
  403. UCHAR DirectoryValue;
  404. ULONG Length;
  405. NTSTATUS Status;
  406. PAGED_CODE();
  407. //
  408. // Check if this is a self or parent entry. There is no version number
  409. // in these cases. We use a fixed string for these.
  410. //
  411. // Self-Entry - Length is 1, value is 0.
  412. // Parent-Entry - Length is 1, value is 1.
  413. //
  414. if ((Dirent->FileNameLen == 1) &&
  415. FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) {
  416. DirectoryValue = *((PCHAR) Dirent->FileName);
  417. if ((DirectoryValue == 0) || (DirectoryValue == 1)) {
  418. //
  419. // We should not have allocated a name by the time we see these cases.
  420. // If we have, this means that the image is in violation of ISO 9660 7.6.2,
  421. // which states that the ./.. entries must be the first two in the directory.
  422. //
  423. if (FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER )) {
  424. CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  425. }
  426. //
  427. // Now use one of the hard coded directory names.
  428. //
  429. Dirent->CdFileName.FileName = CdUnicodeDirectoryNames[DirectoryValue];
  430. //
  431. // Show that there is no version number.
  432. //
  433. Dirent->CdFileName.VersionString.Length = 0;
  434. //
  435. // The case name is the same as the exact name.
  436. //
  437. Dirent->CdCaseFileName = Dirent->CdFileName;
  438. //
  439. // Mark this as a constant value entry.
  440. //
  441. SetFlag( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY );
  442. //
  443. // Return now.
  444. //
  445. return;
  446. }
  447. }
  448. //
  449. // Mark this as a non-constant value entry.
  450. //
  451. ClearFlag( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY );
  452. //
  453. // Compute how large a buffer we will need. If this is an ignore
  454. // case operation then we will want a double size buffer. If the disk is not
  455. // a Joliet disk then we might need two bytes for each byte in the name.
  456. //
  457. Length = Dirent->FileNameLen;
  458. if (IgnoreCase) {
  459. Length *= 2;
  460. }
  461. if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) {
  462. Length *= sizeof( WCHAR );
  463. }
  464. //
  465. // Now decide if we need to allocate a new buffer. We will if
  466. // this name won't fit in the embedded name buffer and it is
  467. // larger than the current allocated buffer. We always use the
  468. // allocated buffer if present.
  469. //
  470. // If we haven't allocated a buffer then use the embedded buffer if the data
  471. // will fit. This is the typical case.
  472. //
  473. if (!FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER ) &&
  474. (Length <= sizeof( Dirent->NameBuffer ))) {
  475. Dirent->CdFileName.FileName.MaximumLength = sizeof( Dirent->NameBuffer );
  476. Dirent->CdFileName.FileName.Buffer = Dirent->NameBuffer;
  477. } else {
  478. //
  479. // We need to use an allocated buffer. Check if the current buffer
  480. // is large enough.
  481. //
  482. if (Length > Dirent->CdFileName.FileName.MaximumLength) {
  483. //
  484. // Free any allocated buffer.
  485. //
  486. if (FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER )) {
  487. ExFreePool( Dirent->CdFileName.FileName.Buffer );
  488. ClearFlag( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER );
  489. }
  490. Dirent->CdFileName.FileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool,
  491. Length,
  492. TAG_DIRENT_NAME );
  493. SetFlag( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER );
  494. Dirent->CdFileName.FileName.MaximumLength = (USHORT) Length;
  495. }
  496. }
  497. //
  498. // We now have a buffer for the name. We need to either convert the on-disk bigendian
  499. // to little endian or covert the name to Unicode.
  500. //
  501. if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) {
  502. Status = RtlOemToUnicodeN( Dirent->CdFileName.FileName.Buffer,
  503. Dirent->CdFileName.FileName.MaximumLength,
  504. &Length,
  505. Dirent->FileName,
  506. Dirent->FileNameLen );
  507. ASSERT( Status == STATUS_SUCCESS );
  508. Dirent->CdFileName.FileName.Length = (USHORT) Length;
  509. } else {
  510. //
  511. // Convert this string to little endian.
  512. //
  513. CdConvertBigToLittleEndian( IrpContext,
  514. Dirent->FileName,
  515. Dirent->FileNameLen,
  516. (PCHAR) Dirent->CdFileName.FileName.Buffer );
  517. Dirent->CdFileName.FileName.Length = (USHORT) Dirent->FileNameLen;
  518. }
  519. //
  520. // Split the name into name and version strings.
  521. //
  522. CdConvertNameToCdName( IrpContext,
  523. &Dirent->CdFileName );
  524. //
  525. // The name length better be non-zero.
  526. //
  527. if (Dirent->CdFileName.FileName.Length == 0) {
  528. CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  529. }
  530. //
  531. // If the filename ends with a period then back up one character.
  532. //
  533. if (Dirent->CdFileName.FileName.Buffer[(Dirent->CdFileName.FileName.Length - sizeof( WCHAR )) / 2] == L'.') {
  534. //
  535. // Slide the version string down.
  536. //
  537. if (Dirent->CdFileName.VersionString.Length != 0) {
  538. PWCHAR NewVersion;
  539. //
  540. // Start from the position currently containing the separator.
  541. //
  542. NewVersion = Add2Ptr( Dirent->CdFileName.FileName.Buffer,
  543. Dirent->CdFileName.FileName.Length,
  544. PWCHAR );
  545. //
  546. // Now overwrite the period.
  547. //
  548. RtlMoveMemory( NewVersion - 1,
  549. NewVersion,
  550. Dirent->CdFileName.VersionString.Length + sizeof( WCHAR ));
  551. //
  552. // Now point to the new version string.
  553. //
  554. Dirent->CdFileName.VersionString.Buffer = NewVersion;
  555. }
  556. //
  557. // Shrink the filename length.
  558. //
  559. Dirent->CdFileName.FileName.Length -= sizeof( WCHAR );
  560. }
  561. //
  562. // If this an exact case operation then use the filename exactly.
  563. //
  564. if (!IgnoreCase) {
  565. Dirent->CdCaseFileName = Dirent->CdFileName;
  566. //
  567. // Otherwise perform our upcase operation. We already have guaranteed the buffers are
  568. // there.
  569. //
  570. } else {
  571. Dirent->CdCaseFileName.FileName.Buffer = Add2Ptr( Dirent->CdFileName.FileName.Buffer,
  572. Dirent->CdFileName.FileName.MaximumLength / 2,
  573. PWCHAR);
  574. Dirent->CdCaseFileName.FileName.MaximumLength = Dirent->CdFileName.FileName.MaximumLength / 2;
  575. CdUpcaseName( IrpContext,
  576. &Dirent->CdFileName,
  577. &Dirent->CdCaseFileName );
  578. }
  579. return;
  580. }
  581. BOOLEAN
  582. CdFindFile (
  583. IN PIRP_CONTEXT IrpContext,
  584. IN PFCB Fcb,
  585. IN PCD_NAME Name,
  586. IN BOOLEAN IgnoreCase,
  587. IN OUT PFILE_ENUM_CONTEXT FileContext,
  588. OUT PCD_NAME *MatchingName
  589. )
  590. /*++
  591. Routine Description:
  592. This routine is called to search a dirctory for a file matching the input
  593. name. This name has been upcased at this point if this a case-insensitive
  594. search. The name has been separated into separate name and version strings.
  595. We look for an exact match in the name and only consider the version if
  596. there is a version specified in the search name.
  597. Arguments:
  598. Fcb - Fcb for the directory being scanned.
  599. Name - Name to search for.
  600. IgnoreCase - Indicates the case of the search.
  601. FileContext - File context to use for the search. This has already been
  602. initialized.
  603. MatchingName - Pointer to buffer containing matching name. We need this
  604. in case we don't match the name in the directory but match the
  605. short name instead.
  606. Return Value:
  607. BOOLEAN - TRUE if matching entry is found, FALSE otherwise.
  608. --*/
  609. {
  610. PDIRENT Dirent;
  611. ULONG ShortNameDirentOffset;
  612. BOOLEAN Found = FALSE;
  613. PAGED_CODE();
  614. //
  615. // Make sure there is a stream file for this Fcb.
  616. //
  617. if (Fcb->FileObject == NULL) {
  618. CdCreateInternalStream( IrpContext, Fcb->Vcb, Fcb );
  619. }
  620. //
  621. // Check to see whether we need to check for a possible short name.
  622. //
  623. ShortNameDirentOffset = CdShortNameDirentOffset( IrpContext, &Name->FileName );
  624. //
  625. // Position ourselves at the first entry.
  626. //
  627. CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset );
  628. //
  629. // Loop while there are more entries in this directory.
  630. //
  631. do {
  632. Dirent = &FileContext->InitialDirent->Dirent;
  633. //
  634. // We only consider files which don't have the associated bit set.
  635. // We also only look for files. All directories would already
  636. // have been found.
  637. //
  638. if (!FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_ASSOC | CD_ATTRIBUTE_DIRECTORY )) {
  639. //
  640. // Update the name in the current dirent.
  641. //
  642. CdUpdateDirentName( IrpContext, Dirent, IgnoreCase );
  643. //
  644. // Don't bother with constant entries.
  645. //
  646. if (FlagOn( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY )) {
  647. continue;
  648. }
  649. //
  650. // Now check whether we have a name match.
  651. // We exit the loop if we have a match.
  652. //
  653. if (CdIsNameInExpression( IrpContext,
  654. &Dirent->CdCaseFileName,
  655. Name,
  656. 0,
  657. TRUE )) {
  658. *MatchingName = &Dirent->CdCaseFileName;
  659. Found = TRUE;
  660. break;
  661. }
  662. //
  663. // The names didn't match. If the input name is a possible short
  664. // name and we are at the correct offset in the directory then
  665. // check if the short names match.
  666. //
  667. if (((Dirent->DirentOffset >> SHORT_NAME_SHIFT) == ShortNameDirentOffset) &&
  668. (Name->VersionString.Length == 0) &&
  669. !CdIs8dot3Name( IrpContext,
  670. Dirent->CdFileName.FileName )) {
  671. //
  672. // Create the short name and check for a match.
  673. //
  674. CdGenerate8dot3Name( IrpContext,
  675. &Dirent->CdCaseFileName.FileName,
  676. Dirent->DirentOffset,
  677. FileContext->ShortName.FileName.Buffer,
  678. &FileContext->ShortName.FileName.Length );
  679. //
  680. // Now check whether we have a name match.
  681. // We exit the loop if we have a match.
  682. //
  683. if (CdIsNameInExpression( IrpContext,
  684. &FileContext->ShortName,
  685. Name,
  686. 0,
  687. FALSE )) {
  688. *MatchingName = &FileContext->ShortName,
  689. Found = TRUE;
  690. break;
  691. }
  692. }
  693. }
  694. //
  695. // Go to the next initial dirent for a file.
  696. //
  697. } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext ));
  698. //
  699. // If we find the file then collect all of the dirents.
  700. //
  701. if (Found) {
  702. CdLookupLastFileDirent( IrpContext, Fcb, FileContext );
  703. }
  704. return Found;
  705. }
  706. BOOLEAN
  707. CdFindDirectory (
  708. IN PIRP_CONTEXT IrpContext,
  709. IN PFCB Fcb,
  710. IN PCD_NAME Name,
  711. IN BOOLEAN IgnoreCase,
  712. IN OUT PFILE_ENUM_CONTEXT FileContext
  713. )
  714. /*++
  715. Routine Description:
  716. This routine is called to search a dirctory for a directory matching the input
  717. name. This name has been upcased at this point if this a case-insensitive
  718. search. We look for an exact match in the name and do not look for shortname
  719. equivalents.
  720. Arguments:
  721. Fcb - Fcb for the directory being scanned.
  722. Name - Name to search for.
  723. IgnoreCase - Indicates the case of the search.
  724. FileContext - File context to use for the search. This has already been
  725. initialized.
  726. Return Value:
  727. BOOLEAN - TRUE if matching entry is found, FALSE otherwise.
  728. --*/
  729. {
  730. PDIRENT Dirent;
  731. BOOLEAN Found = FALSE;
  732. PAGED_CODE();
  733. //
  734. // Make sure there is a stream file for this Fcb.
  735. //
  736. if (Fcb->FileObject == NULL) {
  737. CdCreateInternalStream( IrpContext, Fcb->Vcb, Fcb );
  738. }
  739. //
  740. // Position ourselves at the first entry.
  741. //
  742. CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset );
  743. //
  744. // Loop while there are more entries in this directory.
  745. //
  746. do {
  747. Dirent = &FileContext->InitialDirent->Dirent;
  748. //
  749. // We only look for directories. Directories cannot have the
  750. // associated bit set.
  751. //
  752. if (FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) {
  753. //
  754. // Update the name in the current dirent.
  755. //
  756. CdUpdateDirentName( IrpContext, Dirent, IgnoreCase );
  757. //
  758. // Don't bother with constant entries.
  759. //
  760. if (FlagOn( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY )) {
  761. continue;
  762. }
  763. //
  764. // Now check whether we have a name match.
  765. // We exit the loop if we have a match.
  766. //
  767. if (CdIsNameInExpression( IrpContext,
  768. &Dirent->CdCaseFileName,
  769. Name,
  770. 0,
  771. TRUE )) {
  772. Found = TRUE;
  773. break;
  774. }
  775. }
  776. //
  777. // Go to the next initial dirent.
  778. //
  779. } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext ));
  780. return Found;
  781. }
  782. BOOLEAN
  783. CdFindFileByShortName (
  784. IN PIRP_CONTEXT IrpContext,
  785. IN PFCB Fcb,
  786. IN PCD_NAME Name,
  787. IN BOOLEAN IgnoreCase,
  788. IN ULONG ShortNameDirentOffset,
  789. IN OUT PFILE_ENUM_CONTEXT FileContext
  790. )
  791. /*++
  792. Routine Description:
  793. This routine is called to find the file name entry whose short name
  794. is defined by the input DirentOffset. The dirent offset here is
  795. multiplied by 32 and we look for the dirent begins in this 32 byte offset in
  796. directory. The minimum dirent length is 34 so we are guaranteed that only
  797. one dirent can begin in each 32 byte block in the directory.
  798. Arguments:
  799. Fcb - Fcb for the directory being scanned.
  800. Name - Name we are trying to match. We know this contains the tilde
  801. character followed by decimal characters.
  802. IgnoreCase - Indicates whether we need to upcase the long name and
  803. generated short name.
  804. ShortNameDirentOffset - This is the shifted value for the offset of the
  805. name in the directory.
  806. FileContext - This is the initialized file context to use for the search.
  807. Return Value:
  808. BOOLEAN - TRUE if a matching name was found, FALSE otherwise.
  809. --*/
  810. {
  811. BOOLEAN Found = FALSE;
  812. PDIRENT Dirent;
  813. ULONG ThisShortNameDirentOffset;
  814. PAGED_CODE();
  815. //
  816. // Make sure there is a stream file for this Fcb.
  817. //
  818. if (Fcb->FileObject == NULL) {
  819. CdCreateInternalStream( IrpContext, Fcb->Vcb, Fcb );
  820. }
  821. //
  822. // Position ourselves at the start of the directory and update
  823. //
  824. //
  825. CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset );
  826. //
  827. // Loop until we have found the entry or are beyond this dirent.
  828. //
  829. do {
  830. //
  831. // Compute the short name dirent offset for the current dirent.
  832. //
  833. Dirent = &FileContext->InitialDirent->Dirent;
  834. ThisShortNameDirentOffset = Dirent->DirentOffset >> SHORT_NAME_SHIFT;
  835. //
  836. // If beyond the target then exit.
  837. //
  838. if (ThisShortNameDirentOffset > ShortNameDirentOffset) {
  839. break;
  840. }
  841. //
  842. // If equal to the target then check if we have a name match.
  843. // We will either match or fail here.
  844. //
  845. if (ThisShortNameDirentOffset == ShortNameDirentOffset) {
  846. //
  847. // If this is an associated file then get out.
  848. //
  849. if (FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_ASSOC )) {
  850. break;
  851. }
  852. //
  853. // Update the name in the dirent and check if it is not
  854. // an 8.3 name.
  855. //
  856. CdUpdateDirentName( IrpContext, Dirent, IgnoreCase );
  857. if (CdIs8dot3Name( IrpContext,
  858. Dirent->CdFileName.FileName )) {
  859. break;
  860. }
  861. //
  862. // Generate the 8.3 name see if it matches our input name.
  863. //
  864. CdGenerate8dot3Name( IrpContext,
  865. &Dirent->CdCaseFileName.FileName,
  866. Dirent->DirentOffset,
  867. FileContext->ShortName.FileName.Buffer,
  868. &FileContext->ShortName.FileName.Length );
  869. //
  870. // Check if this name matches.
  871. //
  872. if (CdIsNameInExpression( IrpContext,
  873. Name,
  874. &FileContext->ShortName,
  875. 0,
  876. FALSE )) {
  877. //
  878. // Let our caller know we found an entry.
  879. //
  880. Found = TRUE;
  881. }
  882. //
  883. // Break out of the loop.
  884. //
  885. break;
  886. }
  887. //
  888. // Continue until there are no more entries.
  889. //
  890. } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext ));
  891. //
  892. // If we find the file then collect all of the dirents.
  893. //
  894. if (Found) {
  895. CdLookupLastFileDirent( IrpContext, Fcb, FileContext );
  896. }
  897. return Found;
  898. }
  899. BOOLEAN
  900. CdLookupNextInitialFileDirent (
  901. IN PIRP_CONTEXT IrpContext,
  902. IN PFCB Fcb,
  903. IN OUT PFILE_ENUM_CONTEXT FileContext
  904. )
  905. /*++
  906. Routine Description:
  907. This routine is called to walk through the directory until we find the
  908. first possible dirent for file. We are positioned at some point described
  909. by the FileContext. We will walk through any remaing dirents for the
  910. current file until we find the first dirent for some subsequent file.
  911. We can be called when we have found just one dirent for a file or all
  912. of them. We first check the CurrentDirContext. In the typical
  913. single-extent case this is unused. Then we look to the InitialDirContext
  914. which must be initialized.
  915. This routine will save the initial DirContext to the PriorDirContext and
  916. clean up any existing DirContext for the Prior or Current positions in
  917. the enumeration context.
  918. Arguments:
  919. Fcb - This is the directory to scan.
  920. FileContext - This is the file enumeration context. It is currently pointing
  921. at some file in the directory.
  922. Return Value:
  923. --*/
  924. {
  925. PRAW_DIRENT RawDirent;
  926. PDIRENT_ENUM_CONTEXT CurrentDirContext;
  927. PDIRENT_ENUM_CONTEXT TargetDirContext;
  928. PCOMPOUND_DIRENT TempDirent;
  929. BOOLEAN FoundDirent = FALSE;
  930. BOOLEAN FoundLastDirent;
  931. PAGED_CODE();
  932. //
  933. // Start by saving the initial dirent of the current file as the
  934. // previous file.
  935. //
  936. TempDirent = FileContext->PriorDirent;
  937. FileContext->PriorDirent = FileContext->InitialDirent;
  938. FileContext->InitialDirent = TempDirent;
  939. //
  940. // We will use the initial dirent of the prior file unless the
  941. // previous search returned multiple extents.
  942. //
  943. CurrentDirContext = &FileContext->PriorDirent->DirContext;
  944. if (FlagOn( FileContext->Flags, FILE_CONTEXT_MULTIPLE_DIRENTS )) {
  945. CurrentDirContext = &FileContext->CurrentDirent->DirContext;
  946. }
  947. //
  948. // Clear all of the flags and file size for the next file.
  949. //
  950. FileContext->Flags = 0;
  951. FileContext->FileSize = 0;
  952. FileContext->ShortName.FileName.Length = 0;
  953. //
  954. // We always want to store the result into the updated initial dirent
  955. // context.
  956. //
  957. TargetDirContext = &FileContext->InitialDirent->DirContext;
  958. //
  959. // Loop until we find the first dirent after the last dirent of the
  960. // current file. We may not be at the last dirent for the current file yet
  961. // so we may walk forward looking for the last and then find the
  962. // initial dirent for the next file after that.
  963. //
  964. while (TRUE) {
  965. //
  966. // Remember if the last dirent we visited was the last dirent for
  967. // a file.
  968. //
  969. RawDirent = CdRawDirent( IrpContext, CurrentDirContext );
  970. FoundLastDirent = !FlagOn( CdRawDirentFlags( IrpContext, RawDirent ), CD_ATTRIBUTE_MULTI );
  971. //
  972. // Try to find another dirent.
  973. //
  974. FoundDirent = CdLookupNextDirent( IrpContext,
  975. Fcb,
  976. CurrentDirContext,
  977. TargetDirContext );
  978. //
  979. // Exit the loop if no entry found.
  980. //
  981. if (!FoundDirent) {
  982. break;
  983. }
  984. //
  985. // Update the in-memory dirent.
  986. //
  987. CdUpdateDirentFromRawDirent( IrpContext,
  988. Fcb,
  989. TargetDirContext,
  990. &FileContext->InitialDirent->Dirent );
  991. //
  992. // Exit the loop if we had the end for the previous file.
  993. //
  994. if (FoundLastDirent) {
  995. break;
  996. }
  997. //
  998. // Always use a single dirent from this point on.
  999. //
  1000. CurrentDirContext = TargetDirContext;
  1001. }
  1002. return FoundDirent;
  1003. }
  1004. VOID
  1005. CdLookupLastFileDirent (
  1006. IN PIRP_CONTEXT IrpContext,
  1007. IN PFCB Fcb,
  1008. IN PFILE_ENUM_CONTEXT FileContext
  1009. )
  1010. /*++
  1011. Routine Description:
  1012. This routine is called when we've found the matching initial dirent for
  1013. a file. Now we want to find all of the dirents for a file as well as
  1014. compute the running total for the file size.
  1015. We also go out to the system use area and check whether this is an
  1016. XA sector. In that case we will compute the real file size.
  1017. The dirent in the initial compound dirent has been updated from the
  1018. raw dirent when this routine is called.
  1019. Arguments:
  1020. Fcb - Directory containing the entries for the file.
  1021. FileContext - Enumeration context for this search. It currently points
  1022. to the first dirent of the file and the in-memory dirent has been
  1023. updated.
  1024. Return Value:
  1025. None. This routine may raise STATUS_FILE_CORRUPT.
  1026. --*/
  1027. {
  1028. XA_EXTENT_TYPE ExtentType;
  1029. PCOMPOUND_DIRENT CurrentCompoundDirent;
  1030. PDIRENT CurrentDirent;
  1031. BOOLEAN FirstPass = TRUE;
  1032. BOOLEAN FoundDirent;
  1033. PAGED_CODE();
  1034. //
  1035. // The current dirent to look at is the initial dirent for the file.
  1036. //
  1037. CurrentCompoundDirent = FileContext->InitialDirent;
  1038. //
  1039. // Loop until we reach the last dirent for the file.
  1040. //
  1041. while (TRUE) {
  1042. CurrentDirent = &CurrentCompoundDirent->Dirent;
  1043. //
  1044. // Check if this extent has XA sectors.
  1045. //
  1046. if ((CurrentDirent->SystemUseOffset != 0) &&
  1047. FlagOn( Fcb->Vcb->VcbState, VCB_STATE_CDXA ) &&
  1048. CdCheckForXAExtent( IrpContext,
  1049. CdRawDirent( IrpContext, &CurrentCompoundDirent->DirContext ),
  1050. CurrentDirent )) {
  1051. //
  1052. // Any previous dirent must describe XA sectors as well.
  1053. //
  1054. if (!FirstPass && (ExtentType != CurrentDirent->ExtentType)) {
  1055. CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  1056. }
  1057. //
  1058. // If there are XA sectors then the data on the disk must
  1059. // be correctly aligned on sectors and be an integral number of
  1060. // sectors. Only an issue if the logical block size is not
  1061. // 2048.
  1062. //
  1063. if (Fcb->Vcb->BlockSize != SECTOR_SIZE) {
  1064. //
  1065. // We will do the following checks.
  1066. //
  1067. // Data must start on a sector boundary.
  1068. // Data length must be integral number of sectors.
  1069. //
  1070. if ((SectorBlockOffset( Fcb->Vcb, CurrentDirent->StartingOffset ) != 0) ||
  1071. (SectorBlockOffset( Fcb->Vcb, CurrentDirent->DataLength ) != 0)) {
  1072. CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  1073. }
  1074. //
  1075. // If interleaved then both the file unit and interleave
  1076. // gap must be integral number of sectors.
  1077. //
  1078. if ((CurrentDirent->FileUnitSize != 0) &&
  1079. ((SectorBlockOffset( Fcb->Vcb, CurrentDirent->FileUnitSize ) != 0) ||
  1080. (SectorBlockOffset( Fcb->Vcb, CurrentDirent->InterleaveGapSize ) != 0))) {
  1081. CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  1082. }
  1083. }
  1084. //
  1085. // If this is the first dirent then add the bytes for the RIFF
  1086. // header.
  1087. //
  1088. if (FirstPass) {
  1089. FileContext->FileSize = sizeof( RIFF_HEADER );
  1090. }
  1091. //
  1092. // Add the size of the mode2-form2 sector for each sector
  1093. // we have here.
  1094. //
  1095. FileContext->FileSize += Int32x32To64( CurrentDirent->DataLength >> SECTOR_SHIFT,
  1096. XA_SECTOR_SIZE);
  1097. } else {
  1098. //
  1099. // This extent does not have XA sectors. Any previous dirent
  1100. // better not have XA sectors.
  1101. //
  1102. if (!FirstPass && (ExtentType != CurrentDirent->ExtentType)) {
  1103. CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  1104. }
  1105. //
  1106. // Add these bytes to the file size.
  1107. //
  1108. FileContext->FileSize += CurrentDirent->DataLength;
  1109. }
  1110. //
  1111. // If we are at the last dirent then exit.
  1112. //
  1113. if (!FlagOn( CurrentDirent->DirentFlags, CD_ATTRIBUTE_MULTI )) {
  1114. break;
  1115. }
  1116. //
  1117. // Remember the extent type of the current extent.
  1118. //
  1119. ExtentType = CurrentDirent->ExtentType;
  1120. //
  1121. // Look for the next dirent of the file.
  1122. //
  1123. FoundDirent = CdLookupNextDirent( IrpContext,
  1124. Fcb,
  1125. &CurrentCompoundDirent->DirContext,
  1126. &FileContext->CurrentDirent->DirContext );
  1127. //
  1128. // If we didn't find the entry then this is a corrupt directory.
  1129. //
  1130. if (!FoundDirent) {
  1131. CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  1132. }
  1133. //
  1134. // Remember the dirent we just found.
  1135. //
  1136. CurrentCompoundDirent = FileContext->CurrentDirent;
  1137. FirstPass = FALSE;
  1138. //
  1139. // Look up all of the dirent information for the given dirent.
  1140. //
  1141. CdUpdateDirentFromRawDirent( IrpContext,
  1142. Fcb,
  1143. &CurrentCompoundDirent->DirContext,
  1144. &CurrentCompoundDirent->Dirent );
  1145. //
  1146. // Set flag to show there were multiple extents.
  1147. //
  1148. SetFlag( FileContext->Flags, FILE_CONTEXT_MULTIPLE_DIRENTS );
  1149. }
  1150. return;
  1151. }
  1152. VOID
  1153. CdCleanupFileContext (
  1154. IN PIRP_CONTEXT IrpContext,
  1155. IN PFILE_ENUM_CONTEXT FileContext
  1156. )
  1157. /*++
  1158. Routine Description:
  1159. This routine is called to cleanup the enumeration context for a file
  1160. search in a directory. We will unpin any remaining Bcbs and free
  1161. any allocated buffers.
  1162. Arguments:
  1163. FileContext - Enumeration context for the file search.
  1164. Return Value:
  1165. None.
  1166. --*/
  1167. {
  1168. PCOMPOUND_DIRENT CurrentCompoundDirent;
  1169. ULONG Count = 2;
  1170. PAGED_CODE();
  1171. //
  1172. // Cleanup the individual compound dirents.
  1173. //
  1174. do {
  1175. CurrentCompoundDirent = &FileContext->Dirents[ Count ];
  1176. CdCleanupDirContext( IrpContext, &CurrentCompoundDirent->DirContext );
  1177. CdCleanupDirent( IrpContext, &CurrentCompoundDirent->Dirent );
  1178. } while (Count--);
  1179. return;
  1180. }
  1181. //
  1182. // Local support routine
  1183. //
  1184. ULONG
  1185. CdCheckRawDirentBounds (
  1186. IN PIRP_CONTEXT IrpContext,
  1187. IN PDIRENT_ENUM_CONTEXT DirContext
  1188. )
  1189. /*++
  1190. Routine Description:
  1191. This routine takes a Dirent enumeration context and computes the offset
  1192. to the next dirent. A non-zero value indicates the offset within this
  1193. sector. A zero value indicates to move to the next sector. If the
  1194. current dirent does not fit within the sector then we will raise
  1195. STATUS_CORRUPT.
  1196. Arguments:
  1197. DirContext - Enumeration context indicating the current position in
  1198. the sector.
  1199. Return Value:
  1200. ULONG - Offset to the next dirent in this sector or zero if the
  1201. next dirent is in the next sector.
  1202. This routine will raise on a dirent which does not fit into the
  1203. described data buffer.
  1204. --*/
  1205. {
  1206. ULONG NextDirentOffset;
  1207. PRAW_DIRENT RawDirent;
  1208. PAGED_CODE();
  1209. //
  1210. // We should always have at least a byte still available in the
  1211. // current buffer.
  1212. //
  1213. ASSERT( (DirContext->DataLength - DirContext->SectorOffset) >= 1 );
  1214. //
  1215. // Get a pointer to the current dirent.
  1216. //
  1217. RawDirent = CdRawDirent( IrpContext, DirContext );
  1218. //
  1219. // If the dirent length is non-zero then look at the current dirent.
  1220. //
  1221. if (RawDirent->DirLen != 0) {
  1222. //
  1223. // Check the following bound for the dirent length.
  1224. //
  1225. // - Fits in the available bytes in the sector.
  1226. // - Is at least the minimal dirent size.
  1227. // - Is large enough to hold the file name.
  1228. //
  1229. if ((RawDirent->DirLen > (DirContext->DataLength - DirContext->SectorOffset)) ||
  1230. (RawDirent->DirLen < MIN_RAW_DIRENT_LEN) ||
  1231. (RawDirent->DirLen < (MIN_RAW_DIRENT_LEN - 1 + RawDirent->FileIdLen))) {
  1232. CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  1233. }
  1234. //
  1235. // Copy the dirent length field.
  1236. //
  1237. NextDirentOffset = RawDirent->DirLen;
  1238. //
  1239. // If we are exactly at the next sector then tell our caller by
  1240. // returning zero.
  1241. //
  1242. if (NextDirentOffset == (DirContext->DataLength - DirContext->SectorOffset)) {
  1243. NextDirentOffset = 0;
  1244. }
  1245. } else {
  1246. NextDirentOffset = 0;
  1247. }
  1248. return NextDirentOffset;
  1249. }
  1250. //
  1251. // Local support routine
  1252. //
  1253. XA_EXTENT_TYPE
  1254. CdCheckForXAExtent (
  1255. IN PIRP_CONTEXT IrpContext,
  1256. IN PRAW_DIRENT RawDirent,
  1257. IN OUT PDIRENT Dirent
  1258. )
  1259. /*++
  1260. Routine Description:
  1261. This routine is called to scan through the system use area to test if
  1262. the current dirent has the XA bit set. The bit in the in-memory
  1263. dirent will be set as appropriate.
  1264. Arguments:
  1265. RawDirent - Pointer to the on-disk dirent.
  1266. Dirent - Pointer to the in-memory dirent. We will update this with the
  1267. appropriate XA flag.
  1268. Return Value:
  1269. XA_EXTENT_TYPE - Type of physical extent for this on disk dirent.
  1270. --*/
  1271. {
  1272. XA_EXTENT_TYPE ExtentType = Form1Data;
  1273. PSYSTEM_USE_XA SystemUseArea;
  1274. PAGED_CODE();
  1275. //
  1276. // Check if there is enough space for the XA system use area.
  1277. //
  1278. if (Dirent->DirentLength - Dirent->SystemUseOffset >= sizeof( SYSTEM_USE_XA )) {
  1279. SystemUseArea = Add2Ptr( RawDirent, Dirent->SystemUseOffset, PSYSTEM_USE_XA );
  1280. //
  1281. // Check for a valid signature.
  1282. //
  1283. if (SystemUseArea->Signature == SYSTEM_XA_SIGNATURE) {
  1284. //
  1285. // Check for an audio track.
  1286. //
  1287. if (FlagOn( SystemUseArea->Attributes, SYSTEM_USE_XA_DA )) {
  1288. ExtentType = CDAudio;
  1289. } else if (FlagOn( SystemUseArea->Attributes, SYSTEM_USE_XA_FORM2 )) {
  1290. //
  1291. // Check for XA data. Note that a number of discs (video CDs)
  1292. // have files marked as type XA Mode 2 Form 1 (2048 bytes of
  1293. // user data), but actually record these sectors as Mode2 Form 2
  1294. // (2352). We will fail to read these files, since for M2F1,
  1295. // a normal read CD command is issued (as per SCSI specs).
  1296. //
  1297. ExtentType = Mode2Form2Data;
  1298. }
  1299. Dirent->XAAttributes = SystemUseArea->Attributes;
  1300. Dirent->XAFileNumber = SystemUseArea->FileNumber;
  1301. }
  1302. }
  1303. Dirent->ExtentType = ExtentType;
  1304. return ExtentType;
  1305. }