]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_WRITE
authorLaszlo Ersek <lersek@redhat.com>
Wed, 16 Dec 2020 21:11:15 +0000 (22:11 +0100)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Mon, 21 Dec 2020 17:16:23 +0000 (17:16 +0000)
Add the VirtioFsFuseWrite() function, for sending the FUSE_WRITE command
to the Virtio Filesystem device.

(For avoiding oversized FUSE_WRITE commands, save the maximum write buffer
size that is advertized by the FUSE server, in the session init code.)

Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Philippe Mathieu-Daudé <philmd@redhat.com>
Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=3097
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20201216211125.19496-39-lersek@redhat.com>
Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
OvmfPkg/Include/IndustryStandard/VirtioFs.h
OvmfPkg/VirtioFsDxe/FuseInit.c
OvmfPkg/VirtioFsDxe/FuseWrite.c [new file with mode: 0644]
OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf

index fa8c40019f4e2d5bc027d6f56489bce6512cfa6f..0b7b3ff80edde6c4de14246cf30b7ad8e0f0fa4e 100644 (file)
@@ -153,6 +153,7 @@ typedef enum {
   VirtioFsFuseOpRmDir       = 11,\r
   VirtioFsFuseOpOpen        = 14,\r
   VirtioFsFuseOpRead        = 15,\r
+  VirtioFsFuseOpWrite       = 16,\r
   VirtioFsFuseOpStatFs      = 17,\r
   VirtioFsFuseOpRelease     = 18,\r
   VirtioFsFuseOpFsync       = 20,\r
@@ -282,6 +283,24 @@ typedef struct {
   UINT32 Padding;\r
 } VIRTIO_FS_FUSE_READ_REQUEST;\r
 \r
+//\r
+// Headers for VirtioFsFuseOpWrite.\r
+//\r
+typedef struct {\r
+  UINT64 FileHandle;\r
+  UINT64 Offset;\r
+  UINT32 Size;\r
+  UINT32 WriteFlags;\r
+  UINT64 LockOwner;\r
+  UINT32 Flags;\r
+  UINT32 Padding;\r
+} VIRTIO_FS_FUSE_WRITE_REQUEST;\r
+\r
+typedef struct {\r
+  UINT32 Size;\r
+  UINT32 Padding;\r
+} VIRTIO_FS_FUSE_WRITE_RESPONSE;\r
+\r
 //\r
 // Header for VirtioFsFuseOpStatFs.\r
 //\r
index 7aa6ee75caf67b5e523988fa0e4fa4f7ea8bd0eb..4318428049127a5da5e554498d319d3cca63fb13 100644 (file)
 \r
   @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_INIT\r
                            request to. The FUSE request counter\r
-                           "VirtioFs->RequestId" is set to 1 on output.\r
+                           "VirtioFs->RequestId" is set to 1 on output. The\r
+                           maximum write buffer size exposed in the FUSE_INIT\r
+                           response is saved in "VirtioFs->MaxWrite", on\r
+                           output.\r
 \r
   @retval EFI_SUCCESS      The FUSE session has been started.\r
 \r
@@ -126,9 +129,14 @@ VirtioFsFuseInitSession (
   //\r
   if (InitResp.Major < InitReq.Major ||\r
       (InitResp.Major == InitReq.Major && InitResp.Minor < InitReq.Minor) ||\r
-      (InitResp.Flags & VIRTIO_FS_FUSE_INIT_REQ_F_DO_READDIRPLUS) == 0) {\r
+      (InitResp.Flags & VIRTIO_FS_FUSE_INIT_REQ_F_DO_READDIRPLUS) == 0 ||\r
+      InitResp.MaxWrite < SIZE_4KB) {\r
     return EFI_UNSUPPORTED;\r
   }\r
 \r
+  //\r
+  // Save the maximum write buffer size for FUSE_WRITE requests.\r
+  //\r
+  VirtioFs->MaxWrite = InitResp.MaxWrite;\r
   return EFI_SUCCESS;\r
 }\r
diff --git a/OvmfPkg/VirtioFsDxe/FuseWrite.c b/OvmfPkg/VirtioFsDxe/FuseWrite.c
new file mode 100644 (file)
index 0000000..cc552bd
--- /dev/null
@@ -0,0 +1,155 @@
+/** @file\r
+  FUSE_WRITE wrapper for the Virtio Filesystem device.\r
+\r
+  Copyright (C) 2020, Red Hat, Inc.\r
+\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include "VirtioFsDxe.h"\r
+\r
+/**\r
+  Write a chunk to a regular file, by sending the FUSE_WRITE request to the\r
+  Virtio Filesystem device.\r
+\r
+  The function may only be called after VirtioFsFuseInitSession() returns\r
+  successfully and before VirtioFsUninit() is called.\r
+\r
+  @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_WRITE\r
+                           request to. On output, the FUSE request counter\r
+                           "VirtioFs->RequestId" will have been incremented.\r
+\r
+  @param[in] NodeId        The inode number of the regular file to write to.\r
+\r
+  @param[in] FuseHandle    The open handle to the regular file to write to.\r
+\r
+  @param[in] Offset        The absolute file position at which to start\r
+                           writing.\r
+\r
+  @param[in,out] Size      On input, the number of bytes to write. On\r
+                           successful return, the number of bytes actually\r
+                           written, which may be smaller than the value on\r
+                           input.\r
+\r
+  @param[in] Data          The buffer to write to the regular file.\r
+\r
+  @retval EFI_SUCCESS          Write successful. The caller is responsible for\r
+                               checking Size to learn the actual byte count\r
+                               transferred.\r
+\r
+  @retval EFI_BAD_BUFFER_SIZE  On input, Size is larger than\r
+                               "VirtioFs->MaxWrite".\r
+\r
+  @return                      The "errno" value mapped to an EFI_STATUS code,\r
+                               if the Virtio Filesystem device explicitly\r
+                               reported an error.\r
+\r
+  @return                      Error codes propagated from\r
+                               VirtioFsSgListsValidate(),\r
+                               VirtioFsFuseNewRequest(),\r
+                               VirtioFsSgListsSubmit(),\r
+                               VirtioFsFuseCheckResponse().\r
+**/\r
+EFI_STATUS\r
+VirtioFsFuseWrite (\r
+  IN OUT VIRTIO_FS *VirtioFs,\r
+  IN     UINT64    NodeId,\r
+  IN     UINT64    FuseHandle,\r
+  IN     UINT64    Offset,\r
+  IN OUT UINT32    *Size,\r
+  IN     VOID      *Data\r
+  )\r
+{\r
+  VIRTIO_FS_FUSE_REQUEST        CommonReq;\r
+  VIRTIO_FS_FUSE_WRITE_REQUEST  WriteReq;\r
+  VIRTIO_FS_IO_VECTOR           ReqIoVec[3];\r
+  VIRTIO_FS_SCATTER_GATHER_LIST ReqSgList;\r
+  VIRTIO_FS_FUSE_RESPONSE       CommonResp;\r
+  VIRTIO_FS_FUSE_WRITE_RESPONSE WriteResp;\r
+  VIRTIO_FS_IO_VECTOR           RespIoVec[2];\r
+  VIRTIO_FS_SCATTER_GATHER_LIST RespSgList;\r
+  EFI_STATUS                    Status;\r
+\r
+  //\r
+  // Honor the write buffer size limit of the Virtio Filesystem device.\r
+  //\r
+  if (*Size > VirtioFs->MaxWrite) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  //\r
+  // Set up the scatter-gather lists.\r
+  //\r
+  ReqIoVec[0].Buffer = &CommonReq;\r
+  ReqIoVec[0].Size   = sizeof CommonReq;\r
+  ReqIoVec[1].Buffer = &WriteReq;\r
+  ReqIoVec[1].Size   = sizeof WriteReq;\r
+  ReqIoVec[2].Buffer = Data;\r
+  ReqIoVec[2].Size   = *Size;\r
+  ReqSgList.IoVec    = ReqIoVec;\r
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);\r
+\r
+  RespIoVec[0].Buffer = &CommonResp;\r
+  RespIoVec[0].Size   = sizeof CommonResp;\r
+  RespIoVec[1].Buffer = &WriteResp;\r
+  RespIoVec[1].Size   = sizeof WriteResp;\r
+  RespSgList.IoVec    = RespIoVec;\r
+  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);\r
+\r
+  //\r
+  // Validate the scatter-gather lists; calculate the total transfer sizes.\r
+  //\r
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Populate the common request header.\r
+  //\r
+  Status = VirtioFsFuseNewRequest (VirtioFs, &CommonReq, ReqSgList.TotalSize,\r
+             VirtioFsFuseOpWrite, NodeId);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Populate the FUSE_WRITE-specific fields.\r
+  //\r
+  WriteReq.FileHandle = FuseHandle;\r
+  WriteReq.Offset     = Offset;\r
+  WriteReq.Size       = *Size;\r
+  WriteReq.WriteFlags = 0;\r
+  WriteReq.LockOwner  = 0;\r
+  WriteReq.Flags      = 0;\r
+  WriteReq.Padding    = 0;\r
+\r
+  //\r
+  // Submit the request.\r
+  //\r
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Verify the response (all response buffers are fixed size).\r
+  //\r
+  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    if (Status == EFI_DEVICE_ERROR) {\r
+      DEBUG ((DEBUG_ERROR, "%a: Label=\"%s\" NodeId=%Lu FuseHandle=%Lu "\r
+        "Offset=0x%Lx Size=0x%x Data@%p Errno=%d\n", __FUNCTION__,\r
+        VirtioFs->Label, NodeId, FuseHandle, Offset, *Size, Data,\r
+        CommonResp.Error));\r
+      Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);\r
+    }\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Report the actual transfer size.\r
+  //\r
+  *Size = WriteResp.Size;\r
+  return EFI_SUCCESS;\r
+}\r
index a704acdd520e4a3dfc23f7b61566dd6099b4a9d7..132a63400527ab583cbbc96cb7c3f8f574365a4a 100644 (file)
@@ -78,6 +78,7 @@ typedef struct {
   VRING                           Ring;      // VirtioRingInit      2\r
   VOID                            *RingMap;  // VirtioRingMap       2\r
   UINT64                          RequestId; // FuseInitSession     1\r
+  UINT32                          MaxWrite;  // FuseInitSession     1\r
   EFI_EVENT                       ExitBoot;  // DriverBindingStart  0\r
   LIST_ENTRY                      OpenFiles; // DriverBindingStart  0\r
   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL SimpleFs;  // DriverBindingStart  0\r
@@ -334,6 +335,16 @@ VirtioFsFuseReadFileOrDir (
      OUT VOID      *Data\r
   );\r
 \r
+EFI_STATUS\r
+VirtioFsFuseWrite (\r
+  IN OUT VIRTIO_FS *VirtioFs,\r
+  IN     UINT64    NodeId,\r
+  IN     UINT64    FuseHandle,\r
+  IN     UINT64    Offset,\r
+  IN OUT UINT32    *Size,\r
+  IN     VOID      *Data\r
+  );\r
+\r
 EFI_STATUS\r
 VirtioFsFuseStatFs (\r
   IN OUT VIRTIO_FS                      *VirtioFs,\r
index 39c77c7c4ee98a55da07c81fe8776bcbfbd3c008..2c145da5f5aed8be26a6482c4f58598ee195d1c4 100644 (file)
@@ -97,6 +97,7 @@
   FuseRelease.c\r
   FuseStatFs.c\r
   FuseUnlink.c\r
+  FuseWrite.c\r
   Helpers.c\r
   SimpleFsClose.c\r
   SimpleFsDelete.c\r