SimpleFsRead.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. /** @file
  2. EFI_FILE_PROTOCOL.Read() member function for the Virtio Filesystem driver.
  3. Copyright (C) 2020, Red Hat, Inc.
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. **/
  6. #include <Library/BaseMemoryLib.h> // CopyMem()
  7. #include <Library/MemoryAllocationLib.h> // AllocatePool()
  8. #include "VirtioFsDxe.h"
  9. /**
  10. Populate a caller-allocated EFI_FILE_INFO object from
  11. VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE.
  12. @param[in] Dirent The entry read from the directory stream. The
  13. caller is responsible for ensuring that
  14. Dirent->Namelen describe valid storage.
  15. @param[in] SingleFileInfoSize The allocated size of FileInfo.
  16. @param[out] FileInfo The EFI_FILE_INFO object to populate. On
  17. success, all fields in FileInfo will be
  18. updated, setting FileInfo->Size to the
  19. actually used size (which will not exceed
  20. SingleFileInfoSize).
  21. @retval EFI_SUCCESS FileInfo has been filled in.
  22. @return Error codes propagated from
  23. VirtioFsFuseDirentPlusToEfiFileInfo() and
  24. VirtioFsFuseAttrToEfiFileInfo(). The contents of
  25. FileInfo are indeterminate.
  26. **/
  27. STATIC
  28. EFI_STATUS
  29. PopulateFileInfo (
  30. IN VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE *Dirent,
  31. IN UINTN SingleFileInfoSize,
  32. OUT EFI_FILE_INFO *FileInfo
  33. )
  34. {
  35. EFI_STATUS Status;
  36. //
  37. // Convert the name, set the actual size.
  38. //
  39. FileInfo->Size = SingleFileInfoSize;
  40. Status = VirtioFsFuseDirentPlusToEfiFileInfo (Dirent, FileInfo);
  41. if (EFI_ERROR (Status)) {
  42. return Status;
  43. }
  44. //
  45. // Populate the scalar fields.
  46. //
  47. Status = VirtioFsFuseAttrToEfiFileInfo (&Dirent->AttrResp, FileInfo);
  48. return Status;
  49. }
  50. /**
  51. Refill the EFI_FILE_INFO cache from the directory stream.
  52. **/
  53. STATIC
  54. EFI_STATUS
  55. RefillFileInfoCache (
  56. IN OUT VIRTIO_FS_FILE *VirtioFsFile
  57. )
  58. {
  59. VIRTIO_FS *VirtioFs;
  60. EFI_STATUS Status;
  61. VIRTIO_FS_FUSE_STATFS_RESPONSE FilesysAttr;
  62. UINT32 DirentBufSize;
  63. UINT8 *DirentBuf;
  64. UINTN SingleFileInfoSize;
  65. UINT8 *FileInfoArray;
  66. UINT64 DirStreamCookie;
  67. UINT64 CacheEndsAtCookie;
  68. UINTN NumFileInfo;
  69. //
  70. // Allocate a DirentBuf that can receive at least
  71. // VIRTIO_FS_FILE_MAX_FILE_INFO directory entries, based on the maximum
  72. // filename length supported by the filesystem. Note that the multiplication
  73. // is safe from overflow due to the VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE()
  74. // check.
  75. //
  76. VirtioFs = VirtioFsFile->OwnerFs;
  77. Status = VirtioFsFuseStatFs (VirtioFs, VirtioFsFile->NodeId, &FilesysAttr);
  78. if (EFI_ERROR (Status)) {
  79. return Status;
  80. }
  81. DirentBufSize = (UINT32)VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE (
  82. FilesysAttr.Namelen
  83. );
  84. if (DirentBufSize == 0) {
  85. return EFI_UNSUPPORTED;
  86. }
  87. DirentBufSize *= VIRTIO_FS_FILE_MAX_FILE_INFO;
  88. DirentBuf = AllocatePool (DirentBufSize);
  89. if (DirentBuf == NULL) {
  90. return EFI_OUT_OF_RESOURCES;
  91. }
  92. //
  93. // Allocate the EFI_FILE_INFO cache. A single EFI_FILE_INFO element is sized
  94. // accordingly to the maximum filename length supported by the filesystem.
  95. //
  96. // Note that the calculation below cannot overflow, due to the filename limit
  97. // imposed by the VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE() check above. The
  98. // calculation takes the L'\0' character that we'll need to append into
  99. // account.
  100. //
  101. SingleFileInfoSize = (OFFSET_OF (EFI_FILE_INFO, FileName) +
  102. ((UINTN)FilesysAttr.Namelen + 1) * sizeof (CHAR16));
  103. FileInfoArray = AllocatePool (
  104. VIRTIO_FS_FILE_MAX_FILE_INFO * SingleFileInfoSize
  105. );
  106. if (FileInfoArray == NULL) {
  107. Status = EFI_OUT_OF_RESOURCES;
  108. goto FreeDirentBuf;
  109. }
  110. //
  111. // Pick up reading the directory stream where the previous cache ended.
  112. //
  113. DirStreamCookie = VirtioFsFile->FilePosition;
  114. CacheEndsAtCookie = VirtioFsFile->FilePosition;
  115. NumFileInfo = 0;
  116. do {
  117. UINT32 Remaining;
  118. UINT32 Consumed;
  119. //
  120. // Fetch a chunk of the directory stream. The chunk may hold more entries
  121. // than what we can fit in the cache. The chunk may also not entirely fill
  122. // the cache, especially after filtering out entries that cannot be
  123. // supported under UEFI (sockets, FIFOs, filenames with backslashes, etc).
  124. //
  125. Remaining = DirentBufSize;
  126. Status = VirtioFsFuseReadFileOrDir (
  127. VirtioFs,
  128. VirtioFsFile->NodeId,
  129. VirtioFsFile->FuseHandle,
  130. TRUE, // IsDir
  131. DirStreamCookie, // Offset
  132. &Remaining, // Size
  133. DirentBuf // Data
  134. );
  135. if (EFI_ERROR (Status)) {
  136. goto FreeFileInfoArray;
  137. }
  138. if (Remaining == 0) {
  139. //
  140. // The directory stream ends.
  141. //
  142. break;
  143. }
  144. //
  145. // Iterate over all records in DirentBuf. Primarily, forget them all.
  146. // Secondarily, if a record proves transformable to EFI_FILE_INFO, add it
  147. // to the EFI_FILE_INFO cache (unless the cache is full).
  148. //
  149. Consumed = 0;
  150. while (Remaining >= sizeof (VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE)) {
  151. VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE *Dirent;
  152. UINT32 DirentSize;
  153. Dirent = (VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE *)(DirentBuf + Consumed);
  154. DirentSize = (UINT32)VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE (
  155. Dirent->Namelen
  156. );
  157. if (DirentSize == 0) {
  158. //
  159. // This means one of two things: (a) Dirent->Namelen is zero, or (b)
  160. // (b) Dirent->Namelen is unsupportably large. (a) is just invalid for
  161. // the Virtio Filesystem device to send, while (b) shouldn't happen
  162. // because "FilesysAttr.Namelen" -- the maximum filename length
  163. // supported by the filesystem -- proved acceptable above.
  164. //
  165. Status = EFI_PROTOCOL_ERROR;
  166. goto FreeFileInfoArray;
  167. }
  168. if (DirentSize > Remaining) {
  169. //
  170. // Dirent->Namelen suggests that the filename byte array (plus any
  171. // padding) are truncated. This should never happen; the Virtio
  172. // Filesystem device is supposed to send complete entries only.
  173. //
  174. Status = EFI_PROTOCOL_ERROR;
  175. goto FreeFileInfoArray;
  176. }
  177. if (Dirent->Namelen > FilesysAttr.Namelen) {
  178. //
  179. // This is possible without tripping the truncation check above, due to
  180. // how entries are padded. The condition means that Dirent->Namelen is
  181. // reportedly larger than the filesystem limit, without spilling into
  182. // the next alignment bucket. Should never happen.
  183. //
  184. Status = EFI_PROTOCOL_ERROR;
  185. goto FreeFileInfoArray;
  186. }
  187. //
  188. // If we haven't filled the EFI_FILE_INFO cache yet, attempt transforming
  189. // Dirent to EFI_FILE_INFO.
  190. //
  191. if (NumFileInfo < VIRTIO_FS_FILE_MAX_FILE_INFO) {
  192. EFI_FILE_INFO *FileInfo;
  193. FileInfo = (EFI_FILE_INFO *)(FileInfoArray +
  194. (NumFileInfo * SingleFileInfoSize));
  195. Status = PopulateFileInfo (Dirent, SingleFileInfoSize, FileInfo);
  196. if (!EFI_ERROR (Status)) {
  197. //
  198. // Dirent has been transformed and cached successfully.
  199. //
  200. NumFileInfo++;
  201. //
  202. // The next time we refill the cache, restart reading the directory
  203. // stream right after the entry that we've just transformed and
  204. // cached.
  205. //
  206. CacheEndsAtCookie = Dirent->CookieForNextEntry;
  207. }
  208. //
  209. // If Dirent wasn't transformable to an EFI_FILE_INFO, we'll just skip
  210. // it.
  211. //
  212. }
  213. //
  214. // Make the Virtio Filesystem device forget the NodeId in this directory
  215. // entry, as we'll need it no more. (The "." and ".." entries need no
  216. // FUSE_FORGET requests, when returned by FUSE_READDIRPLUS -- and so the
  217. // Virtio Filesystem device reports their NodeId fields as zero.)
  218. //
  219. if (Dirent->NodeResp.NodeId != 0) {
  220. VirtioFsFuseForget (VirtioFs, Dirent->NodeResp.NodeId);
  221. }
  222. //
  223. // Advance to the next entry in DirentBuf.
  224. //
  225. DirStreamCookie = Dirent->CookieForNextEntry;
  226. Consumed += DirentSize;
  227. Remaining -= DirentSize;
  228. }
  229. if (Remaining > 0) {
  230. //
  231. // This suggests that a VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE header was
  232. // truncated. This should never happen; the Virtio Filesystem device is
  233. // supposed to send complete entries only.
  234. //
  235. Status = EFI_PROTOCOL_ERROR;
  236. goto FreeFileInfoArray;
  237. }
  238. //
  239. // Fetch another DirentBuf from the directory stream, unless we've filled
  240. // the EFI_FILE_INFO cache.
  241. //
  242. } while (NumFileInfo < VIRTIO_FS_FILE_MAX_FILE_INFO);
  243. //
  244. // Commit the results. (Note that the result may be an empty cache.)
  245. //
  246. if (VirtioFsFile->FileInfoArray != NULL) {
  247. FreePool (VirtioFsFile->FileInfoArray);
  248. }
  249. VirtioFsFile->FileInfoArray = FileInfoArray;
  250. VirtioFsFile->SingleFileInfoSize = SingleFileInfoSize;
  251. VirtioFsFile->NumFileInfo = NumFileInfo;
  252. VirtioFsFile->NextFileInfo = 0;
  253. VirtioFsFile->FilePosition = CacheEndsAtCookie;
  254. FreePool (DirentBuf);
  255. return EFI_SUCCESS;
  256. FreeFileInfoArray:
  257. FreePool (FileInfoArray);
  258. FreeDirentBuf:
  259. FreePool (DirentBuf);
  260. return Status;
  261. }
  262. /**
  263. Read an entry from the EFI_FILE_INFO cache.
  264. **/
  265. STATIC
  266. EFI_STATUS
  267. ReadFileInfoCache (
  268. IN OUT VIRTIO_FS_FILE *VirtioFsFile,
  269. IN OUT UINTN *BufferSize,
  270. OUT VOID *Buffer
  271. )
  272. {
  273. EFI_FILE_INFO *FileInfo;
  274. UINTN CallerAllocated;
  275. //
  276. // Refill the cache if needed. If the refill doesn't produce any new
  277. // EFI_FILE_INFO, report End of Directory, by setting (*BufferSize) to 0.
  278. //
  279. if (VirtioFsFile->NextFileInfo == VirtioFsFile->NumFileInfo) {
  280. EFI_STATUS Status;
  281. Status = RefillFileInfoCache (VirtioFsFile);
  282. if (EFI_ERROR (Status)) {
  283. return (Status == EFI_BUFFER_TOO_SMALL) ? EFI_DEVICE_ERROR : Status;
  284. }
  285. if (VirtioFsFile->NumFileInfo == 0) {
  286. *BufferSize = 0;
  287. return EFI_SUCCESS;
  288. }
  289. }
  290. FileInfo = (EFI_FILE_INFO *)(VirtioFsFile->FileInfoArray +
  291. (VirtioFsFile->NextFileInfo *
  292. VirtioFsFile->SingleFileInfoSize));
  293. //
  294. // Check if the caller is ready to accept FileInfo. If not, we'll just
  295. // present the required size for now.
  296. //
  297. // (The (UINTN) cast below is safe because FileInfo->Size has been reduced
  298. // from VirtioFsFile->SingleFileInfoSize, in
  299. //
  300. // RefillFileInfoCache()
  301. // PopulateFileInfo()
  302. // VirtioFsFuseDirentPlusToEfiFileInfo()
  303. //
  304. // and VirtioFsFile->SingleFileInfoSize was computed from
  305. // FilesysAttr.Namelen, which had been accepted by
  306. // VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE().)
  307. //
  308. CallerAllocated = *BufferSize;
  309. *BufferSize = (UINTN)FileInfo->Size;
  310. if (CallerAllocated < *BufferSize) {
  311. return EFI_BUFFER_TOO_SMALL;
  312. }
  313. //
  314. // Output FileInfo, and remove it from the cache.
  315. //
  316. CopyMem (Buffer, FileInfo, *BufferSize);
  317. VirtioFsFile->NextFileInfo++;
  318. return EFI_SUCCESS;
  319. }
  320. /**
  321. Read from a regular file.
  322. **/
  323. STATIC
  324. EFI_STATUS
  325. ReadRegularFile (
  326. IN OUT VIRTIO_FS_FILE *VirtioFsFile,
  327. IN OUT UINTN *BufferSize,
  328. OUT VOID *Buffer
  329. )
  330. {
  331. VIRTIO_FS *VirtioFs;
  332. EFI_STATUS Status;
  333. VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
  334. UINTN Transferred;
  335. UINTN Left;
  336. VirtioFs = VirtioFsFile->OwnerFs;
  337. //
  338. // The UEFI spec forbids reads that start beyond the end of the file.
  339. //
  340. Status = VirtioFsFuseGetAttr (VirtioFs, VirtioFsFile->NodeId, &FuseAttr);
  341. if (EFI_ERROR (Status) || (VirtioFsFile->FilePosition > FuseAttr.Size)) {
  342. return EFI_DEVICE_ERROR;
  343. }
  344. Status = EFI_SUCCESS;
  345. Transferred = 0;
  346. Left = *BufferSize;
  347. while (Left > 0) {
  348. UINT32 ReadSize;
  349. //
  350. // FUSE_READ cannot express a >=4GB buffer size.
  351. //
  352. ReadSize = (UINT32)MIN ((UINTN)MAX_UINT32, Left);
  353. Status = VirtioFsFuseReadFileOrDir (
  354. VirtioFs,
  355. VirtioFsFile->NodeId,
  356. VirtioFsFile->FuseHandle,
  357. FALSE, // IsDir
  358. VirtioFsFile->FilePosition + Transferred,
  359. &ReadSize,
  360. (UINT8 *)Buffer + Transferred
  361. );
  362. if (EFI_ERROR (Status) || (ReadSize == 0)) {
  363. break;
  364. }
  365. Transferred += ReadSize;
  366. Left -= ReadSize;
  367. }
  368. *BufferSize = Transferred;
  369. VirtioFsFile->FilePosition += Transferred;
  370. //
  371. // If we managed to read some data, return success. If zero bytes were
  372. // transferred due to zero-sized buffer on input or due to EOF on first read,
  373. // return SUCCESS. Otherwise, return the error due to which zero bytes were
  374. // transferred.
  375. //
  376. return (Transferred > 0) ? EFI_SUCCESS : Status;
  377. }
  378. EFI_STATUS
  379. EFIAPI
  380. VirtioFsSimpleFileRead (
  381. IN EFI_FILE_PROTOCOL *This,
  382. IN OUT UINTN *BufferSize,
  383. OUT VOID *Buffer
  384. )
  385. {
  386. VIRTIO_FS_FILE *VirtioFsFile;
  387. EFI_STATUS Status;
  388. VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
  389. if (VirtioFsFile->IsDirectory) {
  390. Status = ReadFileInfoCache (VirtioFsFile, BufferSize, Buffer);
  391. } else {
  392. Status = ReadRegularFile (VirtioFsFile, BufferSize, Buffer);
  393. }
  394. return Status;
  395. }