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.

1706 lines
37 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. CdfsBoot.c
  5. Abstract:
  6. This module implements the Cdfs boot file system used by the operating
  7. system loader.
  8. Author:
  9. Brian Andrew [BrianAn] 05-Aug-1991
  10. Revision History:
  11. --*/
  12. #include "bootlib.h"
  13. #include "cd.h"
  14. #include "blcache.h"
  15. BOOTFS_INFO CdfsBootFsInfo = {L"cdfs"};
  16. //
  17. // Local procedure prototypes.
  18. //
  19. ARC_STATUS
  20. CdfsReadDisk(
  21. IN ULONG DeviceId,
  22. IN ULONG Lbo,
  23. IN ULONG ByteCount,
  24. IN OUT PVOID Buffer,
  25. IN BOOLEAN CacheNewData
  26. );
  27. VOID
  28. CdfsFirstComponent(
  29. IN OUT PSTRING String,
  30. OUT PSTRING FirstComponent
  31. );
  32. typedef enum _COMPARISON_RESULTS {
  33. LessThan = -1,
  34. EqualTo = 0,
  35. GreaterThan = 1
  36. } COMPARISON_RESULTS;
  37. COMPARISON_RESULTS
  38. CdfsCompareNames(
  39. IN PSTRING Name1,
  40. IN PSTRING Name2
  41. );
  42. ARC_STATUS
  43. CdfsSearchDirectory(
  44. IN PSTRING Name,
  45. OUT PBOOLEAN IsDirectory
  46. );
  47. VOID
  48. CdfsGetDirectoryInfo(
  49. IN PRAW_DIR_REC DirEntry,
  50. IN BOOLEAN IsoVol,
  51. OUT PULONG SectorOffset,
  52. OUT PULONG DiskOffset,
  53. OUT PULONG Length
  54. );
  55. COMPARISON_RESULTS
  56. CdfsFileMatch(
  57. IN PRAW_DIR_REC DirEntry,
  58. IN PSTRING FileName
  59. );
  60. typedef union _USHORT2 {
  61. USHORT Ushort[2];
  62. ULONG ForceAlignment;
  63. } USHORT2, *PUSHORT2;
  64. //
  65. // This macro copies an unaligned src longword to an aligned dsr longword
  66. // accessing the source on a word boundary.
  67. //
  68. #define CopyUshort2(Dst,Src) { \
  69. ((PUSHORT2)(Dst))->Ushort[0] = ((UNALIGNED USHORT2 *)(Src))->Ushort[0]; \
  70. ((PUSHORT2)(Dst))->Ushort[1] = ((UNALIGNED USHORT2 *)(Src))->Ushort[1]; \
  71. }
  72. //
  73. // The following macro upcases a single ascii character
  74. //
  75. #define ToUpper(C) ((((C) >= 'a') && ((C) <= 'z')) ? (C) - 'a' + 'A' : (C))
  76. #define SetFlag(Flags,SingleFlag) { (Flags) |= (SingleFlag); }
  77. //
  78. // The following macro indicate if the flag is on or off
  79. //
  80. #define FlagOn(Flags,SingleFlag) ((BOOLEAN)( \
  81. (((Flags) & (SingleFlag)) != 0 ? TRUE : FALSE) \
  82. ) \
  83. )
  84. //
  85. // Define global data.
  86. //
  87. // Context Pointer - This is a pointer to the context for the current file
  88. // operation that is active.
  89. //
  90. PCDFS_STRUCTURE_CONTEXT CdfsStructureContext;
  91. //
  92. // File Descriptor - This is a pointer to the file descriptor for the current
  93. // file operation that is active.
  94. //
  95. PBL_FILE_TABLE CdfsFileTableEntry;
  96. //
  97. // File entry table - This is a structure that provides entry to the Cdfs
  98. // file system procedures. It is exported when a Cdfs file structure
  99. // is recognized.
  100. //
  101. BL_DEVICE_ENTRY_TABLE CdfsDeviceEntryTable;
  102. PBL_DEVICE_ENTRY_TABLE
  103. IsCdfsFileStructure (
  104. IN ULONG DeviceId,
  105. IN PVOID StructureContext
  106. )
  107. /*++
  108. Routine Description:
  109. This routine determines if the partition on the specified channel
  110. contains a Cdfs file system volume.
  111. Arguments:
  112. DeviceId - Supplies the file table index for the device on which
  113. read operations are to be performed.
  114. StructureContext - Supplies a pointer to a Hpfs file structure context.
  115. Return Value:
  116. A pointer to the Cdfs entry table is returned if the partition is
  117. recognized as containing a Cdfs volume. Otherwise, NULL is returned.
  118. --*/
  119. {
  120. UCHAR UnalignedSector[CD_SECTOR_SIZE + 256];
  121. PRAW_ISO_VD RawVd;
  122. PRAW_DIR_REC RootDe;
  123. UCHAR DescType;
  124. UCHAR Version;
  125. BOOLEAN IsoVol;
  126. BOOLEAN HsgVol;
  127. STRING IsoVolId;
  128. STRING HsgVolId;
  129. STRING DiskId;
  130. ULONG DiskOffset;
  131. //
  132. // Capture in our global variable the Cdfs Structure context record
  133. //
  134. CdfsStructureContext = (PCDFS_STRUCTURE_CONTEXT)StructureContext;
  135. RtlZeroMemory((PVOID)CdfsStructureContext, sizeof(CDFS_STRUCTURE_CONTEXT));
  136. //
  137. // Compute the properly aligned buffer for reading in cdrom
  138. // sectors.
  139. //
  140. RawVd = ALIGN_BUFFER( UnalignedSector );
  141. //
  142. // Initialize the string Id's to match.
  143. //
  144. RtlInitString( &IsoVolId, ISO_VOL_ID );
  145. RtlInitString( &HsgVolId, HSG_VOL_ID );
  146. DiskId.Length = 5;
  147. DiskId.MaximumLength = 5;
  148. //
  149. // We initially start at the first volume descriptor.
  150. //
  151. DiskOffset = FIRST_VD_SECTOR * CD_SECTOR_SIZE;
  152. //
  153. // We loop, reading in volume descriptors until we find either
  154. // a primary, terminator or a sector that cannot contain either.
  155. //
  156. while (TRUE) {
  157. //
  158. // Initialize the Hsg boolean.
  159. //
  160. HsgVol = FALSE;
  161. //
  162. // Read the sector at our current position. Return NULL on an
  163. // error
  164. //
  165. if (CdfsReadDisk( DeviceId,
  166. DiskOffset,
  167. CD_SECTOR_SIZE,
  168. RawVd,
  169. CACHE_NEW_DATA) != ESUCCESS) {
  170. return NULL;
  171. }
  172. //
  173. // Compare the Id string in the volume descriptor with the Iso
  174. // and Hsg values.
  175. //
  176. DiskId.Buffer = RVD_STD_ID( RawVd, TRUE );
  177. IsoVol = (BOOLEAN)(CdfsCompareNames( &DiskId, &IsoVolId ) == EqualTo);
  178. if (!IsoVol) {
  179. //
  180. // Repeat the test with the Hsg Id string.
  181. //
  182. DiskId.Buffer = RVD_STD_ID( RawVd, FALSE );
  183. HsgVol = (BOOLEAN)(CdfsCompareNames( &DiskId, &HsgVolId ) == EqualTo);
  184. //
  185. // If neither, then return NULL.
  186. //
  187. if (!HsgVol) {
  188. return NULL;
  189. }
  190. }
  191. //
  192. // Get the volume descriptor type and standard version number.
  193. //
  194. DescType = RVD_DESC_TYPE( RawVd, IsoVol );
  195. Version = RVD_VERSION( RawVd, IsoVol );
  196. //
  197. // Return NULL, if the version is incorrect or this is a terminal
  198. // volume descriptor.
  199. //
  200. if (Version != VERSION_1
  201. || DescType == VD_TERMINATOR) {
  202. return NULL;
  203. }
  204. //
  205. // If this is a primary volume descriptor, then our search is over.
  206. //
  207. if (DescType == VD_PRIMARY) {
  208. //
  209. // Update the fields of the Cdfs context structure that apply
  210. // to the volume.
  211. //
  212. CdfsStructureContext->IsIsoVol = IsoVol;
  213. CdfsStructureContext->LbnBlockSize = RVD_LB_SIZE( RawVd, IsoVol );
  214. CdfsStructureContext->LogicalBlockCount = RVD_VOL_SIZE( RawVd, IsoVol );
  215. //
  216. // Get the information on the root directory and save it in
  217. // the context structure.
  218. //
  219. RootDe = (PRAW_DIR_REC) (RVD_ROOT_DE( RawVd, IsoVol ));
  220. CdfsGetDirectoryInfo( RootDe,
  221. IsoVol,
  222. &CdfsStructureContext->RootDirSectorOffset,
  223. &CdfsStructureContext->RootDirDiskOffset,
  224. &CdfsStructureContext->RootDirSize );
  225. //
  226. // Exit the loop.
  227. //
  228. break;
  229. }
  230. //
  231. // Otherwise move to the next sector.
  232. //
  233. DiskOffset += CD_SECTOR_SIZE;
  234. }
  235. //
  236. // Initialize the file entry table.
  237. //
  238. CdfsDeviceEntryTable.Open = CdfsOpen;
  239. CdfsDeviceEntryTable.Close = CdfsClose;
  240. CdfsDeviceEntryTable.Read = CdfsRead;
  241. CdfsDeviceEntryTable.Seek = CdfsSeek;
  242. CdfsDeviceEntryTable.Write = CdfsWrite;
  243. CdfsDeviceEntryTable.GetFileInformation = CdfsGetFileInformation;
  244. CdfsDeviceEntryTable.SetFileInformation = CdfsSetFileInformation;
  245. CdfsDeviceEntryTable.BootFsInfo = &CdfsBootFsInfo;
  246. //
  247. // And return the address of the table to our caller.
  248. //
  249. return &CdfsDeviceEntryTable;
  250. }
  251. ARC_STATUS
  252. CdfsClose (
  253. IN ULONG FileId
  254. )
  255. /*++
  256. Routine Description:
  257. This routine closes the file specified by the file id.
  258. Arguments:
  259. FileId - Supplies the file table index.
  260. Return Value:
  261. ESUCCESS if returned as the function value.
  262. --*/
  263. {
  264. //
  265. // Indicate that the file isn't open any longer
  266. //
  267. BlFileTable[FileId].Flags.Open = 0;
  268. //
  269. // And return to our caller
  270. //
  271. return ESUCCESS;
  272. }
  273. ARC_STATUS
  274. CdfsOpen (
  275. IN CHAR * FIRMWARE_PTR FileName,
  276. IN OPEN_MODE OpenMode,
  277. IN ULONG * FIRMWARE_PTR FileId
  278. )
  279. /*++
  280. Routine Description:
  281. This routine searches the root directory for a file matching FileName.
  282. If a match is found the dirent for the file is saved and the file is
  283. opened.
  284. Arguments:
  285. FileName - Supplies a pointer to a zero terminated file name.
  286. OpenMode - Supplies the mode of the open.
  287. FileId - Supplies a pointer to a variable that specifies the file
  288. table entry that is to be filled in if the open is successful.
  289. Return Value:
  290. ESUCCESS is returned if the open operation is successful. Otherwise,
  291. an unsuccessful status is returned that describes the reason for failure.
  292. --*/
  293. {
  294. ARC_STATUS Status;
  295. ULONG DeviceId;
  296. STRING PathName;
  297. STRING Name;
  298. BOOLEAN IsDirectory;
  299. BOOLEAN SearchSucceeded;
  300. //
  301. // Save the address of the file table entry, context area, and the device
  302. // id in use.
  303. //
  304. CdfsFileTableEntry = &BlFileTable[*FileId];
  305. CdfsStructureContext = (PCDFS_STRUCTURE_CONTEXT)CdfsFileTableEntry->StructureContext;
  306. DeviceId = CdfsFileTableEntry->DeviceId;
  307. //
  308. // Construct a file name descriptor from the input file name.
  309. //
  310. RtlInitString( &PathName, FileName );
  311. //
  312. // Set the starting directory to be the root directory.
  313. //
  314. CdfsStructureContext->DirSectorOffset = CdfsStructureContext->RootDirSectorOffset;
  315. CdfsStructureContext->DirDiskOffset = CdfsStructureContext->RootDirDiskOffset;
  316. CdfsStructureContext->DirSize = CdfsStructureContext->RootDirSize;
  317. //
  318. // While the path name has some characters in it we'll go through our
  319. // loop which extracts the first part of the path name and searches
  320. // the current fnode (which must be a directory) for an the entry.
  321. // If what we find is a directory then we have a new directory fnode
  322. // and simply continue back to the top of the loop.
  323. //
  324. IsDirectory = TRUE;
  325. SearchSucceeded = TRUE;
  326. while (PathName.Length > 0
  327. && IsDirectory) {
  328. //
  329. // Extract the first component.
  330. //
  331. CdfsFirstComponent( &PathName, &Name );
  332. //
  333. // Copy the name into the filename buffer.
  334. //
  335. CdfsFileTableEntry->FileNameLength = (UCHAR) Name.Length;
  336. RtlMoveMemory( CdfsFileTableEntry->FileName,
  337. Name.Buffer,
  338. Name.Length );
  339. //
  340. // Look to see if the file exists.
  341. //
  342. Status = CdfsSearchDirectory( &Name,
  343. &IsDirectory );
  344. if (Status == ENOENT) {
  345. SearchSucceeded = FALSE;
  346. break;
  347. }
  348. if (Status != ESUCCESS) {
  349. return Status;
  350. }
  351. }
  352. //
  353. // If the path name length is not zero then we were trying to crack a path
  354. // with an nonexistent (or non directory) name in it. For example, we tried
  355. // to crack a\b\c\d and b is not a directory or does not exist (then the path
  356. // name will still contain c\d).
  357. //
  358. if (PathName.Length != 0) {
  359. return ENOTDIR;
  360. }
  361. //
  362. // At this point we've cracked the name up to (an maybe including the last
  363. // component). We located the last component if the SearchSucceeded flag is
  364. // true, otherwise the last component does not exist. If we located the last
  365. // component then this is like an open or a supersede, but not a create.
  366. //
  367. if (SearchSucceeded) {
  368. //
  369. // Check if the last component is a directory
  370. //
  371. if (IsDirectory) {
  372. //
  373. // For an existing directory the only valid open mode is OpenDirectory
  374. // all other modes return an error
  375. //
  376. switch (OpenMode) {
  377. case ArcOpenReadOnly:
  378. case ArcOpenWriteOnly:
  379. case ArcOpenReadWrite:
  380. case ArcCreateWriteOnly:
  381. case ArcCreateReadWrite:
  382. case ArcSupersedeWriteOnly:
  383. case ArcSupersedeReadWrite:
  384. //
  385. // If we reach here then the caller got a directory but didn't
  386. // want to open a directory
  387. //
  388. return EISDIR;
  389. case ArcOpenDirectory:
  390. //
  391. // If we reach here then the caller got a directory and wanted
  392. // to open a directory.
  393. //
  394. CdfsFileTableEntry->u.CdfsFileContext.FileSize = CdfsStructureContext->DirSize;
  395. CdfsFileTableEntry->u.CdfsFileContext.DiskOffset = CdfsStructureContext->DirDiskOffset;
  396. CdfsFileTableEntry->u.CdfsFileContext.IsDirectory = TRUE;
  397. CdfsFileTableEntry->Flags.Open = 1;
  398. CdfsFileTableEntry->Flags.Read = 1;
  399. CdfsFileTableEntry->Position.LowPart = 0;
  400. CdfsFileTableEntry->Position.HighPart = 0;
  401. return ESUCCESS;
  402. case ArcCreateDirectory:
  403. //
  404. // If we reach here then the caller got a directory and wanted
  405. // to create a new directory
  406. //
  407. return EACCES;
  408. }
  409. }
  410. //
  411. // If we get there then we have an existing file that is being opened.
  412. // We can open existing files only read only.
  413. //
  414. switch (OpenMode) {
  415. case ArcOpenReadOnly:
  416. //
  417. // If we reach here then the user got a file and wanted to open the
  418. // file read only
  419. //
  420. CdfsFileTableEntry->u.CdfsFileContext.FileSize = CdfsStructureContext->DirSize;
  421. CdfsFileTableEntry->u.CdfsFileContext.DiskOffset = CdfsStructureContext->DirDiskOffset;
  422. CdfsFileTableEntry->u.CdfsFileContext.IsDirectory = FALSE;
  423. CdfsFileTableEntry->Flags.Open = 1;
  424. CdfsFileTableEntry->Flags.Read = 1;
  425. CdfsFileTableEntry->Position.LowPart = 0;
  426. CdfsFileTableEntry->Position.HighPart = 0;
  427. return ESUCCESS;
  428. case ArcOpenWriteOnly:
  429. case ArcOpenReadWrite:
  430. case ArcCreateWriteOnly:
  431. case ArcCreateReadWrite:
  432. case ArcSupersedeWriteOnly:
  433. case ArcSupersedeReadWrite:
  434. //
  435. // If we reach here then we are trying to open a read only
  436. // device for write.
  437. //
  438. return EROFS;
  439. case ArcOpenDirectory:
  440. case ArcCreateDirectory:
  441. //
  442. // If we reach here then the user got a file and wanted a directory
  443. //
  444. return ENOTDIR;
  445. }
  446. }
  447. //
  448. // If we get here the last component does not exist so we are trying to create
  449. // either a new file or a directory.
  450. //
  451. switch (OpenMode) {
  452. case ArcOpenReadOnly:
  453. case ArcOpenWriteOnly:
  454. case ArcOpenReadWrite:
  455. case ArcOpenDirectory:
  456. //
  457. // If we reach here then the user did not get a file but wanted a file
  458. //
  459. return ENOENT;
  460. case ArcCreateWriteOnly:
  461. case ArcSupersedeWriteOnly:
  462. case ArcCreateReadWrite:
  463. case ArcSupersedeReadWrite:
  464. case ArcCreateDirectory:
  465. //
  466. // If we get hre the user wants to create something.
  467. //
  468. return EROFS;
  469. }
  470. //
  471. // If we reach here then the path name is exhausted and we didn't
  472. // reach a file so return an error to our caller
  473. //
  474. return ENOENT;
  475. }
  476. ARC_STATUS
  477. CdfsRead (
  478. IN ULONG FileId,
  479. OUT VOID * FIRMWARE_PTR Buffer,
  480. IN ULONG Length,
  481. OUT ULONG * FIRMWARE_PTR Transfer
  482. )
  483. /*++
  484. Routine Description:
  485. This routine reads data from the specified file.
  486. Arguments:
  487. FileId - Supplies the file table index.
  488. Buffer - Supplies a pointer to the buffer that receives the data
  489. read.
  490. Length - Supplies the number of bytes that are to be read.
  491. Transfer - Supplies a pointer to a variable that receives the number
  492. of bytes actually transfered.
  493. Return Value:
  494. ESUCCESS is returned if the read operation is successful. Otherwise,
  495. an unsuccessful status is returned that describes the reason for failure.
  496. --*/
  497. {
  498. ARC_STATUS Status;
  499. ULONG DeviceId;
  500. ULONG DiskOffset;
  501. //
  502. // Save the address of the file table entry, context area, and the device
  503. // id in use.
  504. //
  505. CdfsFileTableEntry = &BlFileTable[FileId];
  506. CdfsStructureContext = (PCDFS_STRUCTURE_CONTEXT)CdfsFileTableEntry->StructureContext;
  507. DeviceId = CdfsFileTableEntry->DeviceId;
  508. //
  509. // Clear the transfer count and set the initial disk offset.
  510. //
  511. *Transfer = 0;
  512. //
  513. // Check for end of file.
  514. //
  515. //
  516. // If the file position is currently at the end of file, then return
  517. // a success status with no bytes read from the file. If the file
  518. // plus the length of the transfer is beyond the end of file, then
  519. // read only the remaining part of the file. Otherwise, read the
  520. // requested number of bytes.
  521. //
  522. if (CdfsFileTableEntry->Position.LowPart ==
  523. CdfsFileTableEntry->u.CdfsFileContext.FileSize) {
  524. return ESUCCESS;
  525. } else {
  526. if ((CdfsFileTableEntry->Position.LowPart + Length) >=
  527. CdfsFileTableEntry->u.CdfsFileContext.FileSize) {
  528. Length = CdfsFileTableEntry->u.CdfsFileContext.FileSize -
  529. CdfsFileTableEntry->Position.LowPart;
  530. }
  531. }
  532. DiskOffset = CdfsFileTableEntry->Position.LowPart
  533. + CdfsFileTableEntry->u.CdfsFileContext.DiskOffset;
  534. //
  535. // Read in runs (i.e., sectors) until the byte count goes to zero
  536. //
  537. while (Length > 0) {
  538. ULONG CurrentRunByteCount;
  539. //
  540. // Compute the current read byte count.
  541. //
  542. if (Length > MAX_CDROM_READ) {
  543. CurrentRunByteCount = MAX_CDROM_READ;
  544. } else {
  545. CurrentRunByteCount = Length;
  546. }
  547. //
  548. // Read from the disk.
  549. //
  550. if ((Status = CdfsReadDisk( DeviceId,
  551. DiskOffset,
  552. CurrentRunByteCount,
  553. Buffer,
  554. DONT_CACHE_NEW_DATA)) != ESUCCESS) {
  555. return Status;
  556. }
  557. //
  558. // Update the remaining length.
  559. //
  560. Length -= CurrentRunByteCount;
  561. //
  562. // Update the current position and the number of bytes transfered
  563. //
  564. CdfsFileTableEntry->Position.LowPart += CurrentRunByteCount;
  565. DiskOffset += CurrentRunByteCount;
  566. *Transfer += CurrentRunByteCount;
  567. //
  568. // Update buffer to point to the next byte location to fill in
  569. //
  570. Buffer = (PCHAR)Buffer + CurrentRunByteCount;
  571. }
  572. //
  573. // If we get here then remaining sector count is zero so we can
  574. // return success to our caller
  575. //
  576. return ESUCCESS;
  577. }
  578. ARC_STATUS
  579. CdfsSeek (
  580. IN ULONG FileId,
  581. IN LARGE_INTEGER * FIRMWARE_PTR Offset,
  582. IN SEEK_MODE SeekMode
  583. )
  584. /*++
  585. Routine Description:
  586. This routine seeks to the specified position for the file specified
  587. by the file id.
  588. Arguments:
  589. FileId - Supplies the file table index.
  590. Offset - Supplies the offset in the file to position to.
  591. SeekMode - Supplies the mode of the seek operation.
  592. Return Value:
  593. ESUCCESS if returned as the function value.
  594. --*/
  595. {
  596. ULONG NewPosition;
  597. //
  598. // Compute the new position
  599. //
  600. if (SeekMode == SeekAbsolute) {
  601. NewPosition = Offset->LowPart;
  602. } else {
  603. NewPosition = BlFileTable[FileId].Position.LowPart + Offset->LowPart;
  604. }
  605. //
  606. // If the new position is greater than the file size then return
  607. // an error
  608. //
  609. if (NewPosition > BlFileTable[FileId].u.CdfsFileContext.FileSize) {
  610. return EINVAL;
  611. }
  612. //
  613. // Otherwise set the new position and return to our caller
  614. //
  615. BlFileTable[FileId].Position.LowPart = NewPosition;
  616. return ESUCCESS;
  617. }
  618. ARC_STATUS
  619. CdfsWrite (
  620. IN ULONG FileId,
  621. IN VOID * FIRMWARE_PTR Buffer,
  622. IN ULONG Length,
  623. OUT ULONG * FIRMWARE_PTR Transfer
  624. )
  625. /*++
  626. Routine Description:
  627. This routine writes data to the specified file.
  628. Arguments:
  629. FileId - Supplies the file table index.
  630. Buffer - Supplies a pointer to the buffer that contains the data
  631. written.
  632. Length - Supplies the number of bytes that are to be written.
  633. Transfer - Supplies a pointer to a variable that receives the number
  634. of bytes actually transfered.
  635. Return Value:
  636. ESUCCESS is returned if the write operation is successful. Otherwise,
  637. an unsuccessful status is returned that describes the reason for failure.
  638. --*/
  639. {
  640. return EROFS;
  641. UNREFERENCED_PARAMETER( FileId );
  642. UNREFERENCED_PARAMETER( Buffer );
  643. UNREFERENCED_PARAMETER( Length );
  644. UNREFERENCED_PARAMETER( Transfer );
  645. }
  646. ARC_STATUS
  647. CdfsGetFileInformation (
  648. IN ULONG FileId,
  649. OUT FILE_INFORMATION * FIRMWARE_PTR Buffer
  650. )
  651. /*++
  652. Routine Description:
  653. This procedure returns to the user a buffer filled with file information
  654. Arguments:
  655. FileId - Supplies the File id for the operation
  656. Buffer - Supplies the buffer to receive the file information. Note that
  657. it must be large enough to hold the full file name
  658. Return Value:
  659. ESUCCESS is returned for all get information requests.
  660. --*/
  661. {
  662. PBL_FILE_TABLE FileTableEntry;
  663. ULONG i;
  664. //
  665. // Load our local variables
  666. //
  667. FileTableEntry = &BlFileTable[FileId];
  668. //
  669. // Zero out the buffer, and fill in its non-zero values
  670. //
  671. RtlZeroMemory(Buffer, sizeof(FILE_INFORMATION));
  672. Buffer->EndingAddress.LowPart = FileTableEntry->u.CdfsFileContext.FileSize;
  673. Buffer->CurrentPosition.LowPart = FileTableEntry->Position.LowPart;
  674. Buffer->CurrentPosition.HighPart = 0;
  675. SetFlag(Buffer->Attributes, ArcReadOnlyFile);
  676. if (FileTableEntry->u.CdfsFileContext.IsDirectory) {
  677. SetFlag( Buffer->Attributes, ArcDirectoryFile );
  678. }
  679. Buffer->FileNameLength = FileTableEntry->FileNameLength;
  680. for (i = 0; i < FileTableEntry->FileNameLength; i += 1) {
  681. Buffer->FileName[i] = FileTableEntry->FileName[i];
  682. }
  683. return ESUCCESS;
  684. }
  685. ARC_STATUS
  686. CdfsSetFileInformation (
  687. IN ULONG FileId,
  688. IN ULONG AttributeFlags,
  689. IN ULONG AttributeMask
  690. )
  691. /*++
  692. Routine Description:
  693. This routine sets the file attributes of the indicated file
  694. Arguments:
  695. FileId - Supplies the File Id for the operation
  696. AttributeFlags - Supplies the value (on or off) for each attribute being modified
  697. AttributeMask - Supplies a mask of the attributes being altered. All other
  698. file attributes are left alone.
  699. Return Value:
  700. EROFS is always returned in this case.
  701. --*/
  702. {
  703. return EROFS;
  704. UNREFERENCED_PARAMETER( FileId );
  705. UNREFERENCED_PARAMETER( AttributeFlags );
  706. UNREFERENCED_PARAMETER( AttributeMask );
  707. }
  708. ARC_STATUS
  709. CdfsInitialize (
  710. VOID
  711. )
  712. /*++
  713. Routine Description:
  714. This routine initializes the cdfs boot filesystem.
  715. Currently this is a no-op.
  716. Arguments:
  717. None.
  718. Return Value:
  719. ESUCCESS.
  720. --*/
  721. {
  722. return ESUCCESS;
  723. }
  724. //
  725. // Internal support routine
  726. //
  727. ARC_STATUS
  728. CdfsReadDisk(
  729. IN ULONG DeviceId,
  730. IN ULONG Lbo,
  731. IN ULONG ByteCount,
  732. IN OUT PVOID Buffer,
  733. IN BOOLEAN CacheNewData
  734. )
  735. /*++
  736. Routine Description:
  737. This routine reads in zero or more sectors from the specified device.
  738. Arguments:
  739. DeviceId - Supplies the device id to use in the arc calls.
  740. Lbo - Supplies the LBO to start reading from.
  741. ByteCount - Supplies the number of bytes to read.
  742. Buffer - Supplies a pointer to the buffer to read the bytes into.
  743. Return Value:
  744. ESUCCESS is returned if the read operation is successful. Otherwise,
  745. an unsuccessful status is returned that describes the reason for failure.
  746. --*/
  747. {
  748. LARGE_INTEGER LargeLbo;
  749. ARC_STATUS Status;
  750. ULONG i;
  751. //
  752. // Special case the zero byte read request
  753. //
  754. if (ByteCount == 0) {
  755. return ESUCCESS;
  756. }
  757. //
  758. // Issue the read through the cache.
  759. //
  760. LargeLbo.QuadPart = Lbo;
  761. Status = BlDiskCacheRead(DeviceId,
  762. &LargeLbo,
  763. Buffer,
  764. ByteCount,
  765. &i,
  766. CacheNewData);
  767. if (Status != ESUCCESS) {
  768. return Status;
  769. }
  770. //
  771. // Make sure we got back the amount requested
  772. //
  773. if (ByteCount != i) {
  774. return EIO;
  775. }
  776. //
  777. // Everything is fine so return success to our caller
  778. //
  779. return ESUCCESS;
  780. }
  781. //
  782. // Internal support routine
  783. //
  784. VOID
  785. CdfsFirstComponent(
  786. IN OUT PSTRING String,
  787. OUT PSTRING FirstComponent
  788. )
  789. /*++
  790. Routine Description:
  791. This routine takes an input path name and separates it into its
  792. first file name component and the remaining part.
  793. Arguments:
  794. String - Supplies the original string being dissected. On return
  795. this string will now point to the remaining part.
  796. FirstComponent - Returns the string representing the first file name
  797. in the input string.
  798. Return Value:
  799. None.
  800. --*/
  801. {
  802. ULONG Index;
  803. //
  804. // Copy over the string variable into the first component variable
  805. //
  806. *FirstComponent = *String;
  807. //
  808. // Now if the first character in the name is a backslash then
  809. // simply skip over the backslash.
  810. //
  811. if (FirstComponent->Buffer[0] == '\\') {
  812. FirstComponent->Buffer += 1;
  813. FirstComponent->Length -= 1;
  814. }
  815. //
  816. // Now search the name for a backslash
  817. //
  818. for (Index = 0; Index < FirstComponent->Length; Index += 1) {
  819. if (FirstComponent->Buffer[Index] == '\\') {
  820. break;
  821. }
  822. }
  823. //
  824. // At this point Index denotes a backslash or is equal to the length
  825. // of the string. So update string to be the remaining part.
  826. // Decrement the length of the first component by the approprate
  827. // amount
  828. //
  829. String->Buffer = &FirstComponent->Buffer[Index];
  830. String->Length = (SHORT)(FirstComponent->Length - Index);
  831. FirstComponent->Length = (SHORT)Index;
  832. //
  833. // And return to our caller.
  834. //
  835. return;
  836. }
  837. //
  838. // Internal support routine
  839. //
  840. COMPARISON_RESULTS
  841. CdfsCompareNames(
  842. IN PSTRING Name1,
  843. IN PSTRING Name2
  844. )
  845. /*++
  846. Routine Description:
  847. This routine takes two names and compare them ignoring case. This
  848. routine does not do implied dot or dbcs processing.
  849. Arguments:
  850. Name1 - Supplies the first name to compare
  851. Name2 - Supplies the second name to compare
  852. Return Value:
  853. LessThan if Name1 is lexically less than Name2
  854. EqualTo if Name1 is lexically equal to Name2
  855. GreaterThan if Name1 is lexically greater than Name2
  856. --*/
  857. {
  858. ULONG i;
  859. ULONG MinimumLength;
  860. //
  861. // Compute the smallest of the two name lengths
  862. //
  863. MinimumLength = (Name1->Length < Name2->Length ? Name1->Length : Name2->Length);
  864. //
  865. // Now compare each character in the names.
  866. //
  867. for (i = 0; i < MinimumLength; i += 1) {
  868. if (ToUpper(Name1->Buffer[i]) < ToUpper(Name2->Buffer[i])) {
  869. return LessThan;
  870. }
  871. if (ToUpper(Name1->Buffer[i]) > ToUpper(Name2->Buffer[i])) {
  872. return GreaterThan;
  873. }
  874. }
  875. //
  876. // The names compared equal up to the smallest name length so
  877. // now check the name lengths
  878. //
  879. if (Name1->Length < Name2->Length) {
  880. return LessThan;
  881. }
  882. if (Name1->Length > Name2->Length) {
  883. return GreaterThan;
  884. }
  885. return EqualTo;
  886. }
  887. //
  888. // Internal support routine.
  889. //
  890. ARC_STATUS
  891. CdfsSearchDirectory(
  892. IN PSTRING Name,
  893. OUT PBOOLEAN IsDirectory
  894. )
  895. /*++
  896. Routine Description:
  897. This routine walks through the current directory in the Cdfs
  898. context structure, looking for a match for 'Name'. We will find
  899. the first non-multi-extent, non-interleave file. We will ignore
  900. any version number for the file. The details about the file, if
  901. found, are stored in the Cdfs context structure.
  902. Arguments:
  903. Name - This is the name of the file to search for.
  904. IsDirectory - Supplies the address of a boolean where we store
  905. whether this is or is not a directory.
  906. Return Value:
  907. ESUCCESS is returned if the operation is successful. Otherwise,
  908. an unsuccessful status is returned that describes the reason for failure.
  909. --*/
  910. {
  911. ARC_STATUS Status;
  912. ULONG SectorOffset;
  913. ULONG SectorDiskOffset;
  914. ULONG DirentOffset;
  915. ULONG RemainingBytes;
  916. BOOLEAN ReadSector;
  917. BOOLEAN SearchForMultiEnd;
  918. UCHAR UnalignedBuffer[CD_SECTOR_SIZE + 256];
  919. PUCHAR RawSector;
  920. PRAW_DIR_REC RawDe;
  921. COMPARISON_RESULTS ComparisonResult;
  922. //
  923. // Initialize the local variables.
  924. //
  925. RawSector = ALIGN_BUFFER( UnalignedBuffer );
  926. SearchForMultiEnd = FALSE;
  927. //
  928. // Remember where we are within the disk, sector and directory file.
  929. //
  930. SectorOffset = CdfsStructureContext->DirSectorOffset;
  931. SectorDiskOffset = CdfsStructureContext->DirDiskOffset - SectorOffset;
  932. DirentOffset = 0;
  933. ReadSector = FALSE;
  934. //
  935. // If this is the root directory, then we can return immediately.
  936. //
  937. if (Name->Length == 1
  938. && *Name->Buffer == '\\') {
  939. *IsDirectory = TRUE;
  940. //
  941. // The structure context is already filled in.
  942. //
  943. return ESUCCESS;
  944. }
  945. //
  946. // Compute the remaining bytes in this sector.
  947. //
  948. RemainingBytes = CD_SECTOR_SIZE - SectorOffset;
  949. //
  950. // Loop until the directory is exhausted or a matching dirent for the
  951. // target name is found.
  952. //
  953. while (TRUE) {
  954. //
  955. // If the current offset is beyond the end of the directory,
  956. // raise an appropriate status.
  957. //
  958. if (DirentOffset >= CdfsStructureContext->DirSize) {
  959. return ENOENT;
  960. }
  961. //
  962. // If the remaining bytes in this sector is less than the
  963. // minimum needed for a dirent, then move to the next sector.
  964. //
  965. if (RemainingBytes < MIN_DIR_REC_SIZE) {
  966. SectorDiskOffset += CD_SECTOR_SIZE;
  967. DirentOffset += RemainingBytes;
  968. SectorOffset = 0;
  969. RemainingBytes = CD_SECTOR_SIZE;
  970. ReadSector = FALSE;
  971. continue;
  972. }
  973. //
  974. // If we have not read in the sector, do so now.
  975. //
  976. if (!ReadSector) {
  977. Status = CdfsReadDisk( CdfsFileTableEntry->DeviceId,
  978. SectorDiskOffset,
  979. CD_SECTOR_SIZE,
  980. RawSector,
  981. CACHE_NEW_DATA);
  982. if (Status != ESUCCESS) {
  983. return Status;
  984. }
  985. ReadSector = TRUE;
  986. }
  987. //
  988. // If the first byte of the next dirent is '\0', then we move to
  989. // the next sector.
  990. //
  991. if (*(RawSector + SectorOffset) == '\0') {
  992. SectorDiskOffset += CD_SECTOR_SIZE;
  993. DirentOffset += RemainingBytes;
  994. SectorOffset = 0;
  995. RemainingBytes = CD_SECTOR_SIZE;
  996. ReadSector = FALSE;
  997. continue;
  998. }
  999. RawDe = (PRAW_DIR_REC) ((PUCHAR) RawSector + SectorOffset);
  1000. //
  1001. // If the size of this dirent extends beyond the end of this sector
  1002. // we abort the search.
  1003. //
  1004. if ((ULONG)RawDe->DirLen > RemainingBytes) {
  1005. return EINVAL;
  1006. }
  1007. //
  1008. // We have correctly found the next dirent. We first check whether
  1009. // we are looking for the last dirent for a multi-extent.
  1010. //
  1011. if (SearchForMultiEnd) {
  1012. //
  1013. // If this is the last of a multi-extent we change our search
  1014. // state.
  1015. //
  1016. if (!FlagOn( DE_FILE_FLAGS( CdfsStructureContext->IsIsoVol, RawDe ),
  1017. ISO_ATTR_MULTI )) {
  1018. SearchForMultiEnd = TRUE;
  1019. }
  1020. //
  1021. // If this is a multi-extent dirent, we change our search state.
  1022. //
  1023. } else if (FlagOn( DE_FILE_FLAGS( CdfsStructureContext->IsIsoVol, RawDe ),
  1024. ISO_ATTR_MULTI )) {
  1025. SearchForMultiEnd = TRUE;
  1026. //
  1027. // If this is a file match, we update the Cdfs context structure
  1028. // and the 'IsDirectory' flag.
  1029. //
  1030. } else {
  1031. ComparisonResult = CdfsFileMatch( RawDe, Name );
  1032. if (ComparisonResult == EqualTo) {
  1033. CdfsGetDirectoryInfo( RawDe,
  1034. CdfsStructureContext->IsIsoVol,
  1035. &CdfsStructureContext->DirSectorOffset,
  1036. &CdfsStructureContext->DirDiskOffset,
  1037. &CdfsStructureContext->DirSize );
  1038. *IsDirectory = FlagOn( DE_FILE_FLAGS( CdfsStructureContext->IsIsoVol, RawDe ),
  1039. ISO_ATTR_DIRECTORY );
  1040. return ESUCCESS;
  1041. //
  1042. // If we have passed this file in the directory, then
  1043. // exit with the appropriate error code.
  1044. //
  1045. } else if (ComparisonResult == GreaterThan) {
  1046. return ENOENT;
  1047. }
  1048. }
  1049. //
  1050. // Otherwise we simply compute the next sector offset, disk offset
  1051. // and file offset.
  1052. //
  1053. SectorOffset += RawDe->DirLen;
  1054. DirentOffset += RawDe->DirLen;
  1055. RemainingBytes -= RawDe->DirLen;
  1056. }
  1057. return ESUCCESS;
  1058. }
  1059. //
  1060. // Internal support routine.
  1061. //
  1062. VOID
  1063. CdfsGetDirectoryInfo(
  1064. IN PRAW_DIR_REC DirEntry,
  1065. IN BOOLEAN IsoVol,
  1066. OUT PULONG SectorOffset,
  1067. OUT PULONG DiskOffset,
  1068. OUT PULONG Length
  1069. )
  1070. /*++
  1071. Routine Description:
  1072. This routine takes a pointer to a raw directory structure on the disk
  1073. and computes the file size, disk offset and file length for the
  1074. directory entry.
  1075. Arguments:
  1076. DirEntry - This points to raw data from the disk.
  1077. IsoVol - Boolean indicating that this is an ISO volume.
  1078. SectorOffset - This supplies the address to store the sector offset of the
  1079. start of the disk data.
  1080. DiskOffset - This supplies the address to store the disk offset of the
  1081. start of the disk data.
  1082. Length - This supplies the address to store the number of bytes in
  1083. the file referred by this disk directory.
  1084. Return Value:
  1085. None.
  1086. --*/
  1087. {
  1088. //
  1089. // The disk offset is length of the Xar blocks added to the starting
  1090. // location for the file.
  1091. //
  1092. CopyUshort2( DiskOffset, DirEntry->FileLoc );
  1093. *DiskOffset *= CdfsStructureContext->LbnBlockSize;
  1094. *DiskOffset += (DirEntry->XarLen * CdfsStructureContext->LbnBlockSize);
  1095. //
  1096. // The sector offset is the least significant bytes of the disk offset.
  1097. //
  1098. *SectorOffset = *DiskOffset & (CD_SECTOR_SIZE - 1);
  1099. //
  1100. // The file size is pulled straight from the dirent. We round it
  1101. // to a sector size to protect us from faulty disks if this is a
  1102. // directory. Otherwise we use it directly from the dirent.
  1103. //
  1104. CopyUshort2( Length, DirEntry->DataLen );
  1105. if (FlagOn( DE_FILE_FLAGS( IsoVol, DirEntry ), ISO_ATTR_DIRECTORY )) {
  1106. *Length += (*SectorOffset + CD_SECTOR_SIZE - 1);
  1107. *Length &= ~(CD_SECTOR_SIZE - 1);
  1108. *Length -= *SectorOffset;
  1109. }
  1110. return;
  1111. }
  1112. //
  1113. // Internal support routine.
  1114. //
  1115. COMPARISON_RESULTS
  1116. CdfsFileMatch(
  1117. IN PRAW_DIR_REC DirEntry,
  1118. IN PSTRING FileName
  1119. )
  1120. {
  1121. STRING DirentString;
  1122. ULONG Count;
  1123. PUCHAR StringPtr;
  1124. //
  1125. // We never match either '\0' or '\1'. We will return 'LessThan' in
  1126. // all of these cases.
  1127. //
  1128. if (DirEntry->FileIdLen == 1
  1129. && (DirEntry->FileId[0] == '\0'
  1130. || DirEntry->FileId[0] == '\1')) {
  1131. return LessThan;
  1132. }
  1133. //
  1134. // We assume that we can use the entire file name in the dirent.
  1135. //
  1136. DirentString.Length = DirEntry->FileIdLen;
  1137. DirentString.Buffer = DirEntry->FileId;
  1138. //
  1139. // We walk backwards through the dirent name to check for the
  1140. // existance of a ';' character. We then set the string length
  1141. // to this position.
  1142. //
  1143. StringPtr = DirentString.Buffer + DirentString.Length - 1;
  1144. Count = DirentString.Length;
  1145. while (Count--) {
  1146. if (*StringPtr == ';') {
  1147. DirentString.Length = (SHORT)Count;
  1148. break;
  1149. }
  1150. StringPtr--;
  1151. }
  1152. //
  1153. // We also check for a terminating '.' character and truncate it.
  1154. //
  1155. StringPtr = DirentString.Buffer + DirentString.Length - 1;
  1156. Count = DirentString.Length;
  1157. while (Count--) {
  1158. if (*StringPtr == '.') {
  1159. DirentString.Length = (SHORT)Count;
  1160. } else {
  1161. break;
  1162. }
  1163. StringPtr--;
  1164. }
  1165. //
  1166. // We now have the two filenames to compare. The result of this
  1167. // operation is simply the comparison of the two of them.
  1168. //
  1169. DirentString.MaximumLength = DirentString.Length;
  1170. return CdfsCompareNames( &DirentString, FileName );
  1171. }