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.

1749 lines
38 KiB

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