FuseRead.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /** @file
  2. FUSE_READ / FUSE_READDIRPLUS wrapper for the Virtio Filesystem device.
  3. Copyright (C) 2020, Red Hat, Inc.
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. **/
  6. #include "VirtioFsDxe.h"
  7. /**
  8. Read a chunk from a regular file or a directory stream, by sending the
  9. FUSE_READ / FUSE_READDIRPLUS request to the Virtio Filesystem device.
  10. The function may only be called after VirtioFsFuseInitSession() returns
  11. successfully and before VirtioFsUninit() is called.
  12. @param[in,out] VirtioFs The Virtio Filesystem device to send the FUSE_READ
  13. or FUSE_READDIRPLUS request to. On output, the FUSE
  14. request counter "VirtioFs->RequestId" will have been
  15. incremented.
  16. @param[in] NodeId The inode number of the regular file or directory
  17. stream to read from.
  18. @param[in] FuseHandle The open handle to the regular file or directory
  19. stream to read from.
  20. @param[in] IsDir TRUE if NodeId and FuseHandle refer to a directory,
  21. FALSE if NodeId and FuseHandle refer to a regular
  22. file.
  23. @param[in] Offset If IsDir is FALSE: the absolute file position at
  24. which to start reading.
  25. If IsDir is TRUE: the directory stream cookie at
  26. which to start or continue reading. The zero-valued
  27. cookie identifies the start of the directory stream.
  28. Further positions in the directory stream can be
  29. passed in from the CookieForNextEntry field of
  30. VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE.
  31. @param[in,out] Size On input, the number of bytes to read. On successful
  32. return, the number of bytes actually read, which may
  33. be smaller than the value on input.
  34. When reading a regular file (i.e., when IsDir is
  35. FALSE), EOF can be detected by passing in a nonzero
  36. Size, and finding a zero Size on output.
  37. When reading a directory stream (i.e., when IsDir is
  38. TRUE), Data consists of a sequence of variably-sized
  39. records (directory entries). A read operation
  40. returns the maximal sequence of records that fits in
  41. Size, without having to truncate a record. In order
  42. to guarantee progress, call
  43. VirtioFsFuseStatFs (VirtioFs, NodeId,
  44. &FilesysAttr)
  45. first, to learn the maximum Namelen for the
  46. directory stream. Then assign Size at least
  47. VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE (
  48. FilesysAttr.Namelen)
  49. on input. (Remember that
  50. VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE() may return
  51. 0 if its Namelen argument is invalid.) EOF can be
  52. detected if Size is set on input like described
  53. above, and Size is zero on output.
  54. @param[out] Data Buffer to read the bytes from the regular file or
  55. the directory stream into. The caller is responsible
  56. for providing room for (at least) as many bytes in
  57. Data as Size is on input.
  58. @retval EFI_SUCCESS Read successful. The caller is responsible for checking
  59. Size to learn the actual byte count transferred.
  60. @return The "errno" value mapped to an EFI_STATUS code, if the
  61. Virtio Filesystem device explicitly reported an error.
  62. @return Error codes propagated from VirtioFsSgListsValidate(),
  63. VirtioFsFuseNewRequest(), VirtioFsSgListsSubmit(),
  64. VirtioFsFuseCheckResponse().
  65. **/
  66. EFI_STATUS
  67. VirtioFsFuseReadFileOrDir (
  68. IN OUT VIRTIO_FS *VirtioFs,
  69. IN UINT64 NodeId,
  70. IN UINT64 FuseHandle,
  71. IN BOOLEAN IsDir,
  72. IN UINT64 Offset,
  73. IN OUT UINT32 *Size,
  74. OUT VOID *Data
  75. )
  76. {
  77. VIRTIO_FS_FUSE_REQUEST CommonReq;
  78. VIRTIO_FS_FUSE_READ_REQUEST ReadReq;
  79. VIRTIO_FS_IO_VECTOR ReqIoVec[2];
  80. VIRTIO_FS_SCATTER_GATHER_LIST ReqSgList;
  81. VIRTIO_FS_FUSE_RESPONSE CommonResp;
  82. VIRTIO_FS_IO_VECTOR RespIoVec[2];
  83. VIRTIO_FS_SCATTER_GATHER_LIST RespSgList;
  84. EFI_STATUS Status;
  85. UINTN TailBufferFill;
  86. //
  87. // Set up the scatter-gather lists.
  88. //
  89. ReqIoVec[0].Buffer = &CommonReq;
  90. ReqIoVec[0].Size = sizeof CommonReq;
  91. ReqIoVec[1].Buffer = &ReadReq;
  92. ReqIoVec[1].Size = sizeof ReadReq;
  93. ReqSgList.IoVec = ReqIoVec;
  94. ReqSgList.NumVec = ARRAY_SIZE (ReqIoVec);
  95. RespIoVec[0].Buffer = &CommonResp;
  96. RespIoVec[0].Size = sizeof CommonResp;
  97. RespIoVec[1].Buffer = Data;
  98. RespIoVec[1].Size = *Size;
  99. RespSgList.IoVec = RespIoVec;
  100. RespSgList.NumVec = ARRAY_SIZE (RespIoVec);
  101. //
  102. // Validate the scatter-gather lists; calculate the total transfer sizes.
  103. //
  104. Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
  105. if (EFI_ERROR (Status)) {
  106. return Status;
  107. }
  108. //
  109. // Populate the common request header.
  110. //
  111. Status = VirtioFsFuseNewRequest (
  112. VirtioFs,
  113. &CommonReq,
  114. ReqSgList.TotalSize,
  115. IsDir ? VirtioFsFuseOpReadDirPlus : VirtioFsFuseOpRead,
  116. NodeId
  117. );
  118. if (EFI_ERROR (Status)) {
  119. return Status;
  120. }
  121. //
  122. // Populate the FUSE_READ- / FUSE_READDIRPLUS-specific fields.
  123. //
  124. ReadReq.FileHandle = FuseHandle;
  125. ReadReq.Offset = Offset;
  126. ReadReq.Size = *Size;
  127. ReadReq.ReadFlags = 0;
  128. ReadReq.LockOwner = 0;
  129. ReadReq.Flags = 0;
  130. ReadReq.Padding = 0;
  131. //
  132. // Submit the request.
  133. //
  134. Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
  135. if (EFI_ERROR (Status)) {
  136. return Status;
  137. }
  138. //
  139. // Verify the response. Note that TailBufferFill is variable.
  140. //
  141. Status = VirtioFsFuseCheckResponse (
  142. &RespSgList,
  143. CommonReq.Unique,
  144. &TailBufferFill
  145. );
  146. if (EFI_ERROR (Status)) {
  147. if (Status == EFI_DEVICE_ERROR) {
  148. DEBUG ((
  149. DEBUG_ERROR,
  150. "%a: Label=\"%s\" NodeId=%Lu FuseHandle=%Lu "
  151. "IsDir=%d Offset=0x%Lx Size=0x%x Data@%p Errno=%d\n",
  152. __FUNCTION__,
  153. VirtioFs->Label,
  154. NodeId,
  155. FuseHandle,
  156. IsDir,
  157. Offset,
  158. *Size,
  159. Data,
  160. CommonResp.Error
  161. ));
  162. Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
  163. }
  164. return Status;
  165. }
  166. //
  167. // Report the actual transfer size.
  168. //
  169. // Integer overflow in the (UINT32) cast below is not possible; the
  170. // VIRTIO_FS_SCATTER_GATHER_LIST functions would have caught that.
  171. //
  172. *Size = (UINT32)TailBufferFill;
  173. return EFI_SUCCESS;
  174. }