1
0

file.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801
  1. /*++
  2. Copyright (c) 2014 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. file.c
  5. Abstract:
  6. This module implements file system support for the boot library.
  7. Author:
  8. Evan Green 19-Feb-2014
  9. Environment:
  10. Boot
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include <minoca/kernel/kernel.h>
  16. #include <minoca/lib/fat/fat.h>
  17. #include "firmware.h"
  18. #include "bootlibp.h"
  19. //
  20. // ---------------------------------------------------------------- Definitions
  21. //
  22. //
  23. // ------------------------------------------------------ Data Type Definitions
  24. //
  25. //
  26. // ----------------------------------------------- Internal Function Prototypes
  27. //
  28. KSTATUS
  29. BopOpenVolume (
  30. HANDLE DiskHandle,
  31. PBOOT_VOLUME *VolumeHandle
  32. );
  33. //
  34. // -------------------------------------------------------------------- Globals
  35. //
  36. //
  37. // ------------------------------------------------------------------ Functions
  38. //
  39. KSTATUS
  40. BoOpenBootVolume (
  41. ULONG BootDriveNumber,
  42. ULONGLONG PartitionOffset,
  43. PBOOT_ENTRY BootEntry,
  44. PBOOT_VOLUME *VolumeHandle
  45. )
  46. /*++
  47. Routine Description:
  48. This routine opens a handle to the boot volume device, which is the device
  49. this boot application was loaded from.
  50. Arguments:
  51. BootDriveNumber - Supplies the drive number of the boot device, for PC/AT
  52. systems.
  53. PartitionOffset - Supplies the offset in sectors to the start of the boot
  54. partition, for PC/AT systems.
  55. BootEntry - Supplies an optional pointer to the boot entry, for EFI systems.
  56. VolumeHandle - Supplies a pointer where a handle to the open volume will
  57. be returned on success.
  58. Return Value:
  59. Status code.
  60. --*/
  61. {
  62. HANDLE DiskHandle;
  63. KSTATUS Status;
  64. *VolumeHandle = NULL;
  65. DiskHandle = NULL;
  66. Status = FwOpenBootDisk(BootDriveNumber,
  67. PartitionOffset,
  68. BootEntry,
  69. &DiskHandle);
  70. if (!KSUCCESS(Status)) {
  71. goto OpenBootVolumeEnd;
  72. }
  73. Status = BopOpenVolume(DiskHandle, VolumeHandle);
  74. if (!KSUCCESS(Status)) {
  75. goto OpenBootVolumeEnd;
  76. }
  77. OpenBootVolumeEnd:
  78. if (!KSUCCESS(Status)) {
  79. if (DiskHandle != NULL) {
  80. FwCloseDisk(DiskHandle);
  81. }
  82. }
  83. return Status;
  84. }
  85. KSTATUS
  86. BoCloseVolume (
  87. PBOOT_VOLUME VolumeHandle
  88. )
  89. /*++
  90. Routine Description:
  91. This routine closes a disk handle.
  92. Arguments:
  93. VolumeHandle - Supplies the volume handle returned when the volume was
  94. opened.
  95. Return Value:
  96. Status code.
  97. --*/
  98. {
  99. KSTATUS Status;
  100. Status = FatUnmount(VolumeHandle->FileSystemHandle);
  101. if (!KSUCCESS(Status)) {
  102. return Status;
  103. }
  104. FwCloseDisk(VolumeHandle->DiskHandle);
  105. BoFreeMemory(VolumeHandle);
  106. return STATUS_SUCCESS;
  107. }
  108. KSTATUS
  109. BoOpenVolume (
  110. UCHAR PartitionId[FIRMWARE_PARTITION_ID_SIZE],
  111. PBOOT_VOLUME *Volume
  112. )
  113. /*++
  114. Routine Description:
  115. This routine closes a disk handle.
  116. Arguments:
  117. PartitionId - Supplies the ID of the partition to open.
  118. Volume - Supplies a pointer where a handle to the open volume will
  119. be returned on success.
  120. Return Value:
  121. Status code.
  122. --*/
  123. {
  124. HANDLE DiskHandle;
  125. KSTATUS Status;
  126. *Volume = NULL;
  127. DiskHandle = NULL;
  128. Status = FwOpenPartition(PartitionId, &DiskHandle);
  129. if (!KSUCCESS(Status)) {
  130. goto OpenVolumeEnd;
  131. }
  132. Status = BopOpenVolume(DiskHandle, Volume);
  133. if (!KSUCCESS(Status)) {
  134. goto OpenVolumeEnd;
  135. }
  136. OpenVolumeEnd:
  137. if (!KSUCCESS(Status)) {
  138. if (DiskHandle != NULL) {
  139. FwCloseDisk(DiskHandle);
  140. }
  141. }
  142. return Status;
  143. }
  144. KSTATUS
  145. BoLookupPath (
  146. PBOOT_VOLUME Volume,
  147. PFILE_ID StartingDirectory,
  148. PSTR Path,
  149. PFILE_PROPERTIES FileProperties
  150. )
  151. /*++
  152. Routine Description:
  153. This routine attempts to look up the given file path.
  154. Arguments:
  155. Volume - Supplies a pointer to the volume token.
  156. StartingDirectory - Supplies an optional pointer to a file ID containing
  157. the directory to start path traversal from. If NULL, path lookup will
  158. start with the root of the volume.
  159. Path - Supplies a pointer to the path string to look up.
  160. FileProperties - Supplies a pointer where the properties for the file will
  161. be returned on success.
  162. Return Value:
  163. STATUS_SUCCESS on success.
  164. STATUS_PATH_NOT_FOUND if the given file path does not exist.
  165. Other error codes on other failures.
  166. --*/
  167. {
  168. ULONG ComponentSize;
  169. PSTR CurrentComponent;
  170. FILE_ID DirectoryId;
  171. PSTR NextComponent;
  172. PSTR PathCopy;
  173. ULONG PathLength;
  174. KSTATUS Status;
  175. PathCopy = NULL;
  176. PathLength = RtlStringLength(Path);
  177. if (PathLength == 0) {
  178. Status = STATUS_INVALID_PARAMETER;
  179. goto LookupPathEnd;
  180. }
  181. PathLength += 1;
  182. PathCopy = BoAllocateMemory(PathLength);
  183. if (PathCopy == NULL) {
  184. Status = STATUS_INSUFFICIENT_RESOURCES;
  185. goto LookupPathEnd;
  186. }
  187. RtlCopyMemory(PathCopy, Path, PathLength);
  188. //
  189. // Start with the root directory.
  190. //
  191. if (StartingDirectory != NULL) {
  192. DirectoryId = *StartingDirectory;
  193. RtlZeroMemory(FileProperties, sizeof(FILE_PROPERTIES));
  194. } else {
  195. Status = FatLookup(Volume->FileSystemHandle,
  196. TRUE,
  197. 0,
  198. NULL,
  199. 0,
  200. FileProperties);
  201. if (!KSUCCESS(Status)) {
  202. goto LookupPathEnd;
  203. }
  204. DirectoryId = FileProperties->FileId;
  205. }
  206. //
  207. // Remove any leading slashes.
  208. //
  209. CurrentComponent = PathCopy;
  210. while (*CurrentComponent == '/') {
  211. CurrentComponent += 1;
  212. PathLength -= 1;
  213. }
  214. //
  215. // Loop looking up directory entries.
  216. //
  217. while (TRUE) {
  218. NextComponent = RtlStringFindCharacter(CurrentComponent,
  219. '/',
  220. PathLength);
  221. if (NextComponent != NULL) {
  222. *NextComponent = '\0';
  223. NextComponent += 1;
  224. ComponentSize = (UINTN)NextComponent - (UINTN)CurrentComponent;
  225. } else {
  226. ComponentSize = PathLength;
  227. }
  228. if (ComponentSize > 1) {
  229. Status = FatLookup(Volume->FileSystemHandle,
  230. FALSE,
  231. DirectoryId,
  232. CurrentComponent,
  233. ComponentSize,
  234. FileProperties);
  235. if (!KSUCCESS(Status)) {
  236. goto LookupPathEnd;
  237. }
  238. }
  239. if (NextComponent == NULL) {
  240. break;
  241. }
  242. DirectoryId = FileProperties->FileId;
  243. PathLength -= ComponentSize;
  244. CurrentComponent = NextComponent;
  245. }
  246. LookupPathEnd:
  247. if (PathCopy != NULL) {
  248. BoFreeMemory(PathCopy);
  249. }
  250. return Status;
  251. }
  252. KSTATUS
  253. BoLoadFile (
  254. PBOOT_VOLUME Volume,
  255. PFILE_ID Directory,
  256. PSTR FileName,
  257. PVOID *FilePhysical,
  258. PUINTN FileSize,
  259. PULONGLONG ModificationDate
  260. )
  261. /*++
  262. Routine Description:
  263. This routine loads a file from disk into memory.
  264. Arguments:
  265. Volume - Supplies a pointer to the mounted volume to read the file from.
  266. Directory - Supplies an optional pointer to the ID of the directory to
  267. start path traversal from. If NULL, the root of the volume will be used.
  268. FileName - Supplies the name of the file to load.
  269. FilePhysical - Supplies a pointer where the file buffer's physical address
  270. will be returned. This routine will allocate the buffer to hold the
  271. file. If this parameter is NULL, the status code will reflect whether
  272. or not the file could be opened, but the file contents will not be
  273. loaded.
  274. FileSize - Supplies a pointer where the size of the file in bytes will be
  275. returned.
  276. ModificationDate - Supplies an optional pointer where the modification
  277. date of the file will be returned on success.
  278. Return Value:
  279. Status code.
  280. --*/
  281. {
  282. UINTN AlignedSize;
  283. UINTN BytesRead;
  284. FAT_SEEK_INFORMATION FatSeekInformation;
  285. PVOID File;
  286. FILE_PROPERTIES FileProperties;
  287. PFAT_IO_BUFFER IoBuffer;
  288. ULONGLONG LocalFileSize;
  289. ULONG PageSize;
  290. PVOID PhysicalBuffer;
  291. KSTATUS Status;
  292. File = NULL;
  293. IoBuffer = NULL;
  294. LocalFileSize = 0;
  295. PhysicalBuffer = NULL;
  296. PageSize = MmPageSize();
  297. Status = BoLookupPath(Volume, Directory, FileName, &FileProperties);
  298. if (!KSUCCESS(Status)) {
  299. goto LoadFileEnd;
  300. }
  301. Status = FatOpenFileId(Volume->FileSystemHandle,
  302. FileProperties.FileId,
  303. IO_ACCESS_READ,
  304. 0,
  305. &File);
  306. if (!KSUCCESS(Status)) {
  307. goto LoadFileEnd;
  308. }
  309. READ_INT64_SYNC(&(FileProperties.FileSize), &LocalFileSize);
  310. ASSERT((UINTN)LocalFileSize == LocalFileSize);
  311. //
  312. // If the caller doesn't actually want the data, the work is done.
  313. //
  314. if (FilePhysical == NULL) {
  315. Status = STATUS_SUCCESS;
  316. goto LoadFileEnd;
  317. }
  318. //
  319. // Round the file size up to the nearest page.
  320. //
  321. AlignedSize = ALIGN_RANGE_UP(LocalFileSize + 1, PageSize);
  322. PhysicalBuffer = BoAllocateMemory(AlignedSize);
  323. if (PhysicalBuffer == NULL) {
  324. Status = STATUS_INSUFFICIENT_RESOURCES;
  325. goto LoadFileEnd;
  326. }
  327. IoBuffer = FatCreateIoBuffer(PhysicalBuffer, AlignedSize);
  328. if (IoBuffer == NULL) {
  329. Status = STATUS_INSUFFICIENT_RESOURCES;
  330. goto LoadFileEnd;
  331. }
  332. RtlZeroMemory(&FatSeekInformation, sizeof(FAT_SEEK_INFORMATION));
  333. Status = FatReadFile(File,
  334. &FatSeekInformation,
  335. IoBuffer,
  336. (UINTN)LocalFileSize,
  337. 0,
  338. NULL,
  339. &BytesRead);
  340. if (!KSUCCESS(Status)) {
  341. goto LoadFileEnd;
  342. }
  343. if (BytesRead != LocalFileSize) {
  344. Status = STATUS_FILE_CORRUPT;
  345. goto LoadFileEnd;
  346. }
  347. //
  348. // NULL terminate the file just in case someone tries to read off the end
  349. // of it.
  350. //
  351. *((PUCHAR)PhysicalBuffer + BytesRead) = '\0';
  352. LoadFileEnd:
  353. if (FilePhysical != NULL) {
  354. *FilePhysical = PhysicalBuffer;
  355. }
  356. if (FileSize != NULL) {
  357. *FileSize = LocalFileSize;
  358. }
  359. if (ModificationDate != NULL) {
  360. *ModificationDate = FileProperties.ModifiedTime.Seconds;
  361. }
  362. if (IoBuffer != NULL) {
  363. FatFreeIoBuffer(IoBuffer);
  364. }
  365. if (File != NULL) {
  366. FatCloseFile(File);
  367. }
  368. return Status;
  369. }
  370. KSTATUS
  371. BoStoreFile (
  372. PBOOT_VOLUME Volume,
  373. FILE_ID Directory,
  374. PSTR FileName,
  375. ULONG FileNameLength,
  376. PVOID FilePhysical,
  377. UINTN FileSize,
  378. ULONGLONG ModificationDate
  379. )
  380. /*++
  381. Routine Description:
  382. This routine stores a file buffer to disk.
  383. Arguments:
  384. Volume - Supplies a pointer to the mounted volume to read the file from.
  385. Directory - Supplies the file ID of the directory the file resides in.
  386. FileName - Supplies the name of the file to store.
  387. FileNameLength - Supplies the length of the file name buffer in bytes,
  388. including the null terminator.
  389. FilePhysical - Supplies a pointer to the buffer containing the file
  390. contents.
  391. FileSize - Supplies the size of the file buffer in bytes. The file will be
  392. truncated to this size if it previously existed and was larger.
  393. ModificationDate - Supplies the modification date to set.
  394. Return Value:
  395. Status code.
  396. --*/
  397. {
  398. UINTN BytesWritten;
  399. ULONG DesiredAccess;
  400. FILE_PROPERTIES DirectoryProperties;
  401. ULONGLONG DirectorySize;
  402. FAT_SEEK_INFORMATION FatSeekInformation;
  403. PVOID File;
  404. FILE_PROPERTIES FileProperties;
  405. PFAT_IO_BUFFER IoBuffer;
  406. ULONGLONG NewDirectorySize;
  407. KSTATUS Status;
  408. File = NULL;
  409. IoBuffer = NULL;
  410. RtlZeroMemory(&FileProperties, sizeof(FILE_PROPERTIES));
  411. WRITE_INT64_SYNC(&(FileProperties.FileSize), 0);
  412. ASSERT(Directory != 0);
  413. //
  414. // Load the file into memory.
  415. //
  416. DesiredAccess = IO_ACCESS_WRITE;
  417. Status = FatLookup(Volume->FileSystemHandle,
  418. FALSE,
  419. Directory,
  420. FileName,
  421. FileNameLength,
  422. &FileProperties);
  423. if (KSUCCESS(Status)) {
  424. Status = FatDeleteFileBlocks(Volume->FileSystemHandle,
  425. NULL,
  426. FileProperties.FileId,
  427. 0,
  428. TRUE);
  429. if (!KSUCCESS(Status)) {
  430. goto StoreFileEnd;
  431. }
  432. //
  433. // The file did not exist before. Create it.
  434. //
  435. } else if (Status == STATUS_PATH_NOT_FOUND) {
  436. //
  437. // Look up the directory.
  438. //
  439. RtlZeroMemory(&DirectoryProperties, sizeof(FILE_PROPERTIES));
  440. Status = FatLookup(Volume->FileSystemHandle,
  441. FALSE,
  442. Directory,
  443. ".",
  444. sizeof("."),
  445. &DirectoryProperties);
  446. if (!KSUCCESS(Status)) {
  447. goto StoreFileEnd;
  448. }
  449. FileProperties.Type = IoObjectRegularFile;
  450. FileProperties.Permissions = FILE_PERMISSION_USER_READ |
  451. FILE_PERMISSION_USER_WRITE |
  452. FILE_PERMISSION_GROUP_READ |
  453. FILE_PERMISSION_GROUP_WRITE |
  454. FILE_PERMISSION_OTHER_READ;
  455. FatGetCurrentSystemTime(&(FileProperties.StatusChangeTime));
  456. Status = FatCreate(Volume->FileSystemHandle,
  457. Directory,
  458. FileName,
  459. FileNameLength,
  460. &NewDirectorySize,
  461. &FileProperties);
  462. READ_INT64_SYNC(&(DirectoryProperties.FileSize),
  463. &DirectorySize);
  464. if (NewDirectorySize > DirectorySize) {
  465. WRITE_INT64_SYNC(&(DirectoryProperties.FileSize),
  466. NewDirectorySize);
  467. Status = FatWriteFileProperties(Volume->FileSystemHandle,
  468. &DirectoryProperties,
  469. 0);
  470. if (!KSUCCESS(Status)) {
  471. goto StoreFileEnd;
  472. }
  473. }
  474. //
  475. // Some other error occurred, bail out.
  476. //
  477. } else if (!KSUCCESS(Status)) {
  478. goto StoreFileEnd;
  479. }
  480. //
  481. // Open up the now-empty file.
  482. //
  483. Status = FatOpenFileId(Volume->FileSystemHandle,
  484. FileProperties.FileId,
  485. DesiredAccess,
  486. 0,
  487. &File);
  488. if (!KSUCCESS(Status)) {
  489. goto StoreFileEnd;
  490. }
  491. //
  492. // Create an I/O buffer and write the data out.
  493. //
  494. IoBuffer = FatCreateIoBuffer(FilePhysical, FileSize);
  495. if (IoBuffer == NULL) {
  496. Status = STATUS_INSUFFICIENT_RESOURCES;
  497. goto StoreFileEnd;
  498. }
  499. RtlZeroMemory(&FatSeekInformation, sizeof(FAT_SEEK_INFORMATION));
  500. Status = FatWriteFile(File,
  501. &FatSeekInformation,
  502. IoBuffer,
  503. FileSize,
  504. 0,
  505. NULL,
  506. &BytesWritten);
  507. if (!KSUCCESS(Status)) {
  508. goto StoreFileEnd;
  509. }
  510. if (BytesWritten != FileSize) {
  511. Status = STATUS_FILE_CORRUPT;
  512. goto StoreFileEnd;
  513. }
  514. //
  515. // Update the metadata.
  516. //
  517. WRITE_INT64_SYNC(&(FileProperties.FileSize), FileSize);
  518. FileProperties.ModifiedTime.Seconds = ModificationDate;
  519. FileProperties.AccessTime.Seconds = ModificationDate;
  520. Status = FatWriteFileProperties(Volume->FileSystemHandle,
  521. &FileProperties,
  522. 0);
  523. if (!KSUCCESS(Status)) {
  524. goto StoreFileEnd;
  525. }
  526. StoreFileEnd:
  527. if (IoBuffer != NULL) {
  528. FatFreeIoBuffer(IoBuffer);
  529. }
  530. if (File != NULL) {
  531. FatCloseFile(File);
  532. }
  533. return Status;
  534. }
  535. //
  536. // --------------------------------------------------------- Internal Functions
  537. //
  538. KSTATUS
  539. BopOpenVolume (
  540. HANDLE DiskHandle,
  541. PBOOT_VOLUME *VolumeHandle
  542. )
  543. /*++
  544. Routine Description:
  545. This routine mounts a volume on an open disk handle and creates a volume
  546. handle representing that connection.
  547. Arguments:
  548. DiskHandle - Supplies the open disk or partition handle from the firmware.
  549. PartitionOffset - Supplies the offset in sectors to the start of the
  550. partition.
  551. VolumeHandle - Supplies a pointer where a handle to the open volume will
  552. be returned on success.
  553. Return Value:
  554. Status code.
  555. --*/
  556. {
  557. PBOOT_VOLUME BootVolume;
  558. KSTATUS Status;
  559. BootVolume = BoAllocateMemory(sizeof(BOOT_VOLUME));
  560. if (BootVolume == NULL) {
  561. Status = STATUS_INSUFFICIENT_RESOURCES;
  562. goto OpenVolumeEnd;
  563. }
  564. RtlZeroMemory(BootVolume, sizeof(BOOT_VOLUME));
  565. BootVolume->DiskHandle = DiskHandle;
  566. //
  567. // Attempt to mount the device.
  568. //
  569. BootVolume->Parameters.DeviceToken = BootVolume;
  570. BootVolume->Parameters.BlockSize = FwGetDiskSectorSize(DiskHandle);
  571. BootVolume->Parameters.BlockCount = FwGetDiskSectorCount(DiskHandle);
  572. Status = FatMount(&(BootVolume->Parameters),
  573. 0,
  574. &(BootVolume->FileSystemHandle));
  575. if (!KSUCCESS(Status)) {
  576. goto OpenVolumeEnd;
  577. }
  578. OpenVolumeEnd:
  579. if (!KSUCCESS(Status)) {
  580. if (BootVolume != NULL) {
  581. ASSERT(BootVolume->FileSystemHandle == NULL);
  582. BoFreeMemory(BootVolume);
  583. BootVolume = NULL;
  584. }
  585. }
  586. *VolumeHandle = BootVolume;
  587. return Status;
  588. }