public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver
@ 2020-12-16 21:10 Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 01/48] OvmfPkg: introduce VirtioFsDxe Laszlo Ersek
                   ` (48 more replies)
  0 siblings, 49 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Leif Lindholm,
	Philippe Mathieu-Daudé

Ref:    https://bugzilla.tianocore.org/show_bug.cgi?id=3097
Repo:   https://pagure.io/lersek/edk2.git
Branch: virtio-fs (@ b8fd76d649d2)

The first commit and the bugzilla ticket state the use case.

References, including setup instructions:
- https://libvirt.org/kbase/virtiofs.html
- https://virtio-fs.gitlab.io/

Useful UEFI shell commands for testing: output redirections, attrib,
connect, cp, disconnect, edit, eficompress, efidecompress, hexedit, ls,
map, mkdir, mv, rm, setsize, timezone, touch, type, vol.

The series is largely structured as follows:
- helper functions and FUSE command wrappers are implemented as required
  by the next EFI_FILE_PROTOCOL interface,
- said EFI_FILE_PROTOCOL interface is implemented,
- lather, rinse, repeat.

Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Leif Lindholm <leif@nuviainc.com>
Cc: Philippe Mathieu-Daudé <philmd@redhat.com>

Thanks,
Laszlo

Laszlo Ersek (48):
  OvmfPkg: introduce VirtioFsDxe
  ArmVirtPkg: include VirtioFsDxe in the ArmVirtQemu* platforms
  OvmfPkg/VirtioFsDxe: DriverBinding: open VirtioDevice, install
    SimpleFs
  OvmfPkg/VirtioFsDxe: implement virtio device (un)initialization
  OvmfPkg/VirtioFsDxe: add a scatter-gather list data type
  OvmfPkg/VirtioFsDxe: introduce the basic FUSE request/response headers
  OvmfPkg/VirtioFsDxe: map "errno" values to EFI_STATUS
  OvmfPkg/VirtioFsDxe: submit the FUSE_INIT request to the device
  OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_OPENDIR
  OvmfPkg/VirtioFsDxe: add shared wrapper for FUSE_RELEASE /
    FUSE_RELEASEDIR
  OvmfPkg/VirtioFsDxe: implement
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume()
  OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_FORGET
  OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_FSYNC /
    FUSE_FSYNCDIR
  OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_FLUSH
  OvmfPkg/VirtioFsDxe: flush, sync, release and forget in Close() /
    Delete()
  OvmfPkg/VirtioFsDxe: add helper for appending and sanitizing paths
  OvmfPkg/VirtioFsDxe: manage path lifecycle in OpenVolume, Close,
    Delete
  OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_OPEN
  OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_MKDIR
  OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_CREATE
  OvmfPkg/VirtioFsDxe: convert FUSE inode attributes to EFI_FILE_INFO
  OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_LOOKUP
  OvmfPkg/VirtioFsDxe: split canon. path into last parent + last
    component
  OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_UNLINK / FUSE_RMDIR
  OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_GETATTR
  OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Open()
  OvmfPkg/VirtioFsDxe: erase the dir. entry in
    EFI_FILE_PROTOCOL.Delete()
  OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_STATFS
  OvmfPkg/VirtioFsDxe: add helper for formatting UEFI basenames
  OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.GetInfo()
  OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.GetPosition,
    .SetPosition
  OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_READ /
    FUSE_READDIRPLUS
  OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Read() for regular
    files
  OvmfPkg/VirtioFsDxe: convert FUSE dirent filename to EFI_FILE_INFO
  OvmfPkg/VirtioFsDxe: add EFI_FILE_INFO cache fields to VIRTIO_FS_FILE
  OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Read() for
    directories
  OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Flush()
  OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_WRITE
  OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Write()
  OvmfPkg/VirtioFsDxe: handle the volume label in
    EFI_FILE_PROTOCOL.SetInfo
  OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_RENAME2
  OvmfPkg/VirtioFsDxe: add helper for composing rename/move destination
    path
  OvmfPkg/VirtioFsDxe: handle file rename/move in
    EFI_FILE_PROTOCOL.SetInfo
  OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_SETATTR
  OvmfPkg/VirtioFsDxe: add helper for determining file size update
  OvmfPkg/VirtioFsDxe: add helper for determining access time updates
  OvmfPkg/VirtioFsDxe: add helper for determining file mode bits update
  OvmfPkg/VirtioFsDxe: handle attribute updates in
    EFI_FILE_PROTOCOL.SetInfo

 ArmVirtPkg/ArmVirtQemu.dsc                  |    3 +-
 ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc        |    3 +-
 ArmVirtPkg/ArmVirtQemuKernel.dsc            |    3 +-
 OvmfPkg/Include/IndustryStandard/Virtio10.h |    5 +
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  454 ++++
 OvmfPkg/OvmfPkgIa32.dsc                     |    2 +
 OvmfPkg/OvmfPkgIa32.fdf                     |    1 +
 OvmfPkg/OvmfPkgIa32X64.dsc                  |    2 +
 OvmfPkg/OvmfPkgIa32X64.fdf                  |    1 +
 OvmfPkg/OvmfPkgX64.dsc                      |    2 +
 OvmfPkg/OvmfPkgX64.fdf                      |    1 +
 OvmfPkg/VirtioFsDxe/DriverBinding.c         |  232 ++
 OvmfPkg/VirtioFsDxe/FuseFlush.c             |  111 +
 OvmfPkg/VirtioFsDxe/FuseForget.c            |   85 +
 OvmfPkg/VirtioFsDxe/FuseFsync.c             |  121 +
 OvmfPkg/VirtioFsDxe/FuseGetAttr.c           |  116 +
 OvmfPkg/VirtioFsDxe/FuseInit.c              |  142 ++
 OvmfPkg/VirtioFsDxe/FuseLookup.c            |  148 ++
 OvmfPkg/VirtioFsDxe/FuseMkDir.c             |  134 ++
 OvmfPkg/VirtioFsDxe/FuseOpen.c              |  126 +
 OvmfPkg/VirtioFsDxe/FuseOpenDir.c           |  120 +
 OvmfPkg/VirtioFsDxe/FuseOpenOrCreate.c      |  155 ++
 OvmfPkg/VirtioFsDxe/FuseRead.c              |  191 ++
 OvmfPkg/VirtioFsDxe/FuseRelease.c           |  121 +
 OvmfPkg/VirtioFsDxe/FuseRename.c            |  131 ++
 OvmfPkg/VirtioFsDxe/FuseSetAttr.c           |  174 ++
 OvmfPkg/VirtioFsDxe/FuseStatFs.c            |  102 +
 OvmfPkg/VirtioFsDxe/FuseUnlink.c            |  114 +
 OvmfPkg/VirtioFsDxe/FuseWrite.c             |  155 ++
 OvmfPkg/VirtioFsDxe/Helpers.c               | 2416 ++++++++++++++++++++
 OvmfPkg/VirtioFsDxe/SimpleFsClose.c         |   68 +
 OvmfPkg/VirtioFsDxe/SimpleFsDelete.c        |  110 +
 OvmfPkg/VirtioFsDxe/SimpleFsFlush.c         |   42 +
 OvmfPkg/VirtioFsDxe/SimpleFsGetInfo.c       |  209 ++
 OvmfPkg/VirtioFsDxe/SimpleFsGetPosition.c   |   27 +
 OvmfPkg/VirtioFsDxe/SimpleFsOpen.c          |  505 ++++
 OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c    |   98 +
 OvmfPkg/VirtioFsDxe/SimpleFsRead.c          |  434 ++++
 OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c       |  582 +++++
 OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c   |   67 +
 OvmfPkg/VirtioFsDxe/SimpleFsWrite.c         |   81 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |  544 +++++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |  136 ++
 43 files changed, 8271 insertions(+), 3 deletions(-)
 create mode 100644 OvmfPkg/Include/IndustryStandard/VirtioFs.h
 create mode 100644 OvmfPkg/VirtioFsDxe/DriverBinding.c
 create mode 100644 OvmfPkg/VirtioFsDxe/FuseFlush.c
 create mode 100644 OvmfPkg/VirtioFsDxe/FuseForget.c
 create mode 100644 OvmfPkg/VirtioFsDxe/FuseFsync.c
 create mode 100644 OvmfPkg/VirtioFsDxe/FuseGetAttr.c
 create mode 100644 OvmfPkg/VirtioFsDxe/FuseInit.c
 create mode 100644 OvmfPkg/VirtioFsDxe/FuseLookup.c
 create mode 100644 OvmfPkg/VirtioFsDxe/FuseMkDir.c
 create mode 100644 OvmfPkg/VirtioFsDxe/FuseOpen.c
 create mode 100644 OvmfPkg/VirtioFsDxe/FuseOpenDir.c
 create mode 100644 OvmfPkg/VirtioFsDxe/FuseOpenOrCreate.c
 create mode 100644 OvmfPkg/VirtioFsDxe/FuseRead.c
 create mode 100644 OvmfPkg/VirtioFsDxe/FuseRelease.c
 create mode 100644 OvmfPkg/VirtioFsDxe/FuseRename.c
 create mode 100644 OvmfPkg/VirtioFsDxe/FuseSetAttr.c
 create mode 100644 OvmfPkg/VirtioFsDxe/FuseStatFs.c
 create mode 100644 OvmfPkg/VirtioFsDxe/FuseUnlink.c
 create mode 100644 OvmfPkg/VirtioFsDxe/FuseWrite.c
 create mode 100644 OvmfPkg/VirtioFsDxe/Helpers.c
 create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsClose.c
 create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsDelete.c
 create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsFlush.c
 create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsGetInfo.c
 create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsGetPosition.c
 create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsOpen.c
 create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
 create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsRead.c
 create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c
 create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c
 create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsWrite.c
 create mode 100644 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
 create mode 100644 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf


base-commit: e6ae24e1d676bb2bdc0fc715b49b04908f41fc10
-- 
2.19.1.3.g30247aa5d201


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [edk2 PATCH 01/48] OvmfPkg: introduce VirtioFsDxe
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-18 17:42   ` Ard Biesheuvel
  2020-12-16 21:10 ` [edk2 PATCH 02/48] ArmVirtPkg: include VirtioFsDxe in the ArmVirtQemu* platforms Laszlo Ersek
                   ` (47 subsequent siblings)
  48 siblings, 1 reply; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

The purpose of the driver is to ease file exchange (file sharing) between
the guest firmware and the virtualization host. The driver is supposed to
interoperate with QEMU's "virtiofsd" (Virtio Filesystem Daemon).

References:
- https://virtio-fs.gitlab.io/
- https://libvirt.org/kbase/virtiofs.html

VirtioFsDxe will bind virtio-fs devices, and produce
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL instances on them.

In the longer term, assuming QEMU will create "bootorder" fw_cfg file
entries for virtio-fs devices, booting guest OSes from host-side
directories should become possible (dependent on the matching
QemuBootOrderLib enhancement).

Add the skeleton of the driver. Install EFI_DRIVER_BINDING_PROTOCOL with
stub member functions. Install EFI_COMPONENT_NAME2_PROTOCOL with final
member functions. This suffices for the DRIVERS command in the UEFI Shell
to list the driver with a human-readable name.

The file permission model is described immediately in the INF file as a
comment block, for future reference.

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>
---
 OvmfPkg/OvmfPkgIa32.dsc             |   1 +
 OvmfPkg/OvmfPkgIa32X64.dsc          |   1 +
 OvmfPkg/OvmfPkgX64.dsc              |   1 +
 OvmfPkg/OvmfPkgIa32.fdf             |   1 +
 OvmfPkg/OvmfPkgIa32X64.fdf          |   1 +
 OvmfPkg/OvmfPkgX64.fdf              |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf |  92 ++++++++++++++++
 OvmfPkg/VirtioFsDxe/DriverBinding.c | 112 ++++++++++++++++++++
 8 files changed, 210 insertions(+)

diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index 8eede796a8bd..4ff70674fb6e 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -807,16 +807,17 @@ [Components]
   }
   MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
   MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
   MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
   MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf
   MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
   FatPkg/EnhancedFatDxe/Fat.inf
   MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
+  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
   MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf
   MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf
   OvmfPkg/SataControllerDxe/SataControllerDxe.inf
   MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf
   MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf
   MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
   MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
   MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index f9f82a48f4b9..d40a59183c79 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -821,16 +821,17 @@ [Components.X64]
   }
   MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
   MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
   MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
   MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf
   MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
   FatPkg/EnhancedFatDxe/Fat.inf
   MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
+  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
   MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf
   MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf
   OvmfPkg/SataControllerDxe/SataControllerDxe.inf
   MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf
   MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf
   MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
   MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
   MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index e59ae05b73aa..ec7886235acf 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -817,16 +817,17 @@ [Components]
   }
   MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
   MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
   MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
   MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf
   MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
   FatPkg/EnhancedFatDxe/Fat.inf
   MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
+  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
   MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf
   MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf
   OvmfPkg/SataControllerDxe/SataControllerDxe.inf
   MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf
   MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf
   MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
   MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
   MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf
index c07b775d0a2d..f400c845b9c9 100644
--- a/OvmfPkg/OvmfPkgIa32.fdf
+++ b/OvmfPkg/OvmfPkgIa32.fdf
@@ -285,16 +285,17 @@ [FV.DXEFV]
 INF  OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
 INF  RuleOverride=ACPITABLE OvmfPkg/AcpiTables/AcpiTables.inf
 INF  MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
 INF  MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf
 INF  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
 
 INF  FatPkg/EnhancedFatDxe/Fat.inf
 INF  MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
+INF  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
 
 !if $(TOOL_CHAIN_TAG) != "XCODE5"
 INF  ShellPkg/DynamicCommand/TftpDynamicCommand/TftpDynamicCommand.inf
 INF  ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.inf
 INF  OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf
 !endif
 INF  ShellPkg/Application/Shell/Shell.inf
 
diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf
index 9adf1525c135..d055552fd09f 100644
--- a/OvmfPkg/OvmfPkgIa32X64.fdf
+++ b/OvmfPkg/OvmfPkgIa32X64.fdf
@@ -286,16 +286,17 @@ [FV.DXEFV]
 INF  OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
 INF  RuleOverride=ACPITABLE OvmfPkg/AcpiTables/AcpiTables.inf
 INF  MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
 INF  MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf
 INF  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
 
 INF  FatPkg/EnhancedFatDxe/Fat.inf
 INF  MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
+INF  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
 
 !if $(TOOL_CHAIN_TAG) != "XCODE5"
 INF  ShellPkg/DynamicCommand/TftpDynamicCommand/TftpDynamicCommand.inf
 INF  ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.inf
 INF  OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf
 !endif
 INF  ShellPkg/Application/Shell/Shell.inf
 
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index 17ba9e177ac3..1a2ef5bf2ae3 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -295,16 +295,17 @@ [FV.DXEFV]
 INF  OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
 INF  RuleOverride=ACPITABLE OvmfPkg/AcpiTables/AcpiTables.inf
 INF  MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
 INF  MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf
 INF  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
 
 INF  FatPkg/EnhancedFatDxe/Fat.inf
 INF  MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
+INF  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
 
 !if $(TOOL_CHAIN_TAG) != "XCODE5"
 INF  ShellPkg/DynamicCommand/TftpDynamicCommand/TftpDynamicCommand.inf
 INF  ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.inf
 INF  OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf
 !endif
 INF  ShellPkg/Application/Shell/Shell.inf
 
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
new file mode 100644
index 000000000000..69cb44bc7c96
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -0,0 +1,92 @@
+## @file
+# Provide EFI_SIMPLE_FILE_SYSTEM_PROTOCOL instances on virtio-fs devices.
+#
+# Copyright (C) 2020, Red Hat, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+# Permission Model of this driver:
+#
+# Regardless of the UID and GID values this driver send in the FUSE request
+# header, the daemon (that is, the Virtio Filesystem device) always acts with
+# root privileges on the host side. The only time the daemon considers said UID
+# and GID fields is when creating a new file or directory. Thus, the guest
+# driver cannot rely on the host for enforcing any file mode permissions,
+# regardless of the "personality" that the guest driver poses as, because
+# "root" on the host side ignores all file mode bits.
+#
+# Therefore the guest driver has to do its own permission checking, and use the
+# host-side file mode bits only as a kind of "metadata storage" or "reminder"
+# -- hopefully in a way that makes some sense on the host side too.
+#
+# The complete mapping between the EFI_FILE_PROTOCOL and the host-side file
+# mode bits is described below.
+#
+# - The guest driver poses as UID 0, GID 0, PID 1.
+#
+# - If and only if all "w" bits are missing from a file on the host side, then
+#   the file or directory is reported as EFI_FILE_READ_ONLY in the guest. When
+#   setting EFI_FILE_READ_ONLY in the guest, all "w" bits (0222) are cleared on
+#   the host; when clearing EFI_FILE_READ_ONLY in the guest, all "w" bits are
+#   set on the host. Viewed from the host side, this sort of reflects that an
+#   EFI_FILE_READ_ONLY file should not be written by anyone.
+#
+# - The attributes EFI_FILE_HIDDEN, EFI_FILE_SYSTEM, EFI_FILE_RESERVED, and
+#   EFI_FILE_ARCHIVE are never reported in the guest, and they are silently
+#   ignored when a SetInfo() call or a file-creating Open() call requests them.
+#
+# - On the host, files are created with 0666 file mode bits, directories are
+#   created with 0777 file mode bits.
+#
+# - In the guest, the EFI_FILE_READ_ONLY attribute only controls the permitted
+#   open mode. In particular, on directories, the EFI_FILE_READ_ONLY attribute
+#   does not prevent the creation or deletion of entries inside the directory;
+#   EFI_FILE_READ_ONLY only prevents the renaming, deleting, flushing (syncing)
+#   and touching of the directory itself (with "touching" meaning updating the
+#   timestamps). The fact that EFI_FILE_READ_ONLY being set on a directory is
+#   irrelevant in the guest with regard to entry creation/deletion, is
+#   well-mirrored by the fact that virtiofsd -- which runs as root, regardless
+#   of guest driver personality -- ignores the absence of "w" permissions on a
+#   host-side directory, when creating or removing entries in it.
+#
+# - When an EFI_FILE_PROTOCOL is opened read-only, then the Delete(), Write()
+#   and Flush() member functions are disabled for it. Additionally, SetInfo()
+#   is restricted to flipping the EFI_FILE_READ_ONLY bit (which takes effect at
+#   the next Open()).
+#
+# - As a consequence of the above, for deleting a directory, it must be
+#   presented in the guest as openable for writing.
+#
+# - We diverge from the UEFI spec, and permit Flush() on a directory that has
+#   been opened read-write; otherwise the only way to invoke FUSE_FSYNCDIR on a
+#   directory would be to Close() it.
+#
+# - OpenVolume() opens the root directory for read-only access. The Open()
+#   member function may open it for read-write access. While the root directory
+#   cannot be renamed or deleted, opening it for read-write access is useful
+#   for calling Flush(), according to the previous paragraph, or for updating
+#   the root directory's timestamps with SetInfo().
+##
+
+[Defines]
+  INF_VERSION                           = 1.29
+  BASE_NAME                             = VirtioFsDxe
+  FILE_GUID                             = 7BD9DDF7-8B83-488E-AEC9-24C78610289C
+  MODULE_TYPE                           = UEFI_DRIVER
+  ENTRY_POINT                           = VirtioFsEntryPoint
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[Sources]
+  DriverBinding.c
+
+[LibraryClasses]
+  BaseLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+
+[Protocols]
+  gEfiComponentName2ProtocolGuid        ## PRODUCES
+  gEfiDriverBindingProtocolGuid         ## PRODUCES
diff --git a/OvmfPkg/VirtioFsDxe/DriverBinding.c b/OvmfPkg/VirtioFsDxe/DriverBinding.c
new file mode 100644
index 000000000000..ac0a6330f01b
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/DriverBinding.c
@@ -0,0 +1,112 @@
+/** @file
+  Provide EFI_SIMPLE_FILE_SYSTEM_PROTOCOL instances on virtio-fs devices.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h>                  // AsciiStrCmp()
+#include <Library/UefiBootServicesTableLib.h> // gBS
+#include <Protocol/ComponentName2.h>          // EFI_COMPONENT_NAME2_PROTOCOL
+#include <Protocol/DriverBinding.h>           // EFI_DRIVER_BINDING_PROTOCOL
+
+//
+// UEFI Driver Model protocol instances.
+//
+STATIC EFI_DRIVER_BINDING_PROTOCOL  mDriverBinding;
+STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2;
+
+//
+// UEFI Driver Model protocol member functions.
+//
+EFI_STATUS
+EFIAPI
+VirtioFsBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioFsBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
+  )
+{
+  return EFI_DEVICE_ERROR;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioFsBindingStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  ControllerHandle,
+  IN UINTN                       NumberOfChildren,
+  IN EFI_HANDLE                  *ChildHandleBuffer OPTIONAL
+  )
+{
+  return EFI_DEVICE_ERROR;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioFsGetDriverName (
+  IN  EFI_COMPONENT_NAME2_PROTOCOL *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  )
+{
+  if (AsciiStrCmp (Language, "en") != 0) {
+    return EFI_UNSUPPORTED;
+  }
+  *DriverName = L"Virtio Filesystem Driver";
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioFsGetControllerName (
+  IN  EFI_COMPONENT_NAME2_PROTOCOL *This,
+  IN  EFI_HANDLE                   ControllerHandle,
+  IN  EFI_HANDLE                   ChildHandle OPTIONAL,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **ControllerName
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+//
+// Entry point of this driver.
+//
+EFI_STATUS
+EFIAPI
+VirtioFsEntryPoint (
+  IN EFI_HANDLE       ImageHandle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  EFI_STATUS Status;
+
+  mDriverBinding.Supported           = VirtioFsBindingSupported;
+  mDriverBinding.Start               = VirtioFsBindingStart;
+  mDriverBinding.Stop                = VirtioFsBindingStop;
+  mDriverBinding.Version             = 0x10;
+  mDriverBinding.ImageHandle         = ImageHandle;
+  mDriverBinding.DriverBindingHandle = ImageHandle;
+
+  mComponentName2.GetDriverName      = VirtioFsGetDriverName;
+  mComponentName2.GetControllerName  = VirtioFsGetControllerName;
+  mComponentName2.SupportedLanguages = "en";
+
+  Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle,
+                  &gEfiDriverBindingProtocolGuid, &mDriverBinding,
+                  &gEfiComponentName2ProtocolGuid, &mComponentName2, NULL);
+  return Status;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 02/48] ArmVirtPkg: include VirtioFsDxe in the ArmVirtQemu* platforms
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 01/48] OvmfPkg: introduce VirtioFsDxe Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 03/48] OvmfPkg/VirtioFsDxe: DriverBinding: open VirtioDevice, install SimpleFs Laszlo Ersek
                   ` (46 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Leif Lindholm, Philippe Mathieu-Daudé

Include the VirtioFsDxe driver in the ArmVirtPkg platforms that include
Virtio10Dxe. (The virtio-fs device is virtio-1.0-only.)

Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Cc: Leif Lindholm <leif@nuviainc.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>
---
 ArmVirtPkg/ArmVirtQemu.dsc           | 3 ++-
 ArmVirtPkg/ArmVirtQemuKernel.dsc     | 3 ++-
 ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc | 3 ++-
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc
index 365426bd7dfd..ef5d6dbeaddc 100644
--- a/ArmVirtPkg/ArmVirtQemu.dsc
+++ b/ArmVirtPkg/ArmVirtQemu.dsc
@@ -403,23 +403,24 @@ [Components.common]
   ArmVirtPkg/FdtClientDxe/FdtClientDxe.inf
   ArmVirtPkg/HighMemDxe/HighMemDxe.inf
   OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
   OvmfPkg/VirtioScsiDxe/VirtioScsi.inf
   OvmfPkg/VirtioNetDxe/VirtioNet.inf
   OvmfPkg/VirtioRngDxe/VirtioRng.inf
 
   #
-  # FAT filesystem + GPT/MBR partitioning + UDF filesystem
+  # FAT filesystem + GPT/MBR partitioning + UDF filesystem + virtio-fs
   #
   MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
   MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
   MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
   FatPkg/EnhancedFatDxe/Fat.inf
   MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
+  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
 
   #
   # Bds
   #
   MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf {
     <LibraryClasses>
       DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
       PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
diff --git a/ArmVirtPkg/ArmVirtQemuKernel.dsc b/ArmVirtPkg/ArmVirtQemuKernel.dsc
index f447c62f6898..f8f5f7f4b94b 100644
--- a/ArmVirtPkg/ArmVirtQemuKernel.dsc
+++ b/ArmVirtPkg/ArmVirtQemuKernel.dsc
@@ -339,23 +339,24 @@ [Components.common]
   ArmVirtPkg/FdtClientDxe/FdtClientDxe.inf
   ArmVirtPkg/HighMemDxe/HighMemDxe.inf
   OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
   OvmfPkg/VirtioScsiDxe/VirtioScsi.inf
   OvmfPkg/VirtioNetDxe/VirtioNet.inf
   OvmfPkg/VirtioRngDxe/VirtioRng.inf
 
   #
-  # FAT filesystem + GPT/MBR partitioning + UDF filesystem
+  # FAT filesystem + GPT/MBR partitioning + UDF filesystem + virtio-fs
   #
   MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
   MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
   MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
   FatPkg/EnhancedFatDxe/Fat.inf
   MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
+  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
 
   #
   # Bds
   #
   MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf {
     <LibraryClasses>
       DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
       PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
diff --git a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
index 6eade7e50ff7..1752fee12b79 100644
--- a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
+++ b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
@@ -72,23 +72,24 @@ [FV.FvMain]
   INF MdeModulePkg/Universal/SerialDxe/SerialDxe.inf
 
   INF ArmPkg/Drivers/ArmGic/ArmGicDxe.inf
   INF ArmPkg/Drivers/TimerDxe/TimerDxe.inf
   INF ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.inf
   INF MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
 
   #
-  # FAT filesystem + GPT/MBR partitioning + UDF filesystem
+  # FAT filesystem + GPT/MBR partitioning + UDF filesystem + virtio-fs
   #
   INF MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
   INF MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
   INF FatPkg/EnhancedFatDxe/Fat.inf
   INF MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
   INF MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
+  INF OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
 
   #
   # Status Code Routing
   #
   INF MdeModulePkg/Universal/ReportStatusCodeRouter/RuntimeDxe/ReportStatusCodeRouterRuntimeDxe.inf
 
   #
   # Platform Driver
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 03/48] OvmfPkg/VirtioFsDxe: DriverBinding: open VirtioDevice, install SimpleFs
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 01/48] OvmfPkg: introduce VirtioFsDxe Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 02/48] ArmVirtPkg: include VirtioFsDxe in the ArmVirtQemu* platforms Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 04/48] OvmfPkg/VirtioFsDxe: implement virtio device (un)initialization Laszlo Ersek
                   ` (45 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Complete the Supported, Start, and Stop member functions of
EFI_DRIVER_BINDING_PROTOCOL sufficiently for exercising the UEFI driver
model:

- bind virtio-fs devices,

- produce placeholder EFI_SIMPLE_FILE_SYSTEM_PROTOCOL instances on them.

On the "TO_START" (= Virtio) side, the VirtioFsBindingSupported() function
verifies the Virtio subsystem ID for the virtio-fs device (decimal 26 --
see
<https://github.com/oasis-tcs/virtio-spec/blob/87fa6b5d8155/virtio-fs.tex>).
Beyond that, no actual Virtio setup is performed for now. Those bits are
going to be implemented later in this series.

On the "BY_START" (= UEFI filesystem) side, the VirtioFsOpenVolume()
function -- which is the sole EFI_SIMPLE_FILE_SYSTEM_PROTOCOL member
function -- is a stub; it always returns EFI_NO_MEDIA, for now.

The "CONNECT", "DISCONNECT", and "MAP -R" UEFI Shell commands can be used
to test this patch.

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>
---
 OvmfPkg/Include/IndustryStandard/Virtio10.h |  5 ++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |  7 ++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           | 52 +++++++++++
 OvmfPkg/VirtioFsDxe/DriverBinding.c         | 94 +++++++++++++++++++-
 OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c    | 26 ++++++
 5 files changed, 181 insertions(+), 3 deletions(-)

diff --git a/OvmfPkg/Include/IndustryStandard/Virtio10.h b/OvmfPkg/Include/IndustryStandard/Virtio10.h
index 3c1592d1fdea..2c60be2b7c0c 100644
--- a/OvmfPkg/Include/IndustryStandard/Virtio10.h
+++ b/OvmfPkg/Include/IndustryStandard/Virtio10.h
@@ -12,16 +12,21 @@
 
 #include <IndustryStandard/Pci23.h>
 #include <IndustryStandard/Virtio095.h>
 
 //
 // Subsystem Device IDs (to be) introduced in VirtIo 1.0
 //
 #define VIRTIO_SUBSYSTEM_GPU_DEVICE         16
+//
+// Subsystem Device IDs from the VirtIo spec at git commit 87fa6b5d8155;
+// <https://github.com/oasis-tcs/virtio-spec/tree/87fa6b5d8155>.
+//
+#define VIRTIO_SUBSYSTEM_FILESYSTEM         26
 
 //
 // Structures for parsing the VirtIo 1.0 specific PCI capabilities from the
 // config space
 //
 #pragma pack (1)
 typedef struct {
   EFI_PCI_CAPABILITY_VENDOR_HDR VendorHdr;
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index 69cb44bc7c96..ff9b1c6178bc 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -73,20 +73,27 @@ [Defines]
   INF_VERSION                           = 1.29
   BASE_NAME                             = VirtioFsDxe
   FILE_GUID                             = 7BD9DDF7-8B83-488E-AEC9-24C78610289C
   MODULE_TYPE                           = UEFI_DRIVER
   ENTRY_POINT                           = VirtioFsEntryPoint
 
 [Packages]
   MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
 
 [Sources]
   DriverBinding.c
+  SimpleFsOpenVolume.c
+  VirtioFsDxe.h
 
 [LibraryClasses]
   BaseLib
+  DebugLib
+  MemoryAllocationLib
   UefiBootServicesTableLib
   UefiDriverEntryPoint
 
 [Protocols]
   gEfiComponentName2ProtocolGuid        ## PRODUCES
   gEfiDriverBindingProtocolGuid         ## PRODUCES
+  gEfiSimpleFileSystemProtocolGuid      ## BY_START
+  gVirtioDeviceProtocolGuid             ## TO_START
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
new file mode 100644
index 000000000000..287defd21f23
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -0,0 +1,52 @@
+/** @file
+  Internal macro definitions, type definitions, and function declarations for
+  the Virtio Filesystem device driver.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef VIRTIO_FS_DXE_H_
+#define VIRTIO_FS_DXE_H_
+
+#include <Base.h>                      // SIGNATURE_64()
+#include <Library/DebugLib.h>          // CR()
+#include <Protocol/SimpleFileSystem.h> // EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
+#include <Protocol/VirtioDevice.h>     // VIRTIO_DEVICE_PROTOCOL
+
+#define VIRTIO_FS_SIG SIGNATURE_64 ('V', 'I', 'R', 'T', 'I', 'O', 'F', 'S')
+
+//
+// Main context structure, expressing an EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
+// interface on top of the Virtio Filesystem device.
+//
+typedef struct {
+  //
+  // Parts of this structure are initialized / torn down in various functions
+  // at various call depths. The table to the right should make it easier to
+  // track them.
+  //
+  //                              field         init function       init depth
+  //                              -----------   ------------------  ----------
+  UINT64                          Signature; // DriverBindingStart  0
+  VIRTIO_DEVICE_PROTOCOL          *Virtio;   // DriverBindingStart  0
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL SimpleFs;  // DriverBindingStart  0
+} VIRTIO_FS;
+
+#define VIRTIO_FS_FROM_SIMPLE_FS(SimpleFsReference) \
+  CR (SimpleFsReference, VIRTIO_FS, SimpleFs, VIRTIO_FS_SIG);
+
+//
+// EFI_SIMPLE_FILE_SYSTEM_PROTOCOL member functions for the Virtio Filesystem
+// driver.
+//
+
+EFI_STATUS
+EFIAPI
+VirtioFsOpenVolume (
+  IN  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
+  OUT EFI_FILE_PROTOCOL               **Root
+  );
+
+#endif // VIRTIO_FS_DXE_H_
diff --git a/OvmfPkg/VirtioFsDxe/DriverBinding.c b/OvmfPkg/VirtioFsDxe/DriverBinding.c
index ac0a6330f01b..65e45b5c4bf7 100644
--- a/OvmfPkg/VirtioFsDxe/DriverBinding.c
+++ b/OvmfPkg/VirtioFsDxe/DriverBinding.c
@@ -1,21 +1,25 @@
 /** @file
   Provide EFI_SIMPLE_FILE_SYSTEM_PROTOCOL instances on virtio-fs devices.
 
   Copyright (C) 2020, Red Hat, Inc.
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
+#include <IndustryStandard/Virtio.h>          // VIRTIO_SUBSYSTEM_FILESYSTEM
 #include <Library/BaseLib.h>                  // AsciiStrCmp()
+#include <Library/MemoryAllocationLib.h>      // AllocatePool()
 #include <Library/UefiBootServicesTableLib.h> // gBS
 #include <Protocol/ComponentName2.h>          // EFI_COMPONENT_NAME2_PROTOCOL
 #include <Protocol/DriverBinding.h>           // EFI_DRIVER_BINDING_PROTOCOL
 
+#include "VirtioFsDxe.h"
+
 //
 // UEFI Driver Model protocol instances.
 //
 STATIC EFI_DRIVER_BINDING_PROTOCOL  mDriverBinding;
 STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2;
 
 //
 // UEFI Driver Model protocol member functions.
@@ -23,40 +27,124 @@ STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2;
 EFI_STATUS
 EFIAPI
 VirtioFsBindingSupported (
   IN EFI_DRIVER_BINDING_PROTOCOL *This,
   IN EFI_HANDLE                  ControllerHandle,
   IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
   )
 {
-  return EFI_UNSUPPORTED;
+  EFI_STATUS             Status;
+  VIRTIO_DEVICE_PROTOCOL *Virtio;
+  EFI_STATUS             CloseStatus;
+
+  Status = gBS->OpenProtocol (ControllerHandle, &gVirtioDeviceProtocolGuid,
+                  (VOID **)&Virtio, This->DriverBindingHandle,
+                  ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (Virtio->SubSystemDeviceId != VIRTIO_SUBSYSTEM_FILESYSTEM) {
+    Status = EFI_UNSUPPORTED;
+  }
+
+  CloseStatus = gBS->CloseProtocol (ControllerHandle,
+                       &gVirtioDeviceProtocolGuid, This->DriverBindingHandle,
+                       ControllerHandle);
+  ASSERT_EFI_ERROR (CloseStatus);
+
+  return Status;
 }
 
 EFI_STATUS
 EFIAPI
 VirtioFsBindingStart (
   IN EFI_DRIVER_BINDING_PROTOCOL *This,
   IN EFI_HANDLE                  ControllerHandle,
   IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
   )
 {
-  return EFI_DEVICE_ERROR;
+  VIRTIO_FS  *VirtioFs;
+  EFI_STATUS Status;
+  EFI_STATUS CloseStatus;
+
+  VirtioFs = AllocatePool (sizeof *VirtioFs);
+  if (VirtioFs == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  VirtioFs->Signature = VIRTIO_FS_SIG;
+
+  Status = gBS->OpenProtocol (ControllerHandle, &gVirtioDeviceProtocolGuid,
+                  (VOID **)&VirtioFs->Virtio, This->DriverBindingHandle,
+                  ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
+  if (EFI_ERROR (Status)) {
+    goto FreeVirtioFs;
+  }
+
+  VirtioFs->SimpleFs.Revision   = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
+  VirtioFs->SimpleFs.OpenVolume = VirtioFsOpenVolume;
+
+  Status = gBS->InstallProtocolInterface (&ControllerHandle,
+                  &gEfiSimpleFileSystemProtocolGuid, EFI_NATIVE_INTERFACE,
+                  &VirtioFs->SimpleFs);
+  if (EFI_ERROR (Status)) {
+    goto CloseVirtio;
+  }
+
+  return EFI_SUCCESS;
+
+CloseVirtio:
+  CloseStatus = gBS->CloseProtocol (ControllerHandle,
+                       &gVirtioDeviceProtocolGuid, This->DriverBindingHandle,
+                       ControllerHandle);
+  ASSERT_EFI_ERROR (CloseStatus);
+
+FreeVirtioFs:
+  FreePool (VirtioFs);
+
+  return Status;
 }
 
 EFI_STATUS
 EFIAPI
 VirtioFsBindingStop (
   IN EFI_DRIVER_BINDING_PROTOCOL *This,
   IN EFI_HANDLE                  ControllerHandle,
   IN UINTN                       NumberOfChildren,
   IN EFI_HANDLE                  *ChildHandleBuffer OPTIONAL
   )
 {
-  return EFI_DEVICE_ERROR;
+  EFI_STATUS                      Status;
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFs;
+  VIRTIO_FS                       *VirtioFs;
+
+  Status = gBS->OpenProtocol (ControllerHandle,
+                  &gEfiSimpleFileSystemProtocolGuid, (VOID **)&SimpleFs,
+                  This->DriverBindingHandle, ControllerHandle,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  VirtioFs = VIRTIO_FS_FROM_SIMPLE_FS (SimpleFs);
+
+  Status = gBS->UninstallProtocolInterface (ControllerHandle,
+                  &gEfiSimpleFileSystemProtocolGuid, SimpleFs);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->CloseProtocol (ControllerHandle, &gVirtioDeviceProtocolGuid,
+                  This->DriverBindingHandle, ControllerHandle);
+  ASSERT_EFI_ERROR (Status);
+
+  FreePool (VirtioFs);
+
+  return EFI_SUCCESS;
 }
 
 EFI_STATUS
 EFIAPI
 VirtioFsGetDriverName (
   IN  EFI_COMPONENT_NAME2_PROTOCOL *This,
   IN  CHAR8                        *Language,
   OUT CHAR16                       **DriverName
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c b/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
new file mode 100644
index 000000000000..a5a66a27d84c
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
@@ -0,0 +1,26 @@
+/** @file
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume() member function for the Virtio
+  Filesystem driver.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+/**
+  Open the root directory on the Virtio Filesystem.
+
+  Refer to EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_OPEN_VOLUME for the interface
+  contract.
+**/
+EFI_STATUS
+EFIAPI
+VirtioFsOpenVolume (
+  IN  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
+  OUT EFI_FILE_PROTOCOL               **Root
+  )
+{
+  return EFI_NO_MEDIA;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 04/48] OvmfPkg/VirtioFsDxe: implement virtio device (un)initialization
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (2 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 03/48] OvmfPkg/VirtioFsDxe: DriverBinding: open VirtioDevice, install SimpleFs Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 05/48] OvmfPkg/VirtioFsDxe: add a scatter-gather list data type Laszlo Ersek
                   ` (44 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Add the VirtioFsInit(), VirtioFsUninit(), and VirtioFsExitBoot()
functions.

In VirtioFsInit():

- Verify the host-side config of the virtio-fs device.

- Save the filesystem label ("tag") for later, from the configuration area
  of the virtio-fs device.

- Save the virtio queue size for later as well.

- Set up the virtio ring for sending requests.

In VirtioFsUninit():

- Reset the device.

- Tear down the virtio ring.

In VirtioFsExitBoot():

- Reset the device.

With this patch, the UEFI connect / disconnect controller operations
involve virtio setup / teardown; they are visible in the virtio-fs
daemon's log file. The virtiofsd log also confirms the device reset in
VirtioFsExitBoot(), when an OS is booted while the virtio-fs device is
bound.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  52 ++++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   2 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |  35 +++
 OvmfPkg/VirtioFsDxe/DriverBinding.c         |  26 +-
 OvmfPkg/VirtioFsDxe/Helpers.c               | 299 ++++++++++++++++++++
 5 files changed, 412 insertions(+), 2 deletions(-)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
new file mode 100644
index 000000000000..ea7d80d15d0b
--- /dev/null
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -0,0 +1,52 @@
+/** @file
+  Type and macro definitions specific to the Virtio Filesystem device.
+
+  At the time of this writing, the latest released Virtio specification (v1.1)
+  does not include the virtio-fs device. The development version of the
+  specification defines it however; see the latest version at
+  <https://github.com/oasis-tcs/virtio-spec/blob/87fa6b5d8155/virtio-fs.tex>.
+
+  This header file is minimal, and only defines the types and macros that are
+  necessary for the OvmfPkg implementation.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef VIRTIO_FS_H_
+#define VIRTIO_FS_H_
+
+#include <IndustryStandard/Virtio.h>
+
+//
+// Lowest numbered queue for sending normal priority requests.
+//
+#define VIRTIO_FS_REQUEST_QUEUE 1
+
+//
+// Number of bytes in the "VIRTIO_FS_CONFIG.Tag" field.
+//
+#define VIRTIO_FS_TAG_BYTES 36
+
+//
+// Device configuration layout.
+//
+#pragma pack (1)
+typedef struct {
+  //
+  // The Tag field can be considered the filesystem label, or a mount point
+  // hint. It is UTF-8 encoded, and padded to full size with NUL bytes. If the
+  // encoded bytes take up the entire Tag field, then there is no NUL
+  // terminator.
+  //
+  UINT8 Tag[VIRTIO_FS_TAG_BYTES];
+  //
+  // The total number of request virtqueues exposed by the device (i.e.,
+  // excluding the "hiprio" queue).
+  //
+  UINT32 NumReqQueues;
+} VIRTIO_FS_CONFIG;
+#pragma pack ()
+
+#endif // VIRTIO_FS_H_
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index ff9b1c6178bc..f6eebdb6bc7c 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -77,23 +77,25 @@ [Defines]
   ENTRY_POINT                           = VirtioFsEntryPoint
 
 [Packages]
   MdePkg/MdePkg.dec
   OvmfPkg/OvmfPkg.dec
 
 [Sources]
   DriverBinding.c
+  Helpers.c
   SimpleFsOpenVolume.c
   VirtioFsDxe.h
 
 [LibraryClasses]
   BaseLib
   DebugLib
   MemoryAllocationLib
   UefiBootServicesTableLib
   UefiDriverEntryPoint
+  VirtioLib
 
 [Protocols]
   gEfiComponentName2ProtocolGuid        ## PRODUCES
   gEfiDriverBindingProtocolGuid         ## PRODUCES
   gEfiSimpleFileSystemProtocolGuid      ## BY_START
   gVirtioDeviceProtocolGuid             ## TO_START
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 287defd21f23..2aae96ecd79a 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -6,42 +6,77 @@
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
 #ifndef VIRTIO_FS_DXE_H_
 #define VIRTIO_FS_DXE_H_
 
 #include <Base.h>                      // SIGNATURE_64()
+#include <IndustryStandard/VirtioFs.h> // VIRTIO_FS_TAG_BYTES
 #include <Library/DebugLib.h>          // CR()
 #include <Protocol/SimpleFileSystem.h> // EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
 #include <Protocol/VirtioDevice.h>     // VIRTIO_DEVICE_PROTOCOL
+#include <Uefi/UefiBaseType.h>         // EFI_EVENT
 
 #define VIRTIO_FS_SIG SIGNATURE_64 ('V', 'I', 'R', 'T', 'I', 'O', 'F', 'S')
 
+//
+// Filesystem label encoded in UCS-2, transformed from the UTF-8 representation
+// in "VIRTIO_FS_CONFIG.Tag", and NUL-terminated. Only the printable ASCII code
+// points (U+0020 through U+007E) are supported.
+//
+typedef CHAR16 VIRTIO_FS_LABEL[VIRTIO_FS_TAG_BYTES + 1];
+
 //
 // Main context structure, expressing an EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
 // interface on top of the Virtio Filesystem device.
 //
 typedef struct {
   //
   // Parts of this structure are initialized / torn down in various functions
   // at various call depths. The table to the right should make it easier to
   // track them.
   //
   //                              field         init function       init depth
   //                              -----------   ------------------  ----------
   UINT64                          Signature; // DriverBindingStart  0
   VIRTIO_DEVICE_PROTOCOL          *Virtio;   // DriverBindingStart  0
+  VIRTIO_FS_LABEL                 Label;     // VirtioFsInit        1
+  UINT16                          QueueSize; // VirtioFsInit        1
+  VRING                           Ring;      // VirtioRingInit      2
+  VOID                            *RingMap;  // VirtioRingMap       2
+  EFI_EVENT                       ExitBoot;  // DriverBindingStart  0
   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL SimpleFs;  // DriverBindingStart  0
 } VIRTIO_FS;
 
 #define VIRTIO_FS_FROM_SIMPLE_FS(SimpleFsReference) \
   CR (SimpleFsReference, VIRTIO_FS, SimpleFs, VIRTIO_FS_SIG);
 
+//
+// Initialization and helper routines for the Virtio Filesystem device.
+//
+
+EFI_STATUS
+VirtioFsInit (
+  IN OUT VIRTIO_FS *VirtioFs
+  );
+
+VOID
+VirtioFsUninit (
+  IN OUT VIRTIO_FS *VirtioFs
+  );
+
+VOID
+EFIAPI
+VirtioFsExitBoot (
+  IN EFI_EVENT ExitBootEvent,
+  IN VOID      *VirtioFsAsVoid
+  );
+
 //
 // EFI_SIMPLE_FILE_SYSTEM_PROTOCOL member functions for the Virtio Filesystem
 // driver.
 //
 
 EFI_STATUS
 EFIAPI
 VirtioFsOpenVolume (
diff --git a/OvmfPkg/VirtioFsDxe/DriverBinding.c b/OvmfPkg/VirtioFsDxe/DriverBinding.c
index 65e45b5c4bf7..b888158a805d 100644
--- a/OvmfPkg/VirtioFsDxe/DriverBinding.c
+++ b/OvmfPkg/VirtioFsDxe/DriverBinding.c
@@ -1,17 +1,16 @@
 /** @file
   Provide EFI_SIMPLE_FILE_SYSTEM_PROTOCOL instances on virtio-fs devices.
 
   Copyright (C) 2020, Red Hat, Inc.
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
-#include <IndustryStandard/Virtio.h>          // VIRTIO_SUBSYSTEM_FILESYSTEM
 #include <Library/BaseLib.h>                  // AsciiStrCmp()
 #include <Library/MemoryAllocationLib.h>      // AllocatePool()
 #include <Library/UefiBootServicesTableLib.h> // gBS
 #include <Protocol/ComponentName2.h>          // EFI_COMPONENT_NAME2_PROTOCOL
 #include <Protocol/DriverBinding.h>           // EFI_DRIVER_BINDING_PROTOCOL
 
 #include "VirtioFsDxe.h"
 
@@ -75,28 +74,46 @@ VirtioFsBindingStart (
 
   Status = gBS->OpenProtocol (ControllerHandle, &gVirtioDeviceProtocolGuid,
                   (VOID **)&VirtioFs->Virtio, This->DriverBindingHandle,
                   ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
   if (EFI_ERROR (Status)) {
     goto FreeVirtioFs;
   }
 
+  Status = VirtioFsInit (VirtioFs);
+  if (EFI_ERROR (Status)) {
+    goto CloseVirtio;
+  }
+
+  Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
+                  VirtioFsExitBoot, VirtioFs, &VirtioFs->ExitBoot);
+  if (EFI_ERROR (Status)) {
+    goto UninitVirtioFs;
+  }
+
   VirtioFs->SimpleFs.Revision   = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
   VirtioFs->SimpleFs.OpenVolume = VirtioFsOpenVolume;
 
   Status = gBS->InstallProtocolInterface (&ControllerHandle,
                   &gEfiSimpleFileSystemProtocolGuid, EFI_NATIVE_INTERFACE,
                   &VirtioFs->SimpleFs);
   if (EFI_ERROR (Status)) {
-    goto CloseVirtio;
+    goto CloseExitBoot;
   }
 
   return EFI_SUCCESS;
 
+CloseExitBoot:
+  CloseStatus = gBS->CloseEvent (VirtioFs->ExitBoot);
+  ASSERT_EFI_ERROR (CloseStatus);
+
+UninitVirtioFs:
+  VirtioFsUninit (VirtioFs);
+
 CloseVirtio:
   CloseStatus = gBS->CloseProtocol (ControllerHandle,
                        &gVirtioDeviceProtocolGuid, This->DriverBindingHandle,
                        ControllerHandle);
   ASSERT_EFI_ERROR (CloseStatus);
 
 FreeVirtioFs:
   FreePool (VirtioFs);
@@ -128,16 +145,21 @@ VirtioFsBindingStop (
   VirtioFs = VIRTIO_FS_FROM_SIMPLE_FS (SimpleFs);
 
   Status = gBS->UninstallProtocolInterface (ControllerHandle,
                   &gEfiSimpleFileSystemProtocolGuid, SimpleFs);
   if (EFI_ERROR (Status)) {
     return Status;
   }
 
+  Status = gBS->CloseEvent (VirtioFs->ExitBoot);
+  ASSERT_EFI_ERROR (Status);
+
+  VirtioFsUninit (VirtioFs);
+
   Status = gBS->CloseProtocol (ControllerHandle, &gVirtioDeviceProtocolGuid,
                   This->DriverBindingHandle, ControllerHandle);
   ASSERT_EFI_ERROR (Status);
 
   FreePool (VirtioFs);
 
   return EFI_SUCCESS;
 }
diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
new file mode 100644
index 000000000000..7b4906c54184
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/Helpers.c
@@ -0,0 +1,299 @@
+/** @file
+  Initialization and helper routines for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/VirtioLib.h>           // Virtio10WriteFeatures()
+
+#include "VirtioFsDxe.h"
+
+/**
+  Read the Virtio Filesystem device configuration structure in full.
+
+  @param[in] Virtio   The Virtio protocol underlying the VIRTIO_FS object.
+
+  @param[out] Config  The fully populated VIRTIO_FS_CONFIG structure.
+
+  @retval EFI_SUCCESS  Config has been filled in.
+
+  @return              Error codes propagated from Virtio->ReadDevice(). The
+                       contents of Config are indeterminate.
+**/
+STATIC
+EFI_STATUS
+VirtioFsReadConfig (
+  IN  VIRTIO_DEVICE_PROTOCOL *Virtio,
+  OUT VIRTIO_FS_CONFIG       *Config
+  )
+{
+  UINTN      Idx;
+  EFI_STATUS Status;
+
+  for (Idx = 0; Idx < VIRTIO_FS_TAG_BYTES; Idx++) {
+    Status = Virtio->ReadDevice (
+                       Virtio,                                 // This
+                       OFFSET_OF (VIRTIO_FS_CONFIG, Tag[Idx]), // FieldOffset
+                       sizeof Config->Tag[Idx],                // FieldSize
+                       sizeof Config->Tag[Idx],                // BufferSize
+                       &Config->Tag[Idx]                       // Buffer
+                       );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  Status = Virtio->ReadDevice (
+                     Virtio,                                     // This
+                     OFFSET_OF (VIRTIO_FS_CONFIG, NumReqQueues), // FieldOffset
+                     sizeof Config->NumReqQueues,                // FieldSize
+                     sizeof Config->NumReqQueues,                // BufferSize
+                     &Config->NumReqQueues                       // Buffer
+                     );
+  return Status;
+}
+
+/**
+  Configure the Virtio Filesystem device underlying VirtioFs.
+
+  @param[in,out] VirtioFs  The VIRTIO_FS object for which Virtio communication
+                           should be set up. On input, the caller is
+                           responsible for VirtioFs->Virtio having been
+                           initialized. On output, synchronous Virtio
+                           Filesystem commands (primitives) may be submitted to
+                           the device.
+
+  @retval EFI_SUCCESS      Virtio machinery has been set up.
+
+  @retval EFI_UNSUPPORTED  The host-side configuration of the Virtio Filesystem
+                           is not supported by this driver.
+
+  @return                  Error codes from underlying functions.
+**/
+EFI_STATUS
+VirtioFsInit (
+  IN OUT VIRTIO_FS *VirtioFs
+  )
+{
+  UINT8            NextDevStat;
+  EFI_STATUS       Status;
+  UINT64           Features;
+  VIRTIO_FS_CONFIG Config;
+  UINTN            Idx;
+  UINT64           RingBaseShift;
+
+  //
+  // Execute virtio-v1.1-cs01-87fa6b5d8155, 3.1.1 Driver Requirements: Device
+  // Initialization.
+  //
+  // 1. Reset the device.
+  //
+  NextDevStat = 0;
+  Status = VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
+  if (EFI_ERROR (Status)) {
+    goto Failed;
+  }
+
+  //
+  // 2. Set the ACKNOWLEDGE status bit [...]
+  //
+  NextDevStat |= VSTAT_ACK;
+  Status = VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
+  if (EFI_ERROR (Status)) {
+    goto Failed;
+  }
+
+  //
+  // 3. Set the DRIVER status bit [...]
+  //
+  NextDevStat |= VSTAT_DRIVER;
+  Status = VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
+  if (EFI_ERROR (Status)) {
+    goto Failed;
+  }
+
+  //
+  // 4. Read device feature bits...
+  //
+  Status = VirtioFs->Virtio->GetDeviceFeatures (VirtioFs->Virtio, &Features);
+  if (EFI_ERROR (Status)) {
+    goto Failed;
+  }
+  if ((Features & VIRTIO_F_VERSION_1) == 0) {
+    Status = EFI_UNSUPPORTED;
+    goto Failed;
+  }
+  //
+  // No device-specific feature bits have been defined in file "virtio-fs.tex"
+  // of the virtio spec at <https://github.com/oasis-tcs/virtio-spec.git>, as
+  // of commit 87fa6b5d8155.
+  //
+  Features &= VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM;
+
+  //
+  // ... and write the subset of feature bits understood by the [...] driver to
+  // the device. [...]
+  // 5. Set the FEATURES_OK status bit.
+  // 6. Re-read device status to ensure the FEATURES_OK bit is still set [...]
+  //
+  Status = Virtio10WriteFeatures (VirtioFs->Virtio, Features, &NextDevStat);
+  if (EFI_ERROR (Status)) {
+    goto Failed;
+  }
+
+  //
+  // 7. Perform device-specific setup, including discovery of virtqueues for
+  // the device, [...] reading [...] the device's virtio configuration space
+  //
+  Status = VirtioFsReadConfig (VirtioFs->Virtio, &Config);
+  if (EFI_ERROR (Status)) {
+    goto Failed;
+  }
+
+  //
+  // 7.a. Convert the filesystem label from UTF-8 to UCS-2. Only labels with
+  // printable ASCII code points (U+0020 through U+007E) are supported.
+  // NUL-terminate at either the terminator we find, or right after the
+  // original label.
+  //
+  for (Idx = 0; Idx < VIRTIO_FS_TAG_BYTES && Config.Tag[Idx] != '\0'; Idx++) {
+    if (Config.Tag[Idx] < 0x20 || Config.Tag[Idx] > 0x7E) {
+      Status = EFI_UNSUPPORTED;
+      goto Failed;
+    }
+    VirtioFs->Label[Idx] = Config.Tag[Idx];
+  }
+  VirtioFs->Label[Idx] = L'\0';
+
+  //
+  // 7.b. We need one queue for sending normal priority requests.
+  //
+  if (Config.NumReqQueues < 1) {
+    Status = EFI_UNSUPPORTED;
+    goto Failed;
+  }
+
+  //
+  // 7.c. Fetch and remember the number of descriptors we can place on the
+  // queue at once. We'll need two descriptors per request, as a minimum --
+  // request header, response header.
+  //
+  Status = VirtioFs->Virtio->SetQueueSel (VirtioFs->Virtio,
+                               VIRTIO_FS_REQUEST_QUEUE);
+  if (EFI_ERROR (Status)) {
+    goto Failed;
+  }
+  Status = VirtioFs->Virtio->GetQueueNumMax (VirtioFs->Virtio,
+                               &VirtioFs->QueueSize);
+  if (EFI_ERROR (Status)) {
+    goto Failed;
+  }
+  if (VirtioFs->QueueSize < 2) {
+    Status = EFI_UNSUPPORTED;
+    goto Failed;
+  }
+
+  //
+  // 7.d. [...] population of virtqueues [...]
+  //
+  Status = VirtioRingInit (VirtioFs->Virtio, VirtioFs->QueueSize,
+             &VirtioFs->Ring);
+  if (EFI_ERROR (Status)) {
+    goto Failed;
+  }
+
+  Status = VirtioRingMap (VirtioFs->Virtio, &VirtioFs->Ring, &RingBaseShift,
+             &VirtioFs->RingMap);
+  if (EFI_ERROR (Status)) {
+    goto ReleaseQueue;
+  }
+
+  Status = VirtioFs->Virtio->SetQueueAddress (VirtioFs->Virtio,
+                               &VirtioFs->Ring, RingBaseShift);
+  if (EFI_ERROR (Status)) {
+    goto UnmapQueue;
+  }
+
+  //
+  // 8. Set the DRIVER_OK status bit.
+  //
+  NextDevStat |= VSTAT_DRIVER_OK;
+  Status = VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
+  if (EFI_ERROR (Status)) {
+    goto UnmapQueue;
+  }
+
+  return EFI_SUCCESS;
+
+UnmapQueue:
+  VirtioFs->Virtio->UnmapSharedBuffer (VirtioFs->Virtio, VirtioFs->RingMap);
+
+ReleaseQueue:
+  VirtioRingUninit (VirtioFs->Virtio, &VirtioFs->Ring);
+
+Failed:
+  //
+  // If any of these steps go irrecoverably wrong, the driver SHOULD set the
+  // FAILED status bit to indicate that it has given up on the device (it can
+  // reset the device later to restart if desired). [...]
+  //
+  // Virtio access failure here should not mask the original error.
+  //
+  NextDevStat |= VSTAT_FAILED;
+  VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
+
+  return Status;
+}
+
+/**
+  De-configure the Virtio Filesystem device underlying VirtioFs.
+
+  @param[in] VirtioFs  The VIRTIO_FS object for which Virtio communication
+                       should be torn down. On input, the caller is responsible
+                       for having called VirtioFsInit(). On output, Virtio
+                       Filesystem commands (primitives) must no longer be
+                       submitted to the device.
+**/
+VOID
+VirtioFsUninit (
+  IN OUT VIRTIO_FS *VirtioFs
+  )
+{
+  //
+  // Resetting the Virtio device makes it release its resources and forget its
+  // configuration.
+  //
+  VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, 0);
+  VirtioFs->Virtio->UnmapSharedBuffer (VirtioFs->Virtio, VirtioFs->RingMap);
+  VirtioRingUninit (VirtioFs->Virtio, &VirtioFs->Ring);
+}
+
+/**
+  ExitBootServices event notification function for a Virtio Filesystem object.
+
+  This function resets the VIRTIO_FS.Virtio device, causing it to release all
+  references to guest-side resources. The function may only be called after
+  VirtioFsInit() returns successfully and before VirtioFsUninit() is called.
+
+  @param[in] ExitBootEvent   The VIRTIO_FS.ExitBoot event that has been
+                             signaled.
+
+  @param[in] VirtioFsAsVoid  Pointer to the VIRTIO_FS object, passed in as
+                             (VOID*).
+**/
+VOID
+EFIAPI
+VirtioFsExitBoot (
+  IN EFI_EVENT ExitBootEvent,
+  IN VOID      *VirtioFsAsVoid
+  )
+{
+  VIRTIO_FS *VirtioFs;
+
+  VirtioFs = VirtioFsAsVoid;
+  DEBUG ((DEBUG_VERBOSE, "%a: VirtioFs=0x%p Label=\"%s\"\n", __FUNCTION__,
+    VirtioFsAsVoid, VirtioFs->Label));
+  VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, 0);
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 05/48] OvmfPkg/VirtioFsDxe: add a scatter-gather list data type
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (3 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 04/48] OvmfPkg/VirtioFsDxe: implement virtio device (un)initialization Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 06/48] OvmfPkg/VirtioFsDxe: introduce the basic FUSE request/response headers Laszlo Ersek
                   ` (43 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

In preparation for the variously structured FUSE request/response
exchanges that virtio-fs uses, introduce a scatter-gather list data type.
This will let us express FUSE request-response pairs flexibly.

Add a function for validating whether a (request buffer list, response
buffer list) pair is well-formed, and supported by the Virtio Filesystem
device's queue depth.

Add another function for mapping and submitting a validated pair of
scatter-gather lists to the Virtio Filesystem device.

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>
---
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h |  60 +++
 OvmfPkg/VirtioFsDxe/Helpers.c     | 401 ++++++++++++++++++++
 2 files changed, 461 insertions(+)

diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 2aae96ecd79a..12acbd6dc359 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -46,16 +46,62 @@ typedef struct {
   VOID                            *RingMap;  // VirtioRingMap       2
   EFI_EVENT                       ExitBoot;  // DriverBindingStart  0
   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL SimpleFs;  // DriverBindingStart  0
 } VIRTIO_FS;
 
 #define VIRTIO_FS_FROM_SIMPLE_FS(SimpleFsReference) \
   CR (SimpleFsReference, VIRTIO_FS, SimpleFs, VIRTIO_FS_SIG);
 
+//
+// Structure for describing a contiguous buffer, potentially mapped for Virtio
+// transfer.
+//
+typedef struct {
+  //
+  // The following fields originate from the owner of the buffer.
+  //
+  VOID  *Buffer;
+  UINTN Size;
+  //
+  // All of the fields below, until the end of the structure, are
+  // zero-initialized when the structure is initially validated.
+  //
+  // Mapped, MappedAddress and Mapping are updated when the buffer is mapped
+  // for VirtioOperationBusMasterRead or VirtioOperationBusMasterWrite. They
+  // are again updated when the buffer is unmapped.
+  //
+  BOOLEAN              Mapped;
+  EFI_PHYSICAL_ADDRESS MappedAddress;
+  VOID                 *Mapping;
+  //
+  // Transferred is updated after VirtioFlush() returns successfully:
+  // - for VirtioOperationBusMasterRead, Transferred is set to Size;
+  // - for VirtioOperationBusMasterWrite, Transferred is calculated from the
+  //   UsedLen output parameter of VirtioFlush().
+  //
+  UINTN Transferred;
+} VIRTIO_FS_IO_VECTOR;
+
+//
+// Structure for describing a list of IO Vectors.
+//
+typedef struct {
+  //
+  // The following fields originate from the owner of the buffers.
+  //
+  VIRTIO_FS_IO_VECTOR *IoVec;
+  UINTN               NumVec;
+  //
+  // TotalSize is calculated when the scatter-gather list is initially
+  // validated.
+  //
+  UINT32 TotalSize;
+} VIRTIO_FS_SCATTER_GATHER_LIST;
+
 //
 // Initialization and helper routines for the Virtio Filesystem device.
 //
 
 EFI_STATUS
 VirtioFsInit (
   IN OUT VIRTIO_FS *VirtioFs
   );
@@ -67,16 +113,30 @@ VirtioFsUninit (
 
 VOID
 EFIAPI
 VirtioFsExitBoot (
   IN EFI_EVENT ExitBootEvent,
   IN VOID      *VirtioFsAsVoid
   );
 
+EFI_STATUS
+VirtioFsSgListsValidate (
+  IN     VIRTIO_FS                     *VirtioFs,
+  IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *RequestSgList,
+  IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList OPTIONAL
+  );
+
+EFI_STATUS
+VirtioFsSgListsSubmit (
+  IN OUT VIRTIO_FS                     *VirtioFs,
+  IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *RequestSgList,
+  IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList OPTIONAL
+  );
+
 //
 // EFI_SIMPLE_FILE_SYSTEM_PROTOCOL member functions for the Virtio Filesystem
 // driver.
 //
 
 EFI_STATUS
 EFIAPI
 VirtioFsOpenVolume (
diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
index 7b4906c54184..88264d4b264c 100644
--- a/OvmfPkg/VirtioFsDxe/Helpers.c
+++ b/OvmfPkg/VirtioFsDxe/Helpers.c
@@ -292,8 +292,409 @@ VirtioFsExitBoot (
 {
   VIRTIO_FS *VirtioFs;
 
   VirtioFs = VirtioFsAsVoid;
   DEBUG ((DEBUG_VERBOSE, "%a: VirtioFs=0x%p Label=\"%s\"\n", __FUNCTION__,
     VirtioFsAsVoid, VirtioFs->Label));
   VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, 0);
 }
+
+/**
+  Validate two VIRTIO_FS_SCATTER_GATHER_LIST objects -- list of request
+  buffers, list of response buffers -- together.
+
+  On input, the caller is required to populate the following fields:
+  - VIRTIO_FS_IO_VECTOR.Buffer,
+  - VIRTIO_FS_IO_VECTOR.Size,
+  - VIRTIO_FS_SCATTER_GATHER_LIST.IoVec,
+  - VIRTIO_FS_SCATTER_GATHER_LIST.NumVec.
+
+  On output (on successful return), the following fields will be
+  zero-initialized:
+  - VIRTIO_FS_IO_VECTOR.Mapped,
+  - VIRTIO_FS_IO_VECTOR.MappedAddress,
+  - VIRTIO_FS_IO_VECTOR.Mapping,
+  - VIRTIO_FS_IO_VECTOR.Transferred.
+
+  On output (on successful return), the following fields will be calculated:
+  - VIRTIO_FS_SCATTER_GATHER_LIST.TotalSize.
+
+  The function may only be called after VirtioFsInit() returns successfully and
+  before VirtioFsUninit() is called.
+
+  @param[in] VirtioFs            The Virtio Filesystem device that the
+                                 request-response exchange, expressed via
+                                 RequestSgList and ResponseSgList, will be
+                                 submitted to.
+
+  @param[in,out] RequestSgList   The scatter-gather list that describes the
+                                 request part of the exchange -- the buffers
+                                 that should be sent to the Virtio Filesystem
+                                 device in the virtio transfer.
+
+  @param[in,out] ResponseSgList  The scatter-gather list that describes the
+                                 response part of the exchange -- the buffers
+                                 that the Virtio Filesystem device should
+                                 populate in the virtio transfer. May be NULL
+                                 if the exchange with the Virtio Filesystem
+                                 device consists of a request only, with the
+                                 response part omitted altogether.
+
+  @retval EFI_SUCCESS            RequestSgList and ResponseSgList have been
+                                 validated, output fields have been set.
+
+  @retval EFI_INVALID_PARAMETER  RequestSgList is NULL.
+
+  @retval EFI_INVALID_PARAMETER  On input, a
+                                 VIRTIO_FS_SCATTER_GATHER_LIST.IoVec field is
+                                 NULL, or a
+                                 VIRTIO_FS_SCATTER_GATHER_LIST.NumVec field is
+                                 zero.
+
+  @retval EFI_INVALID_PARAMETER  On input, a VIRTIO_FS_IO_VECTOR.Buffer field
+                                 is NULL, or a VIRTIO_FS_IO_VECTOR.Size field
+                                 is zero.
+
+  @retval EFI_UNSUPPORTED        (RequestSgList->NumVec +
+                                 ResponseSgList->NumVec) exceeds
+                                 VirtioFs->QueueSize, meaning that the total
+                                 list of buffers cannot be placed on the virtio
+                                 queue in a single descriptor chain (with one
+                                 descriptor per buffer).
+
+  @retval EFI_UNSUPPORTED        One of the
+                                 VIRTIO_FS_SCATTER_GATHER_LIST.TotalSize fields
+                                 would exceed MAX_UINT32.
+**/
+EFI_STATUS
+VirtioFsSgListsValidate (
+  IN     VIRTIO_FS                     *VirtioFs,
+  IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *RequestSgList,
+  IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList OPTIONAL
+  )
+{
+  VIRTIO_FS_SCATTER_GATHER_LIST *SgListParam[2];
+  UINT16                        DescriptorsNeeded;
+  UINTN                         ListId;
+
+  if (RequestSgList == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  SgListParam[0] = RequestSgList;
+  SgListParam[1] = ResponseSgList;
+
+  DescriptorsNeeded = 0;
+  for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {
+    VIRTIO_FS_SCATTER_GATHER_LIST *SgList;
+    UINT32                        SgListTotalSize;
+    UINTN                         IoVecIdx;
+
+    SgList = SgListParam[ListId];
+    if (SgList == NULL) {
+      continue;
+    }
+    //
+    // Sanity-check SgList -- it must provide at least one IO Vector.
+    //
+    if (SgList->IoVec == NULL || SgList->NumVec == 0) {
+      return EFI_INVALID_PARAMETER;
+    }
+    //
+    // Make sure that, for each IO Vector in this SgList, a virtio descriptor
+    // can be added to the virtio queue, after the other descriptors added
+    // previously.
+    //
+    if (SgList->NumVec > MAX_UINT16 - DescriptorsNeeded ||
+        DescriptorsNeeded + SgList->NumVec > VirtioFs->QueueSize) {
+      return EFI_UNSUPPORTED;
+    }
+    DescriptorsNeeded += (UINT16)SgList->NumVec;
+
+    SgListTotalSize = 0;
+    for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {
+      VIRTIO_FS_IO_VECTOR *IoVec;
+
+      IoVec = &SgList->IoVec[IoVecIdx];
+      //
+      // Sanity-check this IoVec -- it must describe a non-empty buffer.
+      //
+      if (IoVec->Buffer == NULL || IoVec->Size == 0) {
+        return EFI_INVALID_PARAMETER;
+      }
+      //
+      // Make sure the cumulative size of all IO Vectors in this SgList remains
+      // expressible as a UINT32.
+      //
+      if (IoVec->Size > MAX_UINT32 - SgListTotalSize) {
+        return EFI_UNSUPPORTED;
+      }
+      SgListTotalSize += (UINT32)IoVec->Size;
+
+      //
+      // Initialize those fields in this IO Vector that will be updated in
+      // relation to mapping / transfer.
+      //
+      IoVec->Mapped        = FALSE;
+      IoVec->MappedAddress = 0;
+      IoVec->Mapping       = NULL;
+      IoVec->Transferred   = 0;
+    }
+
+    //
+    // Store the cumulative size of all IO Vectors that we have calculated in
+    // this SgList.
+    //
+    SgList->TotalSize = SgListTotalSize;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Submit a validated pair of (request buffer list, response buffer list) to the
+  Virtio Filesystem device.
+
+  On input, the pair of VIRTIO_FS_SCATTER_GATHER_LIST objects must have been
+  validated together, using the VirtioFsSgListsValidate() function.
+
+  On output (on successful return), the following fields will be re-initialized
+  to zero (after temporarily setting them to different values):
+  - VIRTIO_FS_IO_VECTOR.Mapped,
+  - VIRTIO_FS_IO_VECTOR.MappedAddress,
+  - VIRTIO_FS_IO_VECTOR.Mapping.
+
+  On output (on successful return), the following fields will be calculated:
+  - VIRTIO_FS_IO_VECTOR.Transferred.
+
+  The function may only be called after VirtioFsInit() returns successfully and
+  before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs        The Virtio Filesystem device that the
+                                 request-response exchange, expressed via
+                                 RequestSgList and ResponseSgList, should now
+                                 be submitted to.
+
+  @param[in,out] RequestSgList   The scatter-gather list that describes the
+                                 request part of the exchange -- the buffers
+                                 that should be sent to the Virtio Filesystem
+                                 device in the virtio transfer.
+
+  @param[in,out] ResponseSgList  The scatter-gather list that describes the
+                                 response part of the exchange -- the buffers
+                                 that the Virtio Filesystem device should
+                                 populate in the virtio transfer. May be NULL
+                                 if and only if NULL was passed to
+                                 VirtioFsSgListsValidate() as ResponseSgList.
+
+  @retval EFI_SUCCESS       Transfer complete. The caller should investigate
+                            the VIRTIO_FS_IO_VECTOR.Transferred fields in
+                            ResponseSgList, to ensure coverage of the relevant
+                            response buffers. Subsequently, the caller should
+                            investigate the contents of those buffers.
+
+  @retval EFI_DEVICE_ERROR  The Virtio Filesystem device reported populating
+                            more response bytes than ResponseSgList->TotalSize.
+
+  @return                   Error codes propagated from
+                            VirtioMapAllBytesInSharedBuffer(), VirtioFlush(),
+                            or VirtioFs->Virtio->UnmapSharedBuffer().
+**/
+EFI_STATUS
+VirtioFsSgListsSubmit (
+  IN OUT VIRTIO_FS                     *VirtioFs,
+  IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *RequestSgList,
+  IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList OPTIONAL
+  )
+{
+  VIRTIO_FS_SCATTER_GATHER_LIST *SgListParam[2];
+  VIRTIO_MAP_OPERATION          SgListVirtioMapOp[ARRAY_SIZE (SgListParam)];
+  UINT16                        SgListDescriptorFlag[ARRAY_SIZE (SgListParam)];
+  UINTN                         ListId;
+  VIRTIO_FS_SCATTER_GATHER_LIST *SgList;
+  UINTN                         IoVecIdx;
+  VIRTIO_FS_IO_VECTOR           *IoVec;
+  EFI_STATUS                    Status;
+  DESC_INDICES                  Indices;
+  UINT32                        TotalBytesWrittenByDevice;
+  UINT32                        BytesPermittedForWrite;
+
+  SgListParam[0]          = RequestSgList;
+  SgListVirtioMapOp[0]    = VirtioOperationBusMasterRead;
+  SgListDescriptorFlag[0] = 0;
+
+  SgListParam[1]          = ResponseSgList;
+  SgListVirtioMapOp[1]    = VirtioOperationBusMasterWrite;
+  SgListDescriptorFlag[1] = VRING_DESC_F_WRITE;
+
+  //
+  // Map all IO Vectors.
+  //
+  for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {
+    SgList = SgListParam[ListId];
+    if (SgList == NULL) {
+      continue;
+    }
+    for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {
+      IoVec = &SgList->IoVec[IoVecIdx];
+      //
+      // Map this IO Vector.
+      //
+      Status = VirtioMapAllBytesInSharedBuffer (
+                 VirtioFs->Virtio,
+                 SgListVirtioMapOp[ListId],
+                 IoVec->Buffer,
+                 IoVec->Size,
+                 &IoVec->MappedAddress,
+                 &IoVec->Mapping
+                 );
+      if (EFI_ERROR (Status)) {
+        goto Unmap;
+      }
+      IoVec->Mapped = TRUE;
+    }
+  }
+
+  //
+  // Compose the descriptor chain.
+  //
+  VirtioPrepare (&VirtioFs->Ring, &Indices);
+  for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {
+    SgList = SgListParam[ListId];
+    if (SgList == NULL) {
+      continue;
+    }
+    for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {
+      UINT16 NextFlag;
+
+      IoVec = &SgList->IoVec[IoVecIdx];
+      //
+      // Set VRING_DESC_F_NEXT on all except the very last descriptor.
+      //
+      NextFlag = VRING_DESC_F_NEXT;
+      if (ListId == ARRAY_SIZE (SgListParam) - 1 &&
+          IoVecIdx == SgList->NumVec - 1) {
+        NextFlag = 0;
+      }
+      VirtioAppendDesc (
+        &VirtioFs->Ring,
+        IoVec->MappedAddress,
+        (UINT32)IoVec->Size,
+        SgListDescriptorFlag[ListId] | NextFlag,
+        &Indices
+        );
+    }
+  }
+
+  //
+  // Submit the descriptor chain.
+  //
+  Status = VirtioFlush (VirtioFs->Virtio, VIRTIO_FS_REQUEST_QUEUE,
+             &VirtioFs->Ring, &Indices, &TotalBytesWrittenByDevice);
+  if (EFI_ERROR (Status)) {
+    goto Unmap;
+  }
+
+  //
+  // Sanity-check: the Virtio Filesystem device should not have written more
+  // bytes than what we offered buffers for.
+  //
+  if (ResponseSgList == NULL) {
+    BytesPermittedForWrite = 0;
+  } else {
+    BytesPermittedForWrite = ResponseSgList->TotalSize;
+  }
+  if (TotalBytesWrittenByDevice > BytesPermittedForWrite) {
+    Status = EFI_DEVICE_ERROR;
+    goto Unmap;
+  }
+
+  //
+  // Update the transfer sizes in the IO Vectors.
+  //
+  for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {
+    SgList = SgListParam[ListId];
+    if (SgList == NULL) {
+      continue;
+    }
+    for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {
+      IoVec = &SgList->IoVec[IoVecIdx];
+      if (SgListVirtioMapOp[ListId] == VirtioOperationBusMasterRead) {
+        //
+        // We report that the Virtio Filesystem device has read all buffers in
+        // the request.
+        //
+        IoVec->Transferred = IoVec->Size;
+      } else {
+        //
+        // Regarding the response, calculate how much of the current IO Vector
+        // has been populated by the Virtio Filesystem device. In
+        // "TotalBytesWrittenByDevice", VirtioFlush() reported the total count
+        // across all device-writeable descriptors, in the order they were
+        // chained on the ring.
+        //
+        IoVec->Transferred = MIN ((UINTN)TotalBytesWrittenByDevice,
+                               IoVec->Size);
+        TotalBytesWrittenByDevice -= (UINT32)IoVec->Transferred;
+      }
+    }
+  }
+
+  //
+  // By now, "TotalBytesWrittenByDevice" has been exhausted.
+  //
+  ASSERT (TotalBytesWrittenByDevice == 0);
+
+  //
+  // We've succeeded; fall through.
+  //
+Unmap:
+  //
+  // Unmap all mapped IO Vectors on both the success and the error paths. The
+  // unmapping occurs in reverse order of mapping, in an attempt to avoid
+  // memory fragmentation.
+  //
+  ListId = ARRAY_SIZE (SgListParam);
+  while (ListId > 0) {
+    --ListId;
+    SgList = SgListParam[ListId];
+    if (SgList == NULL) {
+      continue;
+    }
+    IoVecIdx = SgList->NumVec;
+    while (IoVecIdx > 0) {
+      EFI_STATUS UnmapStatus;
+
+      --IoVecIdx;
+      IoVec = &SgList->IoVec[IoVecIdx];
+      //
+      // Unmap this IO Vector, if it has been mapped.
+      //
+      if (!IoVec->Mapped) {
+        continue;
+      }
+      UnmapStatus = VirtioFs->Virtio->UnmapSharedBuffer (VirtioFs->Virtio,
+                                        IoVec->Mapping);
+      //
+      // Re-set the following fields to the values they initially got from
+      // VirtioFsSgListsValidate() -- the above unmapping attempt is considered
+      // final, even if it fails.
+      //
+      IoVec->Mapped        = FALSE;
+      IoVec->MappedAddress = 0;
+      IoVec->Mapping       = NULL;
+
+      //
+      // If we are on the success path, but the unmapping failed, we need to
+      // transparently flip to the failure path -- the caller must learn they
+      // should not consult the response buffers.
+      //
+      // The branch below can be taken at most once.
+      //
+      if (!EFI_ERROR (Status) && EFI_ERROR (UnmapStatus)) {
+        Status = UnmapStatus;
+      }
+    }
+  }
+
+  return Status;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 06/48] OvmfPkg/VirtioFsDxe: introduce the basic FUSE request/response headers
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (4 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 05/48] OvmfPkg/VirtioFsDxe: add a scatter-gather list data type Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-17 11:49   ` [Virtio-fs] " Dr. David Alan Gilbert
  2020-12-16 21:10 ` [edk2 PATCH 07/48] OvmfPkg/VirtioFsDxe: map "errno" values to EFI_STATUS Laszlo Ersek
                   ` (42 subsequent siblings)
  48 siblings, 1 reply; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Introduce the VIRTIO_FS_FUSE_REQUEST and VIRTIO_FS_FUSE_RESPONSE
structures, which are the common headers for the various FUSE
request/response structures.

Introduce the VirtioFsFuseNewRequest() helper function for populating
VIRTIO_FS_FUSE_REQUEST, from parameters and from a VIRTIO_FS-level request
counter.

Introduce the VirtioFsFuseCheckResponse() helper function for verifying
most FUSE response types that begin with the VIRTIO_FS_FUSE_RESPONSE
header.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  49 +++++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |  17 ++
 OvmfPkg/VirtioFsDxe/DriverBinding.c         |   5 +
 OvmfPkg/VirtioFsDxe/Helpers.c               | 216 ++++++++++++++++++++
 4 files changed, 287 insertions(+)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index ea7d80d15d0b..521288b03f1c 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -44,9 +44,58 @@ typedef struct {
   //
   // The total number of request virtqueues exposed by the device (i.e.,
   // excluding the "hiprio" queue).
   //
   UINT32 NumReqQueues;
 } VIRTIO_FS_CONFIG;
 #pragma pack ()
 
+//
+// FUSE-related definitions follow.
+//
+// From virtio-v1.1-cs01-87fa6b5d8155, 5.11 File System Device: "[...] The
+// driver acts as the FUSE client mounting the file system. The virtio file
+// system device provides the mechanism for transporting FUSE requests [...]"
+//
+// Unfortunately, the documentation of the FUSE wire protocol is lacking. The
+// Virtio spec (as of this writing) simply defers to
+// "include/uapi/linux/fuse.h" in the Linux kernel source -- see the reference
+// in virtio spec file "introduction.tex", at commit 87fa6b5d8155.
+//
+// Of course, "include/uapi/linux/fuse.h" is a moving target (the virtio spec
+// does not specify a particular FUSE interface version). The OvmfPkg code
+// targets version 7.31, because that's the lowest version that the QEMU
+// virtio-fs daemon supports at this time -- see QEMU commit 72c42e2d6551
+// ("virtiofsd: Trim out compatibility code", 2020-01-23).
+//
+// Correspondingly, Linux's "include/uapi/linux/fuse.h" is consulted as checked
+// out at commit (c6ff213fe5b8^) = d78092e4937d ("fuse: fix page dereference
+// after free", 2020-09-18); that is, right before commit c6ff213fe5b8 ("fuse:
+// add submount support to <uapi/linux/fuse.h>", 2020-09-18) introduces FUSE
+// interface version 7.32.
+//
+#define VIRTIO_FS_FUSE_MAJOR  7
+#define VIRTIO_FS_FUSE_MINOR 31
+
+#pragma pack (1)
+//
+// Request-response headers common to all request types.
+//
+typedef struct {
+  UINT32 Len;
+  UINT32 Opcode;
+  UINT64 Unique;
+  UINT64 NodeId;
+  UINT32 Uid;
+  UINT32 Gid;
+  UINT32 Pid;
+  UINT32 Padding;
+} VIRTIO_FS_FUSE_REQUEST;
+
+typedef struct {
+  UINT32 Len;
+  INT32  Error;
+  UINT64 Unique;
+} VIRTIO_FS_FUSE_RESPONSE;
+#pragma pack ()
+
 #endif // VIRTIO_FS_H_
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 12acbd6dc359..f7eae9a4b71a 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -39,16 +39,17 @@ typedef struct {
   //                              field         init function       init depth
   //                              -----------   ------------------  ----------
   UINT64                          Signature; // DriverBindingStart  0
   VIRTIO_DEVICE_PROTOCOL          *Virtio;   // DriverBindingStart  0
   VIRTIO_FS_LABEL                 Label;     // VirtioFsInit        1
   UINT16                          QueueSize; // VirtioFsInit        1
   VRING                           Ring;      // VirtioRingInit      2
   VOID                            *RingMap;  // VirtioRingMap       2
+  UINT64                          RequestId; // DriverBindingStart  0
   EFI_EVENT                       ExitBoot;  // DriverBindingStart  0
   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL SimpleFs;  // DriverBindingStart  0
 } VIRTIO_FS;
 
 #define VIRTIO_FS_FROM_SIMPLE_FS(SimpleFsReference) \
   CR (SimpleFsReference, VIRTIO_FS, SimpleFs, VIRTIO_FS_SIG);
 
 //
@@ -127,16 +128,32 @@ VirtioFsSgListsValidate (
 
 EFI_STATUS
 VirtioFsSgListsSubmit (
   IN OUT VIRTIO_FS                     *VirtioFs,
   IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *RequestSgList,
   IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList OPTIONAL
   );
 
+EFI_STATUS
+VirtioFsFuseNewRequest (
+  IN OUT VIRTIO_FS              *VirtioFs,
+     OUT VIRTIO_FS_FUSE_REQUEST *Request,
+  IN     UINT32                 RequestSize,
+  IN     UINT32                 Opcode,
+  IN     UINT64                 NodeId
+  );
+
+EFI_STATUS
+VirtioFsFuseCheckResponse (
+  IN  VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList,
+  IN  UINT64                        RequestId,
+  OUT UINTN                         *TailBufferFill
+  );
+
 //
 // EFI_SIMPLE_FILE_SYSTEM_PROTOCOL member functions for the Virtio Filesystem
 // driver.
 //
 
 EFI_STATUS
 EFIAPI
 VirtioFsOpenVolume (
diff --git a/OvmfPkg/VirtioFsDxe/DriverBinding.c b/OvmfPkg/VirtioFsDxe/DriverBinding.c
index b888158a805d..4a2787a50a6e 100644
--- a/OvmfPkg/VirtioFsDxe/DriverBinding.c
+++ b/OvmfPkg/VirtioFsDxe/DriverBinding.c
@@ -79,16 +79,21 @@ VirtioFsBindingStart (
     goto FreeVirtioFs;
   }
 
   Status = VirtioFsInit (VirtioFs);
   if (EFI_ERROR (Status)) {
     goto CloseVirtio;
   }
 
+  //
+  // Initialize the FUSE request counter.
+  //
+  VirtioFs->RequestId = 1;
+
   Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
                   VirtioFsExitBoot, VirtioFs, &VirtioFs->ExitBoot);
   if (EFI_ERROR (Status)) {
     goto UninitVirtioFs;
   }
 
   VirtioFs->SimpleFs.Revision   = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
   VirtioFs->SimpleFs.OpenVolume = VirtioFsOpenVolume;
diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
index 88264d4b264c..5bd2dc641f6d 100644
--- a/OvmfPkg/VirtioFsDxe/Helpers.c
+++ b/OvmfPkg/VirtioFsDxe/Helpers.c
@@ -693,8 +693,224 @@ VirtioFsSgListsSubmit (
       if (!EFI_ERROR (Status) && EFI_ERROR (UnmapStatus)) {
         Status = UnmapStatus;
       }
     }
   }
 
   return Status;
 }
+
+/**
+  Set up the fields of a new VIRTIO_FS_FUSE_REQUEST object.
+
+  The function may only be called after VirtioFsInit() returns successfully and
+  before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs The Virtio Filesystem device that the request is
+                          being prepared for. The "VirtioFs->RequestId" field
+                          will be copied into "Request->Unique". On output (on
+                          successful return), "VirtioFs->RequestId" will be
+                          incremented.
+
+  @param[out] Request     The VIRTIO_FS_FUSE_REQUEST object whose fields are to
+                          be set.
+
+  @param[in] RequestSize  The total size of the request, including
+                          sizeof(VIRTIO_FS_FUSE_REQUEST).
+
+  @param[in] Opcode       The VIRTIO_FS_FUSE_OPCODE that identifies the command
+                          to send.
+
+  @param[in] NodeId       The inode number of the file that the request refers
+                          to.
+
+  @retval EFI_INVALID_PARAMETER  RequestSize is smaller than
+                                 sizeof(VIRTIO_FS_FUSE_REQUEST).
+
+  @retval EFI_OUT_OF_RESOURCES   "VirtioFs->RequestId" is MAX_UINT64, and can
+                                 be incremented no more.
+
+  @retval EFI_SUCCESS            Request has been populated,
+                                 "VirtioFs->RequestId" has been incremented.
+**/
+EFI_STATUS
+VirtioFsFuseNewRequest (
+  IN OUT VIRTIO_FS              *VirtioFs,
+     OUT VIRTIO_FS_FUSE_REQUEST *Request,
+  IN     UINT32                 RequestSize,
+  IN     UINT32                 Opcode,
+  IN     UINT64                 NodeId
+  )
+{
+  if (RequestSize < sizeof *Request) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (VirtioFs->RequestId == MAX_UINT64) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Request->Len     = RequestSize;
+  Request->Opcode  = Opcode;
+  Request->Unique  = VirtioFs->RequestId++;
+  Request->NodeId  = NodeId;
+  Request->Uid     = 0;
+  Request->Gid     = 0;
+  Request->Pid     = 1;
+  Request->Padding = 0;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Check the common FUSE response format.
+
+  The first buffer in the response scatter-gather list is assumed a
+  VIRTIO_FS_FUSE_RESPONSE structure. Subsequent response buffers, if any, up to
+  and excluding the last one, are assumed fixed size. The last response buffer
+  may or may not be fixed size, as specified by the caller.
+
+  This function may only be called after VirtioFsSgListsSubmit() returns
+  successfully.
+
+  @param[in] ResponseSgList   The scatter-gather list that describes the
+                              response part of the exchange -- the buffers that
+                              the Virtio Filesystem device filled in during the
+                              virtio transfer.
+
+  @param[in] RequestId        The request identifier to which the response is
+                              expected to belong.
+
+  @param[out] TailBufferFill  If NULL, then the last buffer in ResponseSgList
+                              is considered fixed size. Otherwise, the last
+                              buffer is considered variable size, and on
+                              successful return, TailBufferFill reports the
+                              number of bytes in the last buffer.
+
+  @retval EFI_INVALID_PARAMETER  TailBufferFill is not NULL (i.e., the last
+                                 buffer is considered variable size), and
+                                 ResponseSgList->NumVec is 1.
+
+  @retval EFI_INVALID_PARAMETER  The allocated size of the first buffer does
+                                 not match sizeof(VIRTIO_FS_FUSE_RESPONSE).
+
+  @retval EFI_PROTOCOL_ERROR     The VIRTIO_FS_FUSE_RESPONSE structure in the
+                                 first buffer has not been fully populated.
+
+  @retval EFI_PROTOCOL_ERROR     "VIRTIO_FS_FUSE_RESPONSE.Len" in the first
+                                 buffer does not equal the sum of the
+                                 individual buffer sizes (as populated).
+
+  @retval EFI_PROTOCOL_ERROR     "VIRTIO_FS_FUSE_RESPONSE.Unique" in the first
+                                 buffer does not equal RequestId.
+
+  @retval EFI_PROTOCOL_ERROR     "VIRTIO_FS_FUSE_RESPONSE.Error" in the first
+                                 buffer is zero, but a subsequent fixed size
+                                 buffer has not been fully populated.
+
+  @retval EFI_DEVICE_ERROR       "VIRTIO_FS_FUSE_RESPONSE.Error" in the first
+                                 buffer is nonzero. The caller may investigate
+                                 "VIRTIO_FS_FUSE_RESPONSE.Error". Note that the
+                                 completeness of the subsequent fixed size
+                                 buffers is not verified in this case.
+
+  @retval EFI_SUCCESS            Verification successful.
+**/
+EFI_STATUS
+VirtioFsFuseCheckResponse (
+  IN  VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList,
+  IN  UINT64                        RequestId,
+  OUT UINTN                         *TailBufferFill
+  )
+{
+  UINTN                   NumFixedSizeVec;
+  VIRTIO_FS_FUSE_RESPONSE *CommonResp;
+  UINT32                  TotalTransferred;
+  UINTN                   Idx;
+
+  //
+  // Ensured by VirtioFsSgListsValidate().
+  //
+  ASSERT (ResponseSgList->NumVec > 0);
+
+  if (TailBufferFill == NULL) {
+    //
+    // All buffers are considered fixed size.
+    //
+    NumFixedSizeVec = ResponseSgList->NumVec;
+  } else {
+    //
+    // If the last buffer is variable size, then we need that buffer to be
+    // different from the first buffer, which is considered a
+    // VIRTIO_FS_FUSE_RESPONSE (fixed size) structure.
+    //
+    if (ResponseSgList->NumVec == 1) {
+      return EFI_INVALID_PARAMETER;
+    }
+    NumFixedSizeVec = ResponseSgList->NumVec - 1;
+  }
+
+  //
+  // The first buffer is supposed to carry a (fully populated)
+  // VIRTIO_FS_FUSE_RESPONSE structure.
+  //
+  if (ResponseSgList->IoVec[0].Size != sizeof *CommonResp) {
+    return EFI_INVALID_PARAMETER;
+  }
+  if (ResponseSgList->IoVec[0].Transferred != ResponseSgList->IoVec[0].Size) {
+    return EFI_PROTOCOL_ERROR;
+  }
+
+  //
+  // FUSE must report the same number of bytes, written by the Virtio
+  // Filesystem device, as the virtio transport does.
+  //
+  CommonResp = ResponseSgList->IoVec[0].Buffer;
+  TotalTransferred = 0;
+  for (Idx = 0; Idx < ResponseSgList->NumVec; Idx++) {
+    //
+    // Integer overflow and truncation are not possible, based on
+    // VirtioFsSgListsValidate() and VirtioFsSgListsSubmit().
+    //
+    TotalTransferred += (UINT32)ResponseSgList->IoVec[Idx].Transferred;
+  }
+  if (CommonResp->Len != TotalTransferred) {
+    return EFI_PROTOCOL_ERROR;
+  }
+
+  //
+  // Enforce that FUSE match our request ID in the response.
+  //
+  if (CommonResp->Unique != RequestId) {
+    return EFI_PROTOCOL_ERROR;
+  }
+
+  //
+  // If there is an explicit error report, skip checking the transfer
+  // counts for the rest of the fixed size buffers.
+  //
+  if (CommonResp->Error != 0) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  //
+  // There was no error reported, so we require that the Virtio Filesystem
+  // device populate all fixed size buffers. We checked this for the very first
+  // buffer above; let's check the rest (if any).
+  //
+  ASSERT (NumFixedSizeVec >= 1);
+  for (Idx = 1; Idx < NumFixedSizeVec; Idx++) {
+    if (ResponseSgList->IoVec[Idx].Transferred !=
+        ResponseSgList->IoVec[Idx].Size) {
+      return EFI_PROTOCOL_ERROR;
+    }
+  }
+
+  //
+  // If the last buffer is considered variable size, report its filled size.
+  //
+  if (TailBufferFill != NULL) {
+    *TailBufferFill = ResponseSgList->IoVec[NumFixedSizeVec].Transferred;
+  }
+
+  return EFI_SUCCESS;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 07/48] OvmfPkg/VirtioFsDxe: map "errno" values to EFI_STATUS
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (5 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 06/48] OvmfPkg/VirtioFsDxe: introduce the basic FUSE request/response headers Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 08/48] OvmfPkg/VirtioFsDxe: submit the FUSE_INIT request to the device Laszlo Ersek
                   ` (41 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

The VirtioFsFuseCheckResponse() function dedicates the EFI_DEVICE_ERROR
status code to the case when the Virtio Filesystem device explicitly
returns an error via the "VIRTIO_FS_FUSE_RESPONSE.Error" field.

Said field effectively carries a Linux "errno" value. Introduce a helper
function for mapping "errno" values to (hopefully) reasonable EFI_STATUS
codes. This way we'll be able to propagate "errno" values as EFI_STATUS
return codes along the UEFI call stack -- in some detail anyway.

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>
---
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h |   5 +
 OvmfPkg/VirtioFsDxe/Helpers.c     | 200 ++++++++++++++++++++
 2 files changed, 205 insertions(+)

diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index f7eae9a4b71a..772ab743cc8e 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -144,16 +144,21 @@ VirtioFsFuseNewRequest (
 
 EFI_STATUS
 VirtioFsFuseCheckResponse (
   IN  VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList,
   IN  UINT64                        RequestId,
   OUT UINTN                         *TailBufferFill
   );
 
+EFI_STATUS
+VirtioFsErrnoToEfiStatus (
+  IN INT32 Errno
+  );
+
 //
 // EFI_SIMPLE_FILE_SYSTEM_PROTOCOL member functions for the Virtio Filesystem
 // driver.
 //
 
 EFI_STATUS
 EFIAPI
 VirtioFsOpenVolume (
diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
index 5bd2dc641f6d..334fa6c7dd26 100644
--- a/OvmfPkg/VirtioFsDxe/Helpers.c
+++ b/OvmfPkg/VirtioFsDxe/Helpers.c
@@ -909,8 +909,208 @@ VirtioFsFuseCheckResponse (
   // If the last buffer is considered variable size, report its filled size.
   //
   if (TailBufferFill != NULL) {
     *TailBufferFill = ResponseSgList->IoVec[NumFixedSizeVec].Transferred;
   }
 
   return EFI_SUCCESS;
 }
+
+/**
+  An ad-hoc function for mapping FUSE (well, Linux) "errno" values to
+  EFI_STATUS.
+
+  @param[in] Errno  The "VIRTIO_FS_FUSE_RESPONSE.Error" value, returned by the
+                    Virtio Filesystem device. The value is expected to be
+                    negative.
+
+  @return                   An EFI_STATUS error code that's deemed a passable
+                            mapping for the Errno value.
+
+  @retval EFI_DEVICE_ERROR  Fallback EFI_STATUS code for unrecognized Errno
+                            values.
+**/
+EFI_STATUS
+VirtioFsErrnoToEfiStatus (
+  IN INT32 Errno
+  )
+{
+  switch (Errno) {
+  case   -1: // EPERM               Operation not permitted
+    return EFI_SECURITY_VIOLATION;
+
+  case   -2: // ENOENT              No such file or directory
+  case   -3: // ESRCH               No such process
+  case   -6: // ENXIO               No such device or address
+  case  -10: // ECHILD              No child processes
+  case  -19: // ENODEV              No such device
+  case  -49: // EUNATCH             Protocol driver not attached
+  case  -65: // ENOPKG              Package not installed
+  case  -79: // ELIBACC             Can not access a needed shared library
+  case -126: // ENOKEY              Required key not available
+    return EFI_NOT_FOUND;
+
+  case   -4: // EINTR               Interrupted system call
+  case  -11: // EAGAIN, EWOULDBLOCK Resource temporarily unavailable
+  case  -16: // EBUSY               Device or resource busy
+  case  -26: // ETXTBSY             Text file busy
+  case  -35: // EDEADLK, EDEADLOCK  Resource deadlock avoided
+  case  -39: // ENOTEMPTY           Directory not empty
+  case  -42: // ENOMSG              No message of desired type
+  case  -61: // ENODATA             No data available
+  case  -85: // ERESTART            Interrupted system call should be restarted
+    return EFI_NOT_READY;
+
+  case   -5: // EIO                 Input/output error
+  case  -45: // EL2NSYNC            Level 2 not synchronized
+  case  -46: // EL3HLT              Level 3 halted
+  case  -47: // EL3RST              Level 3 reset
+  case  -51: // EL2HLT              Level 2 halted
+  case -121: // EREMOTEIO           Remote I/O error
+  case -133: // EHWPOISON           Memory page has hardware error
+    return EFI_DEVICE_ERROR;
+
+  case   -7: // E2BIG               Argument list too long
+  case  -36: // ENAMETOOLONG        File name too long
+  case  -90: // EMSGSIZE            Message too long
+    return EFI_BAD_BUFFER_SIZE;
+
+  case   -8: // ENOEXEC             Exec format error
+  case  -15: // ENOTBLK             Block device required
+  case  -18: // EXDEV               Invalid cross-device link
+  case  -20: // ENOTDIR             Not a directory
+  case  -21: // EISDIR              Is a directory
+  case  -25: // ENOTTY              Inappropriate ioctl for device
+  case  -27: // EFBIG               File too large
+  case  -29: // ESPIPE              Illegal seek
+  case  -38: // ENOSYS              Function not implemented
+  case  -59: // EBFONT              Bad font file format
+  case  -60: // ENOSTR              Device not a stream
+  case  -83: // ELIBEXEC            Cannot exec a shared library directly
+  case  -88: // ENOTSOCK            Socket operation on non-socket
+  case  -91: // EPROTOTYPE          Protocol wrong type for socket
+  case  -92: // ENOPROTOOPT         Protocol not available
+  case  -93: // EPROTONOSUPPORT     Protocol not supported
+  case  -94: // ESOCKTNOSUPPORT     Socket type not supported
+  case  -95: // ENOTSUP, EOPNOTSUPP Operation not supported
+  case  -96: // EPFNOSUPPORT        Protocol family not supported
+  case  -97: // EAFNOSUPPORT        Address family not supported by protocol
+  case  -99: // EADDRNOTAVAIL       Cannot assign requested address
+  case -118: // ENOTNAM             Not a XENIX named type file
+  case -120: // EISNAM              Is a named type file
+  case -124: // EMEDIUMTYPE         Wrong medium type
+    return EFI_UNSUPPORTED;
+
+  case   -9: // EBADF               Bad file descriptor
+  case  -14: // EFAULT              Bad address
+  case  -44: // ECHRNG              Channel number out of range
+  case  -48: // ELNRNG              Link number out of range
+  case  -53: // EBADR               Invalid request descriptor
+  case  -56: // EBADRQC             Invalid request code
+  case  -57: // EBADSLT             Invalid slot
+  case  -76: // ENOTUNIQ            Name not unique on network
+  case  -84: // EILSEQ        Invalid or incomplete multibyte or wide character
+    return EFI_NO_MAPPING;
+
+  case  -12: // ENOMEM              Cannot allocate memory
+  case  -23: // ENFILE              Too many open files in system
+  case  -24: // EMFILE              Too many open files
+  case  -31: // EMLINK              Too many links
+  case  -37: // ENOLCK              No locks available
+  case  -40: // ELOOP               Too many levels of symbolic links
+  case  -50: // ENOCSI              No CSI structure available
+  case  -55: // ENOANO              No anode
+  case  -63: // ENOSR               Out of streams resources
+  case  -82: // ELIBMAX         Attempting to link in too many shared libraries
+  case  -87: // EUSERS              Too many users
+  case -105: // ENOBUFS             No buffer space available
+  case -109: // ETOOMANYREFS        Too many references: cannot splice
+  case -119: // ENAVAIL             No XENIX semaphores available
+  case -122: // EDQUOT              Disk quota exceeded
+    return EFI_OUT_OF_RESOURCES;
+
+  case  -13: // EACCES              Permission denied
+    return EFI_ACCESS_DENIED;
+
+  case  -17: // EEXIST              File exists
+  case  -98: // EADDRINUSE          Address already in use
+  case -106: // EISCONN             Transport endpoint is already connected
+  case -114: // EALREADY            Operation already in progress
+  case -115: // EINPROGRESS         Operation now in progress
+    return EFI_ALREADY_STARTED;
+
+  case  -22: // EINVAL              Invalid argument
+  case  -33: // EDOM                Numerical argument out of domain
+    return EFI_INVALID_PARAMETER;
+
+  case  -28: // ENOSPC              No space left on device
+  case  -54: // EXFULL              Exchange full
+    return EFI_VOLUME_FULL;
+
+  case  -30: // EROFS               Read-only file system
+    return EFI_WRITE_PROTECTED;
+
+  case  -32: // EPIPE               Broken pipe
+  case  -43: // EIDRM               Identifier removed
+  case  -67: // ENOLINK             Link has been severed
+  case  -68: // EADV                Advertise error
+  case  -69: // ESRMNT              Srmount error
+  case  -70: // ECOMM               Communication error on send
+  case  -73: // EDOTDOT             RFS specific error
+  case  -78: // EREMCHG             Remote address changed
+  case  -86: // ESTRPIPE            Streams pipe error
+  case -102: // ENETRESET           Network dropped connection on reset
+  case -103: // ECONNABORTED        Software caused connection abort
+  case -104: // ECONNRESET          Connection reset by peer
+  case -116: // ESTALE              Stale file handle
+  case -125: // ECANCELED           Operation canceled
+  case -128: // EKEYREVOKED         Key has been revoked
+  case -129: // EKEYREJECTED        Key was rejected by service
+  case -130: // EOWNERDEAD          Owner died
+  case -131: // ENOTRECOVERABLE     State not recoverable
+    return EFI_ABORTED;
+
+  case  -34: // ERANGE              Numerical result out of range
+  case  -75: // EOVERFLOW           Value too large for defined data type
+    return EFI_BUFFER_TOO_SMALL;
+
+  case  -52: // EBADE               Invalid exchange
+  case -108: // ESHUTDOWN         Cannot send after transport endpoint shutdown
+  case -111: // ECONNREFUSED        Connection refused
+    return EFI_END_OF_FILE;
+
+  case  -62: // ETIME               Timer expired
+  case -110: // ETIMEDOUT           Connection timed out
+  case -127: // EKEYEXPIRED         Key has expired
+    return EFI_TIMEOUT;
+
+  case  -64: // ENONET              Machine is not on the network
+  case  -66: // EREMOTE             Object is remote
+  case  -72: // EMULTIHOP           Multihop attempted
+  case -100: // ENETDOWN            Network is down
+  case -101: // ENETUNREACH         Network is unreachable
+  case -112: // EHOSTDOWN           Host is down
+  case -113: // EHOSTUNREACH        No route to host
+  case -123: // ENOMEDIUM           No medium found
+  case -132: // ERFKILL             Operation not possible due to RF-kill
+    return EFI_NO_MEDIA;
+
+  case  -71: // EPROTO              Protocol error
+    return EFI_PROTOCOL_ERROR;
+
+  case  -74: // EBADMSG             Bad message
+  case  -77: // EBADFD              File descriptor in bad state
+  case  -80: // ELIBBAD             Accessing a corrupted shared library
+  case  -81: // ELIBSCN             .lib section in a.out corrupted
+  case -117: // EUCLEAN             Structure needs cleaning
+    return EFI_VOLUME_CORRUPTED;
+
+  case  -89: // EDESTADDRREQ        Destination address required
+  case -107: // ENOTCONN            Transport endpoint is not connected
+    return EFI_NOT_STARTED;
+
+  default:
+    break;
+  }
+
+  return EFI_DEVICE_ERROR;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 08/48] OvmfPkg/VirtioFsDxe: submit the FUSE_INIT request to the device
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (6 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 07/48] OvmfPkg/VirtioFsDxe: map "errno" values to EFI_STATUS Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 09/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_OPENDIR Laszlo Ersek
                   ` (40 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Submit the FUSE_INIT request to the Virtio Filesystem device, for starting
the FUSE session.

The FUSE_INIT request is logged by the virtio-fs daemon, with this patch
applied, when (for example) using the "CONNECT" UEFI shell command.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  31 +++++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |  13 +-
 OvmfPkg/VirtioFsDxe/DriverBinding.c         |   8 +-
 OvmfPkg/VirtioFsDxe/FuseInit.c              | 132 ++++++++++++++++++++
 OvmfPkg/VirtioFsDxe/Helpers.c               |   5 +-
 6 files changed, 182 insertions(+), 8 deletions(-)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index 521288b03f1c..006e0f5debcb 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -71,16 +71,23 @@ typedef struct {
 // out at commit (c6ff213fe5b8^) = d78092e4937d ("fuse: fix page dereference
 // after free", 2020-09-18); that is, right before commit c6ff213fe5b8 ("fuse:
 // add submount support to <uapi/linux/fuse.h>", 2020-09-18) introduces FUSE
 // interface version 7.32.
 //
 #define VIRTIO_FS_FUSE_MAJOR  7
 #define VIRTIO_FS_FUSE_MINOR 31
 
+//
+// FUSE operation codes.
+//
+typedef enum {
+  VirtioFsFuseOpInit        = 26,
+} VIRTIO_FS_FUSE_OPCODE;
+
 #pragma pack (1)
 //
 // Request-response headers common to all request types.
 //
 typedef struct {
   UINT32 Len;
   UINT32 Opcode;
   UINT64 Unique;
@@ -91,11 +98,35 @@ typedef struct {
   UINT32 Padding;
 } VIRTIO_FS_FUSE_REQUEST;
 
 typedef struct {
   UINT32 Len;
   INT32  Error;
   UINT64 Unique;
 } VIRTIO_FS_FUSE_RESPONSE;
+
+//
+// Headers for VirtioFsFuseOpInit.
+//
+typedef struct {
+  UINT32 Major;
+  UINT32 Minor;
+  UINT32 MaxReadahead;
+  UINT32 Flags;
+} VIRTIO_FS_FUSE_INIT_REQUEST;
+
+typedef struct {
+  UINT32 Major;
+  UINT32 Minor;
+  UINT32 MaxReadahead;
+  UINT32 Flags;
+  UINT16 MaxBackground;
+  UINT16 CongestionThreshold;
+  UINT32 MaxWrite;
+  UINT32 TimeGran;
+  UINT16 MaxPages;
+  UINT16 MapAlignment;
+  UINT32 Unused[8];
+} VIRTIO_FS_FUSE_INIT_RESPONSE;
 #pragma pack ()
 
 #endif // VIRTIO_FS_H_
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index f6eebdb6bc7c..8fddc50318f1 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -77,16 +77,17 @@ [Defines]
   ENTRY_POINT                           = VirtioFsEntryPoint
 
 [Packages]
   MdePkg/MdePkg.dec
   OvmfPkg/OvmfPkg.dec
 
 [Sources]
   DriverBinding.c
+  FuseInit.c
   Helpers.c
   SimpleFsOpenVolume.c
   VirtioFsDxe.h
 
 [LibraryClasses]
   BaseLib
   DebugLib
   MemoryAllocationLib
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 772ab743cc8e..b8d464011843 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -39,17 +39,17 @@ typedef struct {
   //                              field         init function       init depth
   //                              -----------   ------------------  ----------
   UINT64                          Signature; // DriverBindingStart  0
   VIRTIO_DEVICE_PROTOCOL          *Virtio;   // DriverBindingStart  0
   VIRTIO_FS_LABEL                 Label;     // VirtioFsInit        1
   UINT16                          QueueSize; // VirtioFsInit        1
   VRING                           Ring;      // VirtioRingInit      2
   VOID                            *RingMap;  // VirtioRingMap       2
-  UINT64                          RequestId; // DriverBindingStart  0
+  UINT64                          RequestId; // FuseInitSession     1
   EFI_EVENT                       ExitBoot;  // DriverBindingStart  0
   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL SimpleFs;  // DriverBindingStart  0
 } VIRTIO_FS;
 
 #define VIRTIO_FS_FROM_SIMPLE_FS(SimpleFsReference) \
   CR (SimpleFsReference, VIRTIO_FS, SimpleFs, VIRTIO_FS_SIG);
 
 //
@@ -133,32 +133,41 @@ VirtioFsSgListsSubmit (
   IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList OPTIONAL
   );
 
 EFI_STATUS
 VirtioFsFuseNewRequest (
   IN OUT VIRTIO_FS              *VirtioFs,
      OUT VIRTIO_FS_FUSE_REQUEST *Request,
   IN     UINT32                 RequestSize,
-  IN     UINT32                 Opcode,
+  IN     VIRTIO_FS_FUSE_OPCODE  Opcode,
   IN     UINT64                 NodeId
   );
 
 EFI_STATUS
 VirtioFsFuseCheckResponse (
   IN  VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList,
   IN  UINT64                        RequestId,
   OUT UINTN                         *TailBufferFill
   );
 
 EFI_STATUS
 VirtioFsErrnoToEfiStatus (
   IN INT32 Errno
   );
 
+//
+// Wrapper functions for FUSE commands (primitives).
+//
+
+EFI_STATUS
+VirtioFsFuseInitSession (
+  IN OUT VIRTIO_FS *VirtioFs
+  );
+
 //
 // EFI_SIMPLE_FILE_SYSTEM_PROTOCOL member functions for the Virtio Filesystem
 // driver.
 //
 
 EFI_STATUS
 EFIAPI
 VirtioFsOpenVolume (
diff --git a/OvmfPkg/VirtioFsDxe/DriverBinding.c b/OvmfPkg/VirtioFsDxe/DriverBinding.c
index 4a2787a50a6e..4f77ffaa9953 100644
--- a/OvmfPkg/VirtioFsDxe/DriverBinding.c
+++ b/OvmfPkg/VirtioFsDxe/DriverBinding.c
@@ -79,20 +79,20 @@ VirtioFsBindingStart (
     goto FreeVirtioFs;
   }
 
   Status = VirtioFsInit (VirtioFs);
   if (EFI_ERROR (Status)) {
     goto CloseVirtio;
   }
 
-  //
-  // Initialize the FUSE request counter.
-  //
-  VirtioFs->RequestId = 1;
+  Status = VirtioFsFuseInitSession (VirtioFs);
+  if (EFI_ERROR (Status)) {
+    goto UninitVirtioFs;
+  }
 
   Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
                   VirtioFsExitBoot, VirtioFs, &VirtioFs->ExitBoot);
   if (EFI_ERROR (Status)) {
     goto UninitVirtioFs;
   }
 
   VirtioFs->SimpleFs.Revision   = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
diff --git a/OvmfPkg/VirtioFsDxe/FuseInit.c b/OvmfPkg/VirtioFsDxe/FuseInit.c
new file mode 100644
index 000000000000..aa19dbdc05cb
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/FuseInit.c
@@ -0,0 +1,132 @@
+/** @file
+  FUSE_INIT wrapper for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+/**
+  Send a FUSE_INIT request to the Virtio Filesystem device, for starting the
+  FUSE session.
+
+  From virtio-v1.1-cs01-87fa6b5d8155, 5.11.5 Device Initialization: "On
+  initialization the driver first discovers the device's virtqueues. The FUSE
+  session is started by sending a FUSE_INIT request as defined by the FUSE
+  protocol on one request virtqueue."
+
+  The function may only be called after VirtioFsInit() returns successfully and
+  before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_INIT
+                           request to. The FUSE request counter
+                           "VirtioFs->RequestId" is set to 1 on output.
+
+  @retval EFI_SUCCESS      The FUSE session has been started.
+
+  @retval EFI_UNSUPPORTED  FUSE interface version negotiation failed.
+
+  @return                  The "errno" value mapped to an EFI_STATUS code, if
+                           the Virtio Filesystem device explicitly reported an
+                           error.
+
+  @return                  Error codes propagated from
+                           VirtioFsSgListsValidate(), VirtioFsFuseNewRequest(),
+                           VirtioFsSgListsSubmit(),
+                           VirtioFsFuseCheckResponse().
+**/
+EFI_STATUS
+VirtioFsFuseInitSession (
+  IN OUT VIRTIO_FS *VirtioFs
+  )
+{
+  VIRTIO_FS_FUSE_REQUEST        CommonReq;
+  VIRTIO_FS_FUSE_INIT_REQUEST   InitReq;
+  VIRTIO_FS_IO_VECTOR           ReqIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST ReqSgList;
+  VIRTIO_FS_FUSE_RESPONSE       CommonResp;
+  VIRTIO_FS_FUSE_INIT_RESPONSE  InitResp;
+  VIRTIO_FS_IO_VECTOR           RespIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST RespSgList;
+  EFI_STATUS                    Status;
+
+  //
+  // Initialize the FUSE request counter.
+  //
+  VirtioFs->RequestId = 1;
+
+  //
+  // Set up the scatter-gather lists.
+  //
+  ReqIoVec[0].Buffer = &CommonReq;
+  ReqIoVec[0].Size   = sizeof CommonReq;
+  ReqIoVec[1].Buffer = &InitReq;
+  ReqIoVec[1].Size   = sizeof InitReq;
+  ReqSgList.IoVec    = ReqIoVec;
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);
+
+  RespIoVec[0].Buffer = &CommonResp;
+  RespIoVec[0].Size   = sizeof CommonResp;
+  RespIoVec[1].Buffer = &InitResp;
+  RespIoVec[1].Size   = sizeof InitResp;
+  RespSgList.IoVec    = RespIoVec;
+  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);
+
+  //
+  // Validate the scatter-gather lists; calculate the total transfer sizes.
+  //
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the common request header.
+  //
+  Status = VirtioFsFuseNewRequest (VirtioFs, &CommonReq, ReqSgList.TotalSize,
+             VirtioFsFuseOpInit, 0);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the FUSE_INIT-specific fields.
+  //
+  InitReq.Major        = VIRTIO_FS_FUSE_MAJOR;
+  InitReq.Minor        = VIRTIO_FS_FUSE_MINOR;
+  InitReq.MaxReadahead = 0;
+  InitReq.Flags        = 0;
+
+  //
+  // Submit the request.
+  //
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Verify the response (all response buffers are fixed size).
+  //
+  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);
+  if (EFI_ERROR (Status)) {
+    if (Status == EFI_DEVICE_ERROR) {
+      DEBUG ((DEBUG_ERROR, "%a: Label=\"%s\" Errno=%d\n", __FUNCTION__,
+        VirtioFs->Label, CommonResp.Error));
+      Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
+    }
+    return Status;
+  }
+
+  //
+  // Check FUSE interface version compatibility.
+  //
+  if (InitResp.Major < InitReq.Major ||
+      (InitResp.Major == InitReq.Major && InitResp.Minor < InitReq.Minor)) {
+    return EFI_UNSUPPORTED;
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
index 334fa6c7dd26..00f762142746 100644
--- a/OvmfPkg/VirtioFsDxe/Helpers.c
+++ b/OvmfPkg/VirtioFsDxe/Helpers.c
@@ -716,33 +716,34 @@ VirtioFsSgListsSubmit (
 
   @param[in] RequestSize  The total size of the request, including
                           sizeof(VIRTIO_FS_FUSE_REQUEST).
 
   @param[in] Opcode       The VIRTIO_FS_FUSE_OPCODE that identifies the command
                           to send.
 
   @param[in] NodeId       The inode number of the file that the request refers
-                          to.
+                          to. When Opcode is VirtioFsFuseOpInit, NodeId is
+                          ignored by the Virtio Filesystem device.
 
   @retval EFI_INVALID_PARAMETER  RequestSize is smaller than
                                  sizeof(VIRTIO_FS_FUSE_REQUEST).
 
   @retval EFI_OUT_OF_RESOURCES   "VirtioFs->RequestId" is MAX_UINT64, and can
                                  be incremented no more.
 
   @retval EFI_SUCCESS            Request has been populated,
                                  "VirtioFs->RequestId" has been incremented.
 **/
 EFI_STATUS
 VirtioFsFuseNewRequest (
   IN OUT VIRTIO_FS              *VirtioFs,
      OUT VIRTIO_FS_FUSE_REQUEST *Request,
   IN     UINT32                 RequestSize,
-  IN     UINT32                 Opcode,
+  IN     VIRTIO_FS_FUSE_OPCODE  Opcode,
   IN     UINT64                 NodeId
   )
 {
   if (RequestSize < sizeof *Request) {
     return EFI_INVALID_PARAMETER;
   }
 
   if (VirtioFs->RequestId == MAX_UINT64) {
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 09/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_OPENDIR
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (7 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 08/48] OvmfPkg/VirtioFsDxe: submit the FUSE_INIT request to the device Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 10/48] OvmfPkg/VirtioFsDxe: add shared wrapper for FUSE_RELEASE / FUSE_RELEASEDIR Laszlo Ersek
                   ` (39 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Add the VirtioFsFuseOpenDir() function, for sending the FUSE_OPENDIR
command to the Virtio Filesystem device.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  15 +++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |   7 ++
 OvmfPkg/VirtioFsDxe/FuseOpenDir.c           | 120 ++++++++++++++++++++
 4 files changed, 143 insertions(+)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index 006e0f5debcb..c48105325515 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -76,16 +76,17 @@ typedef struct {
 #define VIRTIO_FS_FUSE_MAJOR  7
 #define VIRTIO_FS_FUSE_MINOR 31
 
 //
 // FUSE operation codes.
 //
 typedef enum {
   VirtioFsFuseOpInit        = 26,
+  VirtioFsFuseOpOpenDir     = 27,
 } VIRTIO_FS_FUSE_OPCODE;
 
 #pragma pack (1)
 //
 // Request-response headers common to all request types.
 //
 typedef struct {
   UINT32 Len;
@@ -122,11 +123,25 @@ typedef struct {
   UINT16 MaxBackground;
   UINT16 CongestionThreshold;
   UINT32 MaxWrite;
   UINT32 TimeGran;
   UINT16 MaxPages;
   UINT16 MapAlignment;
   UINT32 Unused[8];
 } VIRTIO_FS_FUSE_INIT_RESPONSE;
+
+//
+// Headers for VirtioFsFuseOpOpenDir.
+//
+typedef struct {
+  UINT32 Flags;
+  UINT32 Unused;
+} VIRTIO_FS_FUSE_OPEN_REQUEST;
+
+typedef struct {
+  UINT64 FileHandle;
+  UINT32 OpenFlags;
+  UINT32 Padding;
+} VIRTIO_FS_FUSE_OPEN_RESPONSE;
 #pragma pack ()
 
 #endif // VIRTIO_FS_H_
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index 8fddc50318f1..051acbdd7199 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -78,16 +78,17 @@ [Defines]
 
 [Packages]
   MdePkg/MdePkg.dec
   OvmfPkg/OvmfPkg.dec
 
 [Sources]
   DriverBinding.c
   FuseInit.c
+  FuseOpenDir.c
   Helpers.c
   SimpleFsOpenVolume.c
   VirtioFsDxe.h
 
 [LibraryClasses]
   BaseLib
   DebugLib
   MemoryAllocationLib
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index b8d464011843..9c47454435ae 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -158,16 +158,23 @@ VirtioFsErrnoToEfiStatus (
 // Wrapper functions for FUSE commands (primitives).
 //
 
 EFI_STATUS
 VirtioFsFuseInitSession (
   IN OUT VIRTIO_FS *VirtioFs
   );
 
+EFI_STATUS
+VirtioFsFuseOpenDir (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId,
+     OUT UINT64    *FuseHandle
+  );
+
 //
 // EFI_SIMPLE_FILE_SYSTEM_PROTOCOL member functions for the Virtio Filesystem
 // driver.
 //
 
 EFI_STATUS
 EFIAPI
 VirtioFsOpenVolume (
diff --git a/OvmfPkg/VirtioFsDxe/FuseOpenDir.c b/OvmfPkg/VirtioFsDxe/FuseOpenDir.c
new file mode 100644
index 000000000000..eef522693c3f
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/FuseOpenDir.c
@@ -0,0 +1,120 @@
+/** @file
+  FUSE_OPENDIR wrapper for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+/**
+  Send a FUSE_OPENDIR request to the Virtio Filesystem device, for opening a
+  directory.
+
+  The function may only be called after VirtioFsFuseInitSession() returns
+  successfully and before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device to send the
+                           FUSE_OPENDIR request to. On output, the FUSE request
+                           counter "VirtioFs->RequestId" will have been
+                           incremented.
+
+  @param[in] NodeId        The inode number of the directory to open.
+
+  @param[out] FuseHandle   The open file handle returned by the Virtio
+                           Filesystem device.
+
+  @retval EFI_SUCCESS  The directory has been opened.
+
+  @return              The "errno" value mapped to an EFI_STATUS code, if the
+                       Virtio Filesystem device explicitly reported an error.
+
+  @return              Error codes propagated from VirtioFsSgListsValidate(),
+                       VirtioFsFuseNewRequest(), VirtioFsSgListsSubmit(),
+                       VirtioFsFuseCheckResponse().
+**/
+EFI_STATUS
+VirtioFsFuseOpenDir (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId,
+     OUT UINT64    *FuseHandle
+  )
+{
+  VIRTIO_FS_FUSE_REQUEST        CommonReq;
+  VIRTIO_FS_FUSE_OPEN_REQUEST   OpenReq;
+  VIRTIO_FS_IO_VECTOR           ReqIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST ReqSgList;
+  VIRTIO_FS_FUSE_RESPONSE       CommonResp;
+  VIRTIO_FS_FUSE_OPEN_RESPONSE  OpenResp;
+  VIRTIO_FS_IO_VECTOR           RespIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST RespSgList;
+  EFI_STATUS                    Status;
+
+  //
+  // Set up the scatter-gather lists.
+  //
+  ReqIoVec[0].Buffer = &CommonReq;
+  ReqIoVec[0].Size   = sizeof CommonReq;
+  ReqIoVec[1].Buffer = &OpenReq;
+  ReqIoVec[1].Size   = sizeof OpenReq;
+  ReqSgList.IoVec    = ReqIoVec;
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);
+
+  RespIoVec[0].Buffer = &CommonResp;
+  RespIoVec[0].Size   = sizeof CommonResp;
+  RespIoVec[1].Buffer = &OpenResp;
+  RespIoVec[1].Size   = sizeof OpenResp;
+  RespSgList.IoVec    = RespIoVec;
+  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);
+
+  //
+  // Validate the scatter-gather lists; calculate the total transfer sizes.
+  //
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the common request header.
+  //
+  Status = VirtioFsFuseNewRequest (VirtioFs, &CommonReq, ReqSgList.TotalSize,
+             VirtioFsFuseOpOpenDir, NodeId);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the FUSE_OPENDIR-specific fields.
+  //
+  OpenReq.Flags  = 0;
+  OpenReq.Unused = 0;
+
+  //
+  // Submit the request.
+  //
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Verify the response (all response buffers are fixed size).
+  //
+  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);
+  if (EFI_ERROR (Status)) {
+    if (Status == EFI_DEVICE_ERROR) {
+      DEBUG ((DEBUG_ERROR, "%a: Label=\"%s\" NodeId=%Lu Errno=%d\n",
+        __FUNCTION__, VirtioFs->Label, NodeId, CommonResp.Error));
+      Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
+    }
+    return Status;
+  }
+
+  //
+  // Output the open file handle.
+  //
+  *FuseHandle = OpenResp.FileHandle;
+  return EFI_SUCCESS;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 10/48] OvmfPkg/VirtioFsDxe: add shared wrapper for FUSE_RELEASE / FUSE_RELEASEDIR
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (8 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 09/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_OPENDIR Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 11/48] OvmfPkg/VirtioFsDxe: implement EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume() Laszlo Ersek
                   ` (38 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

The FUSE_RELEASE and FUSE_RELEASEDIR commands only differ in the opcode.
Add a common function called VirtioFsFuseReleaseFileOrDir() for sending
either command.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  12 ++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |   8 ++
 OvmfPkg/VirtioFsDxe/FuseRelease.c           | 121 ++++++++++++++++++++
 4 files changed, 142 insertions(+)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index c48105325515..c17a43c160e3 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -75,18 +75,20 @@ typedef struct {
 //
 #define VIRTIO_FS_FUSE_MAJOR  7
 #define VIRTIO_FS_FUSE_MINOR 31
 
 //
 // FUSE operation codes.
 //
 typedef enum {
+  VirtioFsFuseOpRelease     = 18,
   VirtioFsFuseOpInit        = 26,
   VirtioFsFuseOpOpenDir     = 27,
+  VirtioFsFuseOpReleaseDir  = 29,
 } VIRTIO_FS_FUSE_OPCODE;
 
 #pragma pack (1)
 //
 // Request-response headers common to all request types.
 //
 typedef struct {
   UINT32 Len;
@@ -100,16 +102,26 @@ typedef struct {
 } VIRTIO_FS_FUSE_REQUEST;
 
 typedef struct {
   UINT32 Len;
   INT32  Error;
   UINT64 Unique;
 } VIRTIO_FS_FUSE_RESPONSE;
 
+//
+// Header for VirtioFsFuseOpRelease and VirtioFsFuseOpReleaseDir.
+//
+typedef struct {
+  UINT64 FileHandle;
+  UINT32 Flags;
+  UINT32 ReleaseFlags;
+  UINT64 LockOwner;
+} VIRTIO_FS_FUSE_RELEASE_REQUEST;
+
 //
 // Headers for VirtioFsFuseOpInit.
 //
 typedef struct {
   UINT32 Major;
   UINT32 Minor;
   UINT32 MaxReadahead;
   UINT32 Flags;
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index 051acbdd7199..95b1a5a8f60a 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -79,16 +79,17 @@ [Defines]
 [Packages]
   MdePkg/MdePkg.dec
   OvmfPkg/OvmfPkg.dec
 
 [Sources]
   DriverBinding.c
   FuseInit.c
   FuseOpenDir.c
+  FuseRelease.c
   Helpers.c
   SimpleFsOpenVolume.c
   VirtioFsDxe.h
 
 [LibraryClasses]
   BaseLib
   DebugLib
   MemoryAllocationLib
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 9c47454435ae..a99625d0473a 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -153,16 +153,24 @@ EFI_STATUS
 VirtioFsErrnoToEfiStatus (
   IN INT32 Errno
   );
 
 //
 // Wrapper functions for FUSE commands (primitives).
 //
 
+EFI_STATUS
+VirtioFsFuseReleaseFileOrDir (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId,
+  IN     UINT64    FuseHandle,
+  IN     BOOLEAN   IsDir
+  );
+
 EFI_STATUS
 VirtioFsFuseInitSession (
   IN OUT VIRTIO_FS *VirtioFs
   );
 
 EFI_STATUS
 VirtioFsFuseOpenDir (
   IN OUT VIRTIO_FS *VirtioFs,
diff --git a/OvmfPkg/VirtioFsDxe/FuseRelease.c b/OvmfPkg/VirtioFsDxe/FuseRelease.c
new file mode 100644
index 000000000000..dd45c7864fc8
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/FuseRelease.c
@@ -0,0 +1,121 @@
+/** @file
+  FUSE_RELEASE / FUSE_RELEASEDIR wrapper for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+/**
+  Close a regular file or a directory that is open, by sending the FUSE_RELEASE
+  or FUSE_RELEASEDIR request to the Virtio Filesystem device.
+
+  The function may only be called after VirtioFsFuseInitSession() returns
+  successfully and before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device to send the
+                           FUSE_RELEASE / FUSE_RELEASEDIR request to. On
+                           output, the FUSE request counter
+                           "VirtioFs->RequestId" will have been incremented.
+
+  @param[in] NodeId        The inode number of the file or directory to close.
+
+  @param[in] FuseHandle    The open handle to the file or directory to close.
+
+  @param[in] IsDir         TRUE if NodeId and FuseHandle refer to a directory,
+                           FALSE if NodeId and FuseHandle refer to a regular
+                           file.
+
+  @retval EFI_SUCCESS  The file or directory has been closed.
+
+  @return              The "errno" value mapped to an EFI_STATUS code, if the
+                       Virtio Filesystem device explicitly reported an error.
+
+  @return              Error codes propagated from VirtioFsSgListsValidate(),
+                       VirtioFsFuseNewRequest(), VirtioFsSgListsSubmit(),
+                       VirtioFsFuseCheckResponse().
+**/
+EFI_STATUS
+VirtioFsFuseReleaseFileOrDir (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId,
+  IN     UINT64    FuseHandle,
+  IN     BOOLEAN   IsDir
+  )
+{
+  VIRTIO_FS_FUSE_REQUEST         CommonReq;
+  VIRTIO_FS_FUSE_RELEASE_REQUEST ReleaseReq;
+  VIRTIO_FS_IO_VECTOR            ReqIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST  ReqSgList;
+  VIRTIO_FS_FUSE_RESPONSE        CommonResp;
+  VIRTIO_FS_IO_VECTOR            RespIoVec[1];
+  VIRTIO_FS_SCATTER_GATHER_LIST  RespSgList;
+  EFI_STATUS                     Status;
+
+  //
+  // Set up the scatter-gather lists.
+  //
+  ReqIoVec[0].Buffer = &CommonReq;
+  ReqIoVec[0].Size   = sizeof CommonReq;
+  ReqIoVec[1].Buffer = &ReleaseReq;
+  ReqIoVec[1].Size   = sizeof ReleaseReq;
+  ReqSgList.IoVec    = ReqIoVec;
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);
+
+  RespIoVec[0].Buffer = &CommonResp;
+  RespIoVec[0].Size   = sizeof CommonResp;
+  RespSgList.IoVec    = RespIoVec;
+  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);
+
+  //
+  // Validate the scatter-gather lists; calculate the total transfer sizes.
+  //
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the common request header.
+  //
+  Status = VirtioFsFuseNewRequest (
+             VirtioFs,
+             &CommonReq,
+             ReqSgList.TotalSize,
+             IsDir ? VirtioFsFuseOpReleaseDir : VirtioFsFuseOpRelease,
+             NodeId
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the FUSE_RELEASE- / FUSE_RELEASEDIR-specific fields.
+  //
+  ReleaseReq.FileHandle   = FuseHandle;
+  ReleaseReq.Flags        = 0;
+  ReleaseReq.ReleaseFlags = 0;
+  ReleaseReq.LockOwner    = 0;
+
+  //
+  // Submit the request.
+  //
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Verify the response (all response buffers are fixed size).
+  //
+  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);
+  if (Status == EFI_DEVICE_ERROR) {
+    DEBUG ((DEBUG_ERROR, "%a: Label=\"%s\" NodeId=%Lu FuseHandle=%Lu "
+      "IsDir=%d Errno=%d\n", __FUNCTION__, VirtioFs->Label, NodeId, FuseHandle,
+      IsDir, CommonResp.Error));
+    Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
+  }
+  return Status;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 11/48] OvmfPkg/VirtioFsDxe: implement EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume()
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (9 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 10/48] OvmfPkg/VirtioFsDxe: add shared wrapper for FUSE_RELEASE / FUSE_RELEASEDIR Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 12/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_FORGET Laszlo Ersek
                   ` (37 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

With the help of the VirtioFsFuseOpenDir() and
VirtioFsFuseReleaseFileOrDir() functions introduced previously, we can now
open and close the root directory. So let's implement
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume().

OpenVolume() creates a new EFI_FILE_PROTOCOL object -- a reference to the
root directory of the filesystem. Thus, we have to start tracking
references to EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, lest we unbind the
virtio-fs device while files are open.

There are two methods that release an EFI_FILE_PROTOCOL object: the
Close() and the Delete() member functions. In particular, they are not
allowed to fail with regard to resource management -- they must release
resources unconditionally. Thus, for rolling back the resource accounting
that we do in EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume(), we have to
implement the first versions of EFI_FILE_PROTOCOL.Close() and
EFI_FILE_PROTOCOL.Delete() in this patch as well.

With this patch applied, the UEFI shell can enter the root directory of
the Virtio Filesystem (such as with the "FS3:" shell command), and the
"DIR" shell command exercises FUSE_OPENDIR and FUSE_RELEASEDIR, according
to the virtiofsd log. The "DIR" command reports the root directory as if
it were empty; probably because at this time, we only allow the shell to
open and to close the root directory, but not to read it.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |   5 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |  10 ++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           | 117 ++++++++++++++++++++
 OvmfPkg/VirtioFsDxe/DriverBinding.c         |   5 +
 OvmfPkg/VirtioFsDxe/SimpleFsClose.c         |  48 ++++++++
 OvmfPkg/VirtioFsDxe/SimpleFsDelete.c        |  29 +++++
 OvmfPkg/VirtioFsDxe/SimpleFsFlush.c         |  18 +++
 OvmfPkg/VirtioFsDxe/SimpleFsGetInfo.c       |  21 ++++
 OvmfPkg/VirtioFsDxe/SimpleFsGetPosition.c   |  20 ++++
 OvmfPkg/VirtioFsDxe/SimpleFsOpen.c          |  22 ++++
 OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c    |  57 +++++++++-
 OvmfPkg/VirtioFsDxe/SimpleFsRead.c          |  20 ++++
 OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c       |  21 ++++
 OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c   |  20 ++++
 OvmfPkg/VirtioFsDxe/SimpleFsWrite.c         |  20 ++++
 15 files changed, 432 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index c17a43c160e3..67fcf975e8b2 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -71,16 +71,21 @@ typedef struct {
 // out at commit (c6ff213fe5b8^) = d78092e4937d ("fuse: fix page dereference
 // after free", 2020-09-18); that is, right before commit c6ff213fe5b8 ("fuse:
 // add submount support to <uapi/linux/fuse.h>", 2020-09-18) introduces FUSE
 // interface version 7.32.
 //
 #define VIRTIO_FS_FUSE_MAJOR  7
 #define VIRTIO_FS_FUSE_MINOR 31
 
+//
+// The inode number of the root directory.
+//
+#define VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID 1
+
 //
 // FUSE operation codes.
 //
 typedef enum {
   VirtioFsFuseOpRelease     = 18,
   VirtioFsFuseOpInit        = 26,
   VirtioFsFuseOpOpenDir     = 27,
   VirtioFsFuseOpReleaseDir  = 29,
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index 95b1a5a8f60a..28e66082ecfe 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -81,17 +81,27 @@ [Packages]
   OvmfPkg/OvmfPkg.dec
 
 [Sources]
   DriverBinding.c
   FuseInit.c
   FuseOpenDir.c
   FuseRelease.c
   Helpers.c
+  SimpleFsClose.c
+  SimpleFsDelete.c
+  SimpleFsFlush.c
+  SimpleFsGetInfo.c
+  SimpleFsGetPosition.c
+  SimpleFsOpen.c
   SimpleFsOpenVolume.c
+  SimpleFsRead.c
+  SimpleFsSetInfo.c
+  SimpleFsSetPosition.c
+  SimpleFsWrite.c
   VirtioFsDxe.h
 
 [LibraryClasses]
   BaseLib
   DebugLib
   MemoryAllocationLib
   UefiBootServicesTableLib
   UefiDriverEntryPoint
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index a99625d0473a..34574d0596fc 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -14,16 +14,19 @@
 #include <IndustryStandard/VirtioFs.h> // VIRTIO_FS_TAG_BYTES
 #include <Library/DebugLib.h>          // CR()
 #include <Protocol/SimpleFileSystem.h> // EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
 #include <Protocol/VirtioDevice.h>     // VIRTIO_DEVICE_PROTOCOL
 #include <Uefi/UefiBaseType.h>         // EFI_EVENT
 
 #define VIRTIO_FS_SIG SIGNATURE_64 ('V', 'I', 'R', 'T', 'I', 'O', 'F', 'S')
 
+#define VIRTIO_FS_FILE_SIG \
+  SIGNATURE_64 ('V', 'I', 'O', 'F', 'S', 'F', 'I', 'L')
+
 //
 // Filesystem label encoded in UCS-2, transformed from the UTF-8 representation
 // in "VIRTIO_FS_CONFIG.Tag", and NUL-terminated. Only the printable ASCII code
 // points (U+0020 through U+007E) are supported.
 //
 typedef CHAR16 VIRTIO_FS_LABEL[VIRTIO_FS_TAG_BYTES + 1];
 
 //
@@ -41,16 +44,17 @@ typedef struct {
   UINT64                          Signature; // DriverBindingStart  0
   VIRTIO_DEVICE_PROTOCOL          *Virtio;   // DriverBindingStart  0
   VIRTIO_FS_LABEL                 Label;     // VirtioFsInit        1
   UINT16                          QueueSize; // VirtioFsInit        1
   VRING                           Ring;      // VirtioRingInit      2
   VOID                            *RingMap;  // VirtioRingMap       2
   UINT64                          RequestId; // FuseInitSession     1
   EFI_EVENT                       ExitBoot;  // DriverBindingStart  0
+  LIST_ENTRY                      OpenFiles; // DriverBindingStart  0
   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL SimpleFs;  // DriverBindingStart  0
 } VIRTIO_FS;
 
 #define VIRTIO_FS_FROM_SIMPLE_FS(SimpleFsReference) \
   CR (SimpleFsReference, VIRTIO_FS, SimpleFs, VIRTIO_FS_SIG);
 
 //
 // Structure for describing a contiguous buffer, potentially mapped for Virtio
@@ -93,16 +97,49 @@ typedef struct {
   UINTN               NumVec;
   //
   // TotalSize is calculated when the scatter-gather list is initially
   // validated.
   //
   UINT32 TotalSize;
 } VIRTIO_FS_SCATTER_GATHER_LIST;
 
+//
+// Private context structure that exposes EFI_FILE_PROTOCOL on top of an open
+// FUSE file reference.
+//
+typedef struct {
+  UINT64            Signature;
+  EFI_FILE_PROTOCOL SimpleFile;
+  BOOLEAN           IsDirectory;
+  VIRTIO_FS         *OwnerFs;
+  LIST_ENTRY        OpenFilesEntry;
+  //
+  // In the FUSE wire protocol, every request except FUSE_INIT refers to a
+  // file, namely by the "VIRTIO_FS_FUSE_REQUEST.NodeId" field; that is, by the
+  // inode number of the file. However, some of the FUSE requests that we need
+  // for some of the EFI_FILE_PROTOCOL member functions require an open file
+  // handle *in addition* to the inode number. For simplicity, whenever a
+  // VIRTIO_FS_FILE object is created, primarily defined by its NodeId field,
+  // we also *open* the referenced file at once, and save the returned file
+  // handle in the FuseHandle field. This way, when an EFI_FILE_PROTOCOL member
+  // function must send a FUSE request that needs the file handle *in addition*
+  // to the inode number, FuseHandle will be at our disposal at once.
+  //
+  UINT64 NodeId;
+  UINT64 FuseHandle;
+} VIRTIO_FS_FILE;
+
+#define VIRTIO_FS_FILE_FROM_SIMPLE_FILE(SimpleFileReference) \
+  CR (SimpleFileReference, VIRTIO_FS_FILE, SimpleFile, VIRTIO_FS_FILE_SIG);
+
+#define VIRTIO_FS_FILE_FROM_OPEN_FILES_ENTRY(OpenFilesEntryReference) \
+  CR (OpenFilesEntryReference, VIRTIO_FS_FILE, OpenFilesEntry, \
+    VIRTIO_FS_FILE_SIG);
+
 //
 // Initialization and helper routines for the Virtio Filesystem device.
 //
 
 EFI_STATUS
 VirtioFsInit (
   IN OUT VIRTIO_FS *VirtioFs
   );
@@ -185,9 +222,89 @@ VirtioFsFuseOpenDir (
 
 EFI_STATUS
 EFIAPI
 VirtioFsOpenVolume (
   IN  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
   OUT EFI_FILE_PROTOCOL               **Root
   );
 
+//
+// EFI_FILE_PROTOCOL member functions for the Virtio Filesystem driver.
+//
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileClose (
+  IN EFI_FILE_PROTOCOL *This
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileDelete (
+  IN EFI_FILE_PROTOCOL *This
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileFlush (
+  IN EFI_FILE_PROTOCOL *This
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileGetInfo (
+  IN     EFI_FILE_PROTOCOL *This,
+  IN     EFI_GUID          *InformationType,
+  IN OUT UINTN             *BufferSize,
+     OUT VOID              *Buffer
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileGetPosition (
+  IN     EFI_FILE_PROTOCOL *This,
+     OUT UINT64            *Position
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileOpen (
+  IN     EFI_FILE_PROTOCOL *This,
+     OUT EFI_FILE_PROTOCOL **NewHandle,
+  IN     CHAR16            *FileName,
+  IN     UINT64            OpenMode,
+  IN     UINT64            Attributes
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileRead (
+  IN     EFI_FILE_PROTOCOL *This,
+  IN OUT UINTN             *BufferSize,
+     OUT VOID              *Buffer
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileSetInfo (
+  IN EFI_FILE_PROTOCOL *This,
+  IN EFI_GUID          *InformationType,
+  IN UINTN             BufferSize,
+  IN VOID              *Buffer
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileSetPosition (
+  IN EFI_FILE_PROTOCOL *This,
+  IN UINT64            Position
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileWrite (
+  IN     EFI_FILE_PROTOCOL *This,
+  IN OUT UINTN             *BufferSize,
+  IN     VOID              *Buffer
+  );
+
 #endif // VIRTIO_FS_DXE_H_
diff --git a/OvmfPkg/VirtioFsDxe/DriverBinding.c b/OvmfPkg/VirtioFsDxe/DriverBinding.c
index 4f77ffaa9953..e273c9f362eb 100644
--- a/OvmfPkg/VirtioFsDxe/DriverBinding.c
+++ b/OvmfPkg/VirtioFsDxe/DriverBinding.c
@@ -90,16 +90,17 @@ VirtioFsBindingStart (
   }
 
   Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
                   VirtioFsExitBoot, VirtioFs, &VirtioFs->ExitBoot);
   if (EFI_ERROR (Status)) {
     goto UninitVirtioFs;
   }
 
+  InitializeListHead (&VirtioFs->OpenFiles);
   VirtioFs->SimpleFs.Revision   = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
   VirtioFs->SimpleFs.OpenVolume = VirtioFsOpenVolume;
 
   Status = gBS->InstallProtocolInterface (&ControllerHandle,
                   &gEfiSimpleFileSystemProtocolGuid, EFI_NATIVE_INTERFACE,
                   &VirtioFs->SimpleFs);
   if (EFI_ERROR (Status)) {
     goto CloseExitBoot;
@@ -144,16 +145,20 @@ VirtioFsBindingStop (
                   This->DriverBindingHandle, ControllerHandle,
                   EFI_OPEN_PROTOCOL_GET_PROTOCOL);
   if (EFI_ERROR (Status)) {
     return Status;
   }
 
   VirtioFs = VIRTIO_FS_FROM_SIMPLE_FS (SimpleFs);
 
+  if (!IsListEmpty (&VirtioFs->OpenFiles)) {
+    return EFI_ACCESS_DENIED;
+  }
+
   Status = gBS->UninstallProtocolInterface (ControllerHandle,
                   &gEfiSimpleFileSystemProtocolGuid, SimpleFs);
   if (EFI_ERROR (Status)) {
     return Status;
   }
 
   Status = gBS->CloseEvent (VirtioFs->ExitBoot);
   ASSERT_EFI_ERROR (Status);
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsClose.c b/OvmfPkg/VirtioFsDxe/SimpleFsClose.c
new file mode 100644
index 000000000000..01bbeae21473
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsClose.c
@@ -0,0 +1,48 @@
+/** @file
+  EFI_FILE_PROTOCOL.Close() member function for the Virtio Filesystem driver.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h>             // RemoveEntryList()
+#include <Library/MemoryAllocationLib.h> // FreePool()
+
+#include "VirtioFsDxe.h"
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileClose (
+  IN EFI_FILE_PROTOCOL *This
+  )
+{
+  VIRTIO_FS_FILE *VirtioFsFile;
+  VIRTIO_FS      *VirtioFs;
+
+  VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
+  VirtioFs     = VirtioFsFile->OwnerFs;
+
+  //
+  // At this point, the implementation is only suitable for closing the
+  // VIRTIO_FS_FILE that was created by VirtioFsOpenVolume().
+  //
+  ASSERT (VirtioFsFile->IsDirectory);
+  ASSERT (VirtioFsFile->NodeId == VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID);
+  //
+  // Close the root directory.
+  //
+  // Ignore any errors, because EFI_FILE_PROTOCOL.Close() is required to
+  // release the EFI_FILE_PROTOCOL object unconditionally.
+  //
+  VirtioFsFuseReleaseFileOrDir (VirtioFs, VirtioFsFile->NodeId,
+    VirtioFsFile->FuseHandle, VirtioFsFile->IsDirectory);
+
+  //
+  // One fewer file left open for the owner filesystem.
+  //
+  RemoveEntryList (&VirtioFsFile->OpenFilesEntry);
+
+  FreePool (VirtioFsFile);
+  return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c b/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c
new file mode 100644
index 000000000000..3209923d1e49
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c
@@ -0,0 +1,29 @@
+/** @file
+  EFI_FILE_PROTOCOL.Delete() member function for the Virtio Filesystem driver.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileDelete (
+  IN EFI_FILE_PROTOCOL *This
+  )
+{
+  //
+  // At this point, the implementation is only suitable for closing the
+  // VIRTIO_FS_FILE that was created by VirtioFsOpenVolume().
+  //
+  // Actually deleting the root directory is not possible, so we're only going
+  // to release resources, and return EFI_WARN_DELETE_FAILURE.
+  //
+  // In order to release resources, VirtioFsSimpleFileClose() is just right
+  // here.
+  //
+  VirtioFsSimpleFileClose (This);
+  return EFI_WARN_DELETE_FAILURE;
+}
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsFlush.c b/OvmfPkg/VirtioFsDxe/SimpleFsFlush.c
new file mode 100644
index 000000000000..e48d92140f64
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsFlush.c
@@ -0,0 +1,18 @@
+/** @file
+  EFI_FILE_PROTOCOL.Flush() member function for the Virtio Filesystem driver.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileFlush (
+  IN EFI_FILE_PROTOCOL *This
+  )
+{
+  return EFI_NO_MEDIA;
+}
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsGetInfo.c b/OvmfPkg/VirtioFsDxe/SimpleFsGetInfo.c
new file mode 100644
index 000000000000..6e870460c014
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsGetInfo.c
@@ -0,0 +1,21 @@
+/** @file
+  EFI_FILE_PROTOCOL.GetInfo() member function for the Virtio Filesystem driver.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileGetInfo (
+  IN     EFI_FILE_PROTOCOL *This,
+  IN     EFI_GUID          *InformationType,
+  IN OUT UINTN             *BufferSize,
+     OUT VOID              *Buffer
+  )
+{
+  return EFI_NO_MEDIA;
+}
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsGetPosition.c b/OvmfPkg/VirtioFsDxe/SimpleFsGetPosition.c
new file mode 100644
index 000000000000..2f40d2be2693
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsGetPosition.c
@@ -0,0 +1,20 @@
+/** @file
+  EFI_FILE_PROTOCOL.GetPosition() member function for the Virtio Filesystem
+  driver.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileGetPosition (
+  IN     EFI_FILE_PROTOCOL *This,
+     OUT UINT64            *Position
+  )
+{
+  return EFI_DEVICE_ERROR;
+}
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsOpen.c b/OvmfPkg/VirtioFsDxe/SimpleFsOpen.c
new file mode 100644
index 000000000000..f0e249184079
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsOpen.c
@@ -0,0 +1,22 @@
+/** @file
+  EFI_FILE_PROTOCOL.Open() member function for the Virtio Filesystem driver.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileOpen (
+  IN     EFI_FILE_PROTOCOL *This,
+     OUT EFI_FILE_PROTOCOL **NewHandle,
+  IN     CHAR16            *FileName,
+  IN     UINT64            OpenMode,
+  IN     UINT64            Attributes
+  )
+{
+  return EFI_NO_MEDIA;
+}
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c b/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
index a5a66a27d84c..8c1457a68aad 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
@@ -2,25 +2,80 @@
   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume() member function for the Virtio
   Filesystem driver.
 
   Copyright (C) 2020, Red Hat, Inc.
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
+#include <Library/BaseLib.h>             // InsertTailList()
+#include <Library/MemoryAllocationLib.h> // AllocatePool()
+
 #include "VirtioFsDxe.h"
 
 /**
   Open the root directory on the Virtio Filesystem.
 
   Refer to EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_OPEN_VOLUME for the interface
   contract.
 **/
 EFI_STATUS
 EFIAPI
 VirtioFsOpenVolume (
   IN  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
   OUT EFI_FILE_PROTOCOL               **Root
   )
 {
-  return EFI_NO_MEDIA;
+  VIRTIO_FS      *VirtioFs;
+  VIRTIO_FS_FILE *VirtioFsFile;
+  EFI_STATUS     Status;
+  UINT64         RootDirHandle;
+
+  VirtioFs = VIRTIO_FS_FROM_SIMPLE_FS (This);
+
+  VirtioFsFile = AllocatePool (sizeof *VirtioFsFile);
+  if (VirtioFsFile == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Open the root directory.
+  //
+  Status = VirtioFsFuseOpenDir (VirtioFs, VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID,
+             &RootDirHandle);
+  if (EFI_ERROR (Status)) {
+    goto FreeVirtioFsFile;
+  }
+
+  //
+  // Populate the new VIRTIO_FS_FILE object.
+  //
+  VirtioFsFile->Signature              = VIRTIO_FS_FILE_SIG;
+  VirtioFsFile->SimpleFile.Revision    = EFI_FILE_PROTOCOL_REVISION;
+  VirtioFsFile->SimpleFile.Open        = VirtioFsSimpleFileOpen;
+  VirtioFsFile->SimpleFile.Close       = VirtioFsSimpleFileClose;
+  VirtioFsFile->SimpleFile.Delete      = VirtioFsSimpleFileDelete;
+  VirtioFsFile->SimpleFile.Read        = VirtioFsSimpleFileRead;
+  VirtioFsFile->SimpleFile.Write       = VirtioFsSimpleFileWrite;
+  VirtioFsFile->SimpleFile.GetPosition = VirtioFsSimpleFileGetPosition;
+  VirtioFsFile->SimpleFile.SetPosition = VirtioFsSimpleFileSetPosition;
+  VirtioFsFile->SimpleFile.GetInfo     = VirtioFsSimpleFileGetInfo;
+  VirtioFsFile->SimpleFile.SetInfo     = VirtioFsSimpleFileSetInfo;
+  VirtioFsFile->SimpleFile.Flush       = VirtioFsSimpleFileFlush;
+  VirtioFsFile->IsDirectory            = TRUE;
+  VirtioFsFile->OwnerFs                = VirtioFs;
+  VirtioFsFile->NodeId                 = VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID;
+  VirtioFsFile->FuseHandle             = RootDirHandle;
+
+  //
+  // One more file open for the filesystem.
+  //
+  InsertTailList (&VirtioFs->OpenFiles, &VirtioFsFile->OpenFilesEntry);
+
+  *Root = &VirtioFsFile->SimpleFile;
+  return EFI_SUCCESS;
+
+FreeVirtioFsFile:
+  FreePool (VirtioFsFile);
+
+  return Status;
 }
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsRead.c b/OvmfPkg/VirtioFsDxe/SimpleFsRead.c
new file mode 100644
index 000000000000..e737d5e33204
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsRead.c
@@ -0,0 +1,20 @@
+/** @file
+  EFI_FILE_PROTOCOL.Read() member function for the Virtio Filesystem driver.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileRead (
+  IN     EFI_FILE_PROTOCOL *This,
+  IN OUT UINTN             *BufferSize,
+     OUT VOID              *Buffer
+  )
+{
+  return EFI_NO_MEDIA;
+}
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c b/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c
new file mode 100644
index 000000000000..200b7a1bcd20
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c
@@ -0,0 +1,21 @@
+/** @file
+  EFI_FILE_PROTOCOL.SetInfo() member function for the Virtio Filesystem driver.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileSetInfo (
+  IN EFI_FILE_PROTOCOL *This,
+  IN EFI_GUID          *InformationType,
+  IN UINTN             BufferSize,
+  IN VOID              *Buffer
+  )
+{
+  return EFI_NO_MEDIA;
+}
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c b/OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c
new file mode 100644
index 000000000000..ee8cb1f4e465
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c
@@ -0,0 +1,20 @@
+/** @file
+  EFI_FILE_PROTOCOL.SetPosition() member function for the Virtio Filesystem
+  driver.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileSetPosition (
+  IN EFI_FILE_PROTOCOL *This,
+  IN UINT64            Position
+  )
+{
+  return EFI_DEVICE_ERROR;
+}
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsWrite.c b/OvmfPkg/VirtioFsDxe/SimpleFsWrite.c
new file mode 100644
index 000000000000..90d82bd722b1
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsWrite.c
@@ -0,0 +1,20 @@
+/** @file
+  EFI_FILE_PROTOCOL.Write() member function for the Virtio Filesystem driver.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+EFI_STATUS
+EFIAPI
+VirtioFsSimpleFileWrite (
+  IN     EFI_FILE_PROTOCOL *This,
+  IN OUT UINTN             *BufferSize,
+  IN     VOID              *Buffer
+  )
+{
+  return EFI_NO_MEDIA;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 12/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_FORGET
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (10 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 11/48] OvmfPkg/VirtioFsDxe: implement EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume() Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 13/48] OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_FSYNC / FUSE_FSYNCDIR Laszlo Ersek
                   ` (36 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Add the VirtioFsFuseForget() function, for sending the FUSE_FORGET command
to the Virtio Filesystem device.

This is an unusual command in that it doesn't generate any response from
the FUSE server.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  8 ++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |  1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |  6 ++
 OvmfPkg/VirtioFsDxe/FuseForget.c            | 85 ++++++++++++++++++++
 4 files changed, 100 insertions(+)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index 67fcf975e8b2..0241daf1e8d6 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -80,16 +80,17 @@ typedef struct {
 // The inode number of the root directory.
 //
 #define VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID 1
 
 //
 // FUSE operation codes.
 //
 typedef enum {
+  VirtioFsFuseOpForget      =  2,
   VirtioFsFuseOpRelease     = 18,
   VirtioFsFuseOpInit        = 26,
   VirtioFsFuseOpOpenDir     = 27,
   VirtioFsFuseOpReleaseDir  = 29,
 } VIRTIO_FS_FUSE_OPCODE;
 
 #pragma pack (1)
 //
@@ -107,16 +108,23 @@ typedef struct {
 } VIRTIO_FS_FUSE_REQUEST;
 
 typedef struct {
   UINT32 Len;
   INT32  Error;
   UINT64 Unique;
 } VIRTIO_FS_FUSE_RESPONSE;
 
+//
+// Header for VirtioFsFuseOpForget.
+//
+typedef struct {
+  UINT64 NumberOfLookups;
+} VIRTIO_FS_FUSE_FORGET_REQUEST;
+
 //
 // Header for VirtioFsFuseOpRelease and VirtioFsFuseOpReleaseDir.
 //
 typedef struct {
   UINT64 FileHandle;
   UINT32 Flags;
   UINT32 ReleaseFlags;
   UINT64 LockOwner;
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index 28e66082ecfe..baeb741be18f 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -77,16 +77,17 @@ [Defines]
   ENTRY_POINT                           = VirtioFsEntryPoint
 
 [Packages]
   MdePkg/MdePkg.dec
   OvmfPkg/OvmfPkg.dec
 
 [Sources]
   DriverBinding.c
+  FuseForget.c
   FuseInit.c
   FuseOpenDir.c
   FuseRelease.c
   Helpers.c
   SimpleFsClose.c
   SimpleFsDelete.c
   SimpleFsFlush.c
   SimpleFsGetInfo.c
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 34574d0596fc..68ed6cd7e6a1 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -190,16 +190,22 @@ EFI_STATUS
 VirtioFsErrnoToEfiStatus (
   IN INT32 Errno
   );
 
 //
 // Wrapper functions for FUSE commands (primitives).
 //
 
+EFI_STATUS
+VirtioFsFuseForget (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId
+  );
+
 EFI_STATUS
 VirtioFsFuseReleaseFileOrDir (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    NodeId,
   IN     UINT64    FuseHandle,
   IN     BOOLEAN   IsDir
   );
 
diff --git a/OvmfPkg/VirtioFsDxe/FuseForget.c b/OvmfPkg/VirtioFsDxe/FuseForget.c
new file mode 100644
index 000000000000..fdee42cffbff
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/FuseForget.c
@@ -0,0 +1,85 @@
+/** @file
+  FUSE_FORGET wrapper for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+/**
+  Make the Virtio Filesysem device drop one reference count from a NodeId that
+  the driver looked up by filename.
+
+  Send the FUSE_FORGET request to the Virtio Filesysem device for this. Unlike
+  most other FUSE requests, FUSE_FORGET doesn't elicit a response, not even the
+  common VIRTIO_FS_FUSE_RESPONSE header.
+
+  The function may only be called after VirtioFsFuseInitSession() returns
+  successfully and before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_FORGET
+                           request to. On output, the FUSE request counter
+                           "VirtioFs->RequestId" will have been incremented.
+
+  @param[in] NodeId        The inode number that the client learned by way of
+                           lookup, and that the server should now un-reference
+                           exactly once.
+
+  @retval EFI_SUCCESS  The FUSE_FORGET request has been submitted.
+
+  @return              Error codes propagated from VirtioFsSgListsValidate(),
+                       VirtioFsFuseNewRequest(), VirtioFsSgListsSubmit().
+**/
+EFI_STATUS
+VirtioFsFuseForget (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId
+  )
+{
+  VIRTIO_FS_FUSE_REQUEST        CommonReq;
+  VIRTIO_FS_FUSE_FORGET_REQUEST ForgetReq;
+  VIRTIO_FS_IO_VECTOR           ReqIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST ReqSgList;
+  EFI_STATUS                    Status;
+
+  //
+  // Set up the scatter-gather list (note: only request).
+  //
+  ReqIoVec[0].Buffer = &CommonReq;
+  ReqIoVec[0].Size   = sizeof CommonReq;
+  ReqIoVec[1].Buffer = &ForgetReq;
+  ReqIoVec[1].Size   = sizeof ForgetReq;
+  ReqSgList.IoVec    = ReqIoVec;
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);
+
+  //
+  // Validate the scatter-gather list (request only); calculate the total
+  // transfer size.
+  //
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, NULL);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the common request header.
+  //
+  Status = VirtioFsFuseNewRequest (VirtioFs, &CommonReq, ReqSgList.TotalSize,
+             VirtioFsFuseOpForget, NodeId);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the FUSE_FORGET-specific fields.
+  //
+  ForgetReq.NumberOfLookups = 1;
+
+  //
+  // Submit the request. There's not going to be a response.
+  //
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, NULL);
+  return Status;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 13/48] OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_FSYNC / FUSE_FSYNCDIR
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (11 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 12/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_FORGET Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 14/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_FLUSH Laszlo Ersek
                   ` (35 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

The FUSE_FSYNC and FUSE_FSYNCDIR commands only differ in the opcode. Add a
common function for wrapping both.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  11 ++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |   8 ++
 OvmfPkg/VirtioFsDxe/FuseFsync.c             | 121 ++++++++++++++++++++
 4 files changed, 141 insertions(+)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index 0241daf1e8d6..797d94a39a39 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -82,19 +82,21 @@ typedef struct {
 #define VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID 1
 
 //
 // FUSE operation codes.
 //
 typedef enum {
   VirtioFsFuseOpForget      =  2,
   VirtioFsFuseOpRelease     = 18,
+  VirtioFsFuseOpFsync       = 20,
   VirtioFsFuseOpInit        = 26,
   VirtioFsFuseOpOpenDir     = 27,
   VirtioFsFuseOpReleaseDir  = 29,
+  VirtioFsFuseOpFsyncDir    = 30,
 } VIRTIO_FS_FUSE_OPCODE;
 
 #pragma pack (1)
 //
 // Request-response headers common to all request types.
 //
 typedef struct {
   UINT32 Len;
@@ -125,16 +127,25 @@ typedef struct {
 //
 typedef struct {
   UINT64 FileHandle;
   UINT32 Flags;
   UINT32 ReleaseFlags;
   UINT64 LockOwner;
 } VIRTIO_FS_FUSE_RELEASE_REQUEST;
 
+//
+// Header for VirtioFsFuseOpFsync and VirtioFsFuseOpFsyncDir.
+//
+typedef struct {
+  UINT64 FileHandle;
+  UINT32 FsyncFlags;
+  UINT32 Padding;
+} VIRTIO_FS_FUSE_FSYNC_REQUEST;
+
 //
 // Headers for VirtioFsFuseOpInit.
 //
 typedef struct {
   UINT32 Major;
   UINT32 Minor;
   UINT32 MaxReadahead;
   UINT32 Flags;
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index baeb741be18f..839660a0f904 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -78,16 +78,17 @@ [Defines]
 
 [Packages]
   MdePkg/MdePkg.dec
   OvmfPkg/OvmfPkg.dec
 
 [Sources]
   DriverBinding.c
   FuseForget.c
+  FuseFsync.c
   FuseInit.c
   FuseOpenDir.c
   FuseRelease.c
   Helpers.c
   SimpleFsClose.c
   SimpleFsDelete.c
   SimpleFsFlush.c
   SimpleFsGetInfo.c
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 68ed6cd7e6a1..ac1fb8965472 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -204,16 +204,24 @@ VirtioFsFuseForget (
 EFI_STATUS
 VirtioFsFuseReleaseFileOrDir (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    NodeId,
   IN     UINT64    FuseHandle,
   IN     BOOLEAN   IsDir
   );
 
+EFI_STATUS
+VirtioFsFuseFsyncFileOrDir (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId,
+  IN     UINT64    FuseHandle,
+  IN     BOOLEAN   IsDir
+  );
+
 EFI_STATUS
 VirtioFsFuseInitSession (
   IN OUT VIRTIO_FS *VirtioFs
   );
 
 EFI_STATUS
 VirtioFsFuseOpenDir (
   IN OUT VIRTIO_FS *VirtioFs,
diff --git a/OvmfPkg/VirtioFsDxe/FuseFsync.c b/OvmfPkg/VirtioFsDxe/FuseFsync.c
new file mode 100644
index 000000000000..ea1463cd8073
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/FuseFsync.c
@@ -0,0 +1,121 @@
+/** @file
+  FUSE_FSYNC / FUSE_FSYNCDIR wrapper for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+/**
+  Synchronize the in-core state of a regular file or a directory with the
+  storage device on the host, by sending the FUSE_FSYNC or FUSE_FSYNCDIR
+  request to the Virtio Filesystem device.
+
+  The function may only be called after VirtioFsFuseInitSession() returns
+  successfully and before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_FSYNC
+                           / FUSE_FSYNCDIR request to. On output, the FUSE
+                           request counter "VirtioFs->RequestId" will have been
+                           incremented.
+
+  @param[in] NodeId        The inode number of the file or directory to sync.
+
+  @param[in] FuseHandle    The open handle to the file or directory to sync.
+
+  @param[in] IsDir         TRUE if NodeId and FuseHandle refer to a directory,
+                           FALSE if NodeId and FuseHandle refer to a regular
+                           file.
+
+  @retval EFI_SUCCESS  The file or directory has been synchronized.
+
+  @return              The "errno" value mapped to an EFI_STATUS code, if the
+                       Virtio Filesystem device explicitly reported an error.
+
+  @return              Error codes propagated from VirtioFsSgListsValidate(),
+                       VirtioFsFuseNewRequest(), VirtioFsSgListsSubmit(),
+                       VirtioFsFuseCheckResponse().
+**/
+EFI_STATUS
+VirtioFsFuseFsyncFileOrDir (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId,
+  IN     UINT64    FuseHandle,
+  IN     BOOLEAN   IsDir
+  )
+{
+  VIRTIO_FS_FUSE_REQUEST        CommonReq;
+  VIRTIO_FS_FUSE_FSYNC_REQUEST  FsyncReq;
+  VIRTIO_FS_IO_VECTOR           ReqIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST ReqSgList;
+  VIRTIO_FS_FUSE_RESPONSE       CommonResp;
+  VIRTIO_FS_IO_VECTOR           RespIoVec[1];
+  VIRTIO_FS_SCATTER_GATHER_LIST RespSgList;
+  EFI_STATUS                    Status;
+
+  //
+  // Set up the scatter-gather lists.
+  //
+  ReqIoVec[0].Buffer = &CommonReq;
+  ReqIoVec[0].Size   = sizeof CommonReq;
+  ReqIoVec[1].Buffer = &FsyncReq;
+  ReqIoVec[1].Size   = sizeof FsyncReq;
+  ReqSgList.IoVec    = ReqIoVec;
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);
+
+  RespIoVec[0].Buffer = &CommonResp;
+  RespIoVec[0].Size   = sizeof CommonResp;
+  RespSgList.IoVec    = RespIoVec;
+  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);
+
+  //
+  // Validate the scatter-gather lists; calculate the total transfer sizes.
+  //
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the common request header.
+  //
+  Status = VirtioFsFuseNewRequest (
+             VirtioFs,
+             &CommonReq,
+             ReqSgList.TotalSize,
+             IsDir ? VirtioFsFuseOpFsyncDir : VirtioFsFuseOpFsync,
+             NodeId
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the FUSE_FSYNC- / FUSE_FSYNCDIR-specific fields.
+  //
+  FsyncReq.FileHandle = FuseHandle;
+  FsyncReq.FsyncFlags = 0;
+  FsyncReq.Padding    = 0;
+
+  //
+  // Submit the request.
+  //
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Verify the response (all response buffers are fixed size).
+  //
+  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);
+  if (Status == EFI_DEVICE_ERROR) {
+    DEBUG ((DEBUG_ERROR, "%a: Label=\"%s\" NodeId=%Lu FuseHandle=%Lu "
+      "IsDir=%d Errno=%d\n", __FUNCTION__, VirtioFs->Label, NodeId, FuseHandle,
+      IsDir, CommonResp.Error));
+    Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
+  }
+  return Status;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 14/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_FLUSH
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (12 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 13/48] OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_FSYNC / FUSE_FSYNCDIR Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 15/48] OvmfPkg/VirtioFsDxe: flush, sync, release and forget in Close() / Delete() Laszlo Ersek
                   ` (34 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Add the VirtioFsFuseFlush() function, for sending the FUSE_FLUSH command
to the Virtio Filesystem device.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  11 ++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |   7 ++
 OvmfPkg/VirtioFsDxe/FuseFlush.c             | 111 ++++++++++++++++++++
 4 files changed, 130 insertions(+)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index 797d94a39a39..fec2f4be531f 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -83,16 +83,17 @@ typedef struct {
 
 //
 // FUSE operation codes.
 //
 typedef enum {
   VirtioFsFuseOpForget      =  2,
   VirtioFsFuseOpRelease     = 18,
   VirtioFsFuseOpFsync       = 20,
+  VirtioFsFuseOpFlush       = 25,
   VirtioFsFuseOpInit        = 26,
   VirtioFsFuseOpOpenDir     = 27,
   VirtioFsFuseOpReleaseDir  = 29,
   VirtioFsFuseOpFsyncDir    = 30,
 } VIRTIO_FS_FUSE_OPCODE;
 
 #pragma pack (1)
 //
@@ -136,16 +137,26 @@ typedef struct {
 // Header for VirtioFsFuseOpFsync and VirtioFsFuseOpFsyncDir.
 //
 typedef struct {
   UINT64 FileHandle;
   UINT32 FsyncFlags;
   UINT32 Padding;
 } VIRTIO_FS_FUSE_FSYNC_REQUEST;
 
+//
+// Header for VirtioFsFuseOpFlush.
+//
+typedef struct {
+  UINT64 FileHandle;
+  UINT32 Unused;
+  UINT32 Padding;
+  UINT64 LockOwner;
+} VIRTIO_FS_FUSE_FLUSH_REQUEST;
+
 //
 // Headers for VirtioFsFuseOpInit.
 //
 typedef struct {
   UINT32 Major;
   UINT32 Minor;
   UINT32 MaxReadahead;
   UINT32 Flags;
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index 839660a0f904..15e21772c8ac 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -77,16 +77,17 @@ [Defines]
   ENTRY_POINT                           = VirtioFsEntryPoint
 
 [Packages]
   MdePkg/MdePkg.dec
   OvmfPkg/OvmfPkg.dec
 
 [Sources]
   DriverBinding.c
+  FuseFlush.c
   FuseForget.c
   FuseFsync.c
   FuseInit.c
   FuseOpenDir.c
   FuseRelease.c
   Helpers.c
   SimpleFsClose.c
   SimpleFsDelete.c
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index ac1fb8965472..7151a62bb42b 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -212,16 +212,23 @@ VirtioFsFuseReleaseFileOrDir (
 EFI_STATUS
 VirtioFsFuseFsyncFileOrDir (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    NodeId,
   IN     UINT64    FuseHandle,
   IN     BOOLEAN   IsDir
   );
 
+EFI_STATUS
+VirtioFsFuseFlush (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId,
+  IN     UINT64    FuseHandle
+  );
+
 EFI_STATUS
 VirtioFsFuseInitSession (
   IN OUT VIRTIO_FS *VirtioFs
   );
 
 EFI_STATUS
 VirtioFsFuseOpenDir (
   IN OUT VIRTIO_FS *VirtioFs,
diff --git a/OvmfPkg/VirtioFsDxe/FuseFlush.c b/OvmfPkg/VirtioFsDxe/FuseFlush.c
new file mode 100644
index 000000000000..b689ad32910a
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/FuseFlush.c
@@ -0,0 +1,111 @@
+/** @file
+  FUSE_FLUSH wrapper for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+/**
+  Flush changes queued on the local virtualization host to the remote storage
+  server's memory (not storage device), over the network, by sending the
+  FUSE_FLUSH request to the Virtio Filesystem device.
+
+  The function may only be called after VirtioFsFuseInitSession() returns
+  successfully and before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_FLUSH
+                           request to. On output, the FUSE request counter
+                           "VirtioFs->RequestId" will have been incremented.
+
+  @param[in] NodeId        The inode number of the regular file to flush.
+
+  @param[in] FuseHandle    The open handle to the regular file to flush.
+
+  @retval EFI_SUCCESS  The regular file has been flushed.
+
+  @return              The "errno" value mapped to an EFI_STATUS code, if the
+                       Virtio Filesystem device explicitly reported an error.
+
+  @return              Error codes propagated from VirtioFsSgListsValidate(),
+                       VirtioFsFuseNewRequest(), VirtioFsSgListsSubmit(),
+                       VirtioFsFuseCheckResponse().
+**/
+EFI_STATUS
+VirtioFsFuseFlush (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId,
+  IN     UINT64    FuseHandle
+  )
+{
+  VIRTIO_FS_FUSE_REQUEST        CommonReq;
+  VIRTIO_FS_FUSE_FLUSH_REQUEST  FlushReq;
+  VIRTIO_FS_IO_VECTOR           ReqIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST ReqSgList;
+  VIRTIO_FS_FUSE_RESPONSE       CommonResp;
+  VIRTIO_FS_IO_VECTOR           RespIoVec[1];
+  VIRTIO_FS_SCATTER_GATHER_LIST RespSgList;
+  EFI_STATUS                    Status;
+
+  //
+  // Set up the scatter-gather lists.
+  //
+  ReqIoVec[0].Buffer = &CommonReq;
+  ReqIoVec[0].Size   = sizeof CommonReq;
+  ReqIoVec[1].Buffer = &FlushReq;
+  ReqIoVec[1].Size   = sizeof FlushReq;
+  ReqSgList.IoVec    = ReqIoVec;
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);
+
+  RespIoVec[0].Buffer = &CommonResp;
+  RespIoVec[0].Size   = sizeof CommonResp;
+  RespSgList.IoVec    = RespIoVec;
+  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);
+
+  //
+  // Validate the scatter-gather lists; calculate the total transfer sizes.
+  //
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the common request header.
+  //
+  Status = VirtioFsFuseNewRequest (VirtioFs, &CommonReq, ReqSgList.TotalSize,
+             VirtioFsFuseOpFlush, NodeId);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the FUSE_FLUSH-specific fields.
+  //
+  FlushReq.FileHandle = FuseHandle;
+  FlushReq.Unused     = 0;
+  FlushReq.Padding    = 0;
+  FlushReq.LockOwner  = 0;
+
+  //
+  // Submit the request.
+  //
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Verify the response (all response buffers are fixed size).
+  //
+  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);
+  if (Status == EFI_DEVICE_ERROR) {
+    DEBUG ((DEBUG_ERROR, "%a: Label=\"%s\" NodeId=%Lu FuseHandle=%Lu "
+      "Errno=%d\n", __FUNCTION__, VirtioFs->Label, NodeId, FuseHandle,
+      CommonResp.Error));
+    Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
+  }
+  return Status;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 15/48] OvmfPkg/VirtioFsDxe: flush, sync, release and forget in Close() / Delete()
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (13 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 14/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_FLUSH Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 16/48] OvmfPkg/VirtioFsDxe: add helper for appending and sanitizing paths Laszlo Ersek
                   ` (33 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

The two member functions that free the EFI_FILE_PROTOCOL object are
Close() and Delete(). Before we create VIRTIO_FS_FILE objects with
EFI_FILE_PROTOCOL.Open() later in this patch series, extend each of these
"destructor" functions to get rid of the FuseHandle and NodeId resources
properly -- in a way that matches each function's own purpose.

For the time being, VirtioFsSimpleFileDelete() only gets a reminder about
its core task (namely, removing the file), as the needed machinery will
become only later. But we can already outline the "task list", and deal
with the FuseHandle and NodeId resources. The "task list" of
VirtioFsSimpleFileDelete() is different from that of
VirtioFsSimpleFileClose(), thus both destructors diverge at this point.

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>
---
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h        |  1 +
 OvmfPkg/VirtioFsDxe/SimpleFsClose.c      | 34 ++++++++----
 OvmfPkg/VirtioFsDxe/SimpleFsDelete.c     | 55 +++++++++++++++++---
 OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c |  1 +
 4 files changed, 74 insertions(+), 17 deletions(-)

diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 7151a62bb42b..1cbd27d8fb52 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -105,16 +105,17 @@ typedef struct {
 //
 // Private context structure that exposes EFI_FILE_PROTOCOL on top of an open
 // FUSE file reference.
 //
 typedef struct {
   UINT64            Signature;
   EFI_FILE_PROTOCOL SimpleFile;
   BOOLEAN           IsDirectory;
+  BOOLEAN           IsOpenForWriting;
   VIRTIO_FS         *OwnerFs;
   LIST_ENTRY        OpenFilesEntry;
   //
   // In the FUSE wire protocol, every request except FUSE_INIT refers to a
   // file, namely by the "VIRTIO_FS_FUSE_REQUEST.NodeId" field; that is, by the
   // inode number of the file. However, some of the FUSE requests that we need
   // for some of the EFI_FILE_PROTOCOL member functions require an open file
   // handle *in addition* to the inode number. For simplicity, whenever a
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsClose.c b/OvmfPkg/VirtioFsDxe/SimpleFsClose.c
index 01bbeae21473..bc91ad726b2c 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsClose.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsClose.c
@@ -19,30 +19,46 @@ VirtioFsSimpleFileClose (
 {
   VIRTIO_FS_FILE *VirtioFsFile;
   VIRTIO_FS      *VirtioFs;
 
   VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
   VirtioFs     = VirtioFsFile->OwnerFs;
 
   //
-  // At this point, the implementation is only suitable for closing the
-  // VIRTIO_FS_FILE that was created by VirtioFsOpenVolume().
+  // All actions in this function are "best effort"; the UEFI spec requires
+  // EFI_FILE_PROTOCOL.Close() to sync all data to the device, but it also
+  // requires EFI_FILE_PROTOCOL.Close() to release resources unconditionally,
+  // and to return EFI_SUCCESS unconditionally.
   //
-  ASSERT (VirtioFsFile->IsDirectory);
-  ASSERT (VirtioFsFile->NodeId == VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID);
-  //
-  // Close the root directory.
-  //
-  // Ignore any errors, because EFI_FILE_PROTOCOL.Close() is required to
-  // release the EFI_FILE_PROTOCOL object unconditionally.
+  // Flush, sync, release, and (if needed) forget. If any action fails, we
+  // still try the others.
   //
+  if (VirtioFsFile->IsOpenForWriting) {
+    if (!VirtioFsFile->IsDirectory) {
+      VirtioFsFuseFlush (VirtioFs, VirtioFsFile->NodeId,
+        VirtioFsFile->FuseHandle);
+    }
+
+    VirtioFsFuseFsyncFileOrDir (VirtioFs, VirtioFsFile->NodeId,
+      VirtioFsFile->FuseHandle, VirtioFsFile->IsDirectory);
+  }
+
   VirtioFsFuseReleaseFileOrDir (VirtioFs, VirtioFsFile->NodeId,
     VirtioFsFile->FuseHandle, VirtioFsFile->IsDirectory);
 
+  //
+  // VirtioFsFile->FuseHandle is gone at this point, but VirtioFsFile->NodeId
+  // is still valid. If we've known VirtioFsFile->NodeId from a lookup, then
+  // now we should ask the server to forget it *once*.
+  //
+  if (VirtioFsFile->NodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
+    VirtioFsFuseForget (VirtioFs, VirtioFsFile->NodeId);
+  }
+
   //
   // One fewer file left open for the owner filesystem.
   //
   RemoveEntryList (&VirtioFsFile->OpenFilesEntry);
 
   FreePool (VirtioFsFile);
   return EFI_SUCCESS;
 }
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c b/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c
index 3209923d1e49..bbad64bf7886 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c
@@ -1,29 +1,68 @@
 /** @file
   EFI_FILE_PROTOCOL.Delete() member function for the Virtio Filesystem driver.
 
   Copyright (C) 2020, Red Hat, Inc.
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
+#include <Library/BaseLib.h>             // RemoveEntryList()
+#include <Library/MemoryAllocationLib.h> // FreePool()
+
 #include "VirtioFsDxe.h"
 
 EFI_STATUS
 EFIAPI
 VirtioFsSimpleFileDelete (
   IN EFI_FILE_PROTOCOL *This
   )
 {
+  VIRTIO_FS_FILE *VirtioFsFile;
+  VIRTIO_FS      *VirtioFs;
+  EFI_STATUS     Status;
+
+  VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
+  VirtioFs     = VirtioFsFile->OwnerFs;
+
   //
-  // At this point, the implementation is only suitable for closing the
-  // VIRTIO_FS_FILE that was created by VirtioFsOpenVolume().
+  // All actions in this function are "best effort"; the UEFI spec requires
+  // EFI_FILE_PROTOCOL.Delete() to release resources unconditionally. If a step
+  // related to removing the file fails, it's only reflected in the return
+  // status (EFI_WARN_DELETE_FAILURE rather than EFI_SUCCESS).
   //
-  // Actually deleting the root directory is not possible, so we're only going
-  // to release resources, and return EFI_WARN_DELETE_FAILURE.
+  // Release, remove, and (if needed) forget. We don't waste time flushing and
+  // syncing; if the EFI_FILE_PROTOCOL user cares enough, they should keep the
+  // parent directory open until after this function call returns, and then
+  // force a sync on *that* EFI_FILE_PROTOCOL instance, using either the
+  // Flush() member function, or the Close() member function.
   //
-  // In order to release resources, VirtioFsSimpleFileClose() is just right
-  // here.
+  // If any action fails below, we still try the others.
   //
-  VirtioFsSimpleFileClose (This);
-  return EFI_WARN_DELETE_FAILURE;
+  VirtioFsFuseReleaseFileOrDir (VirtioFs, VirtioFsFile->NodeId,
+    VirtioFsFile->FuseHandle, VirtioFsFile->IsDirectory);
+
+  //
+  // VirtioFsFile->FuseHandle is gone at this point, but VirtioFsFile->NodeId
+  // is still valid. Continue with removing the file or directory. The result
+  // of this operation determines the return status of the function.
+  //
+  // TODO
+  //
+  Status = EFI_WARN_DELETE_FAILURE;
+
+  //
+  // Finally, if we've known VirtioFsFile->NodeId from a lookup, then we should
+  // also ask the server to forget it *once*.
+  //
+  if (VirtioFsFile->NodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
+    VirtioFsFuseForget (VirtioFs, VirtioFsFile->NodeId);
+  }
+
+  //
+  // One fewer file left open for the owner filesystem.
+  //
+  RemoveEntryList (&VirtioFsFile->OpenFilesEntry);
+
+  FreePool (VirtioFsFile);
+  return Status;
 }
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c b/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
index 8c1457a68aad..67d2deb6bdf2 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
@@ -57,16 +57,17 @@ VirtioFsOpenVolume (
   VirtioFsFile->SimpleFile.Read        = VirtioFsSimpleFileRead;
   VirtioFsFile->SimpleFile.Write       = VirtioFsSimpleFileWrite;
   VirtioFsFile->SimpleFile.GetPosition = VirtioFsSimpleFileGetPosition;
   VirtioFsFile->SimpleFile.SetPosition = VirtioFsSimpleFileSetPosition;
   VirtioFsFile->SimpleFile.GetInfo     = VirtioFsSimpleFileGetInfo;
   VirtioFsFile->SimpleFile.SetInfo     = VirtioFsSimpleFileSetInfo;
   VirtioFsFile->SimpleFile.Flush       = VirtioFsSimpleFileFlush;
   VirtioFsFile->IsDirectory            = TRUE;
+  VirtioFsFile->IsOpenForWriting       = FALSE;
   VirtioFsFile->OwnerFs                = VirtioFs;
   VirtioFsFile->NodeId                 = VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID;
   VirtioFsFile->FuseHandle             = RootDirHandle;
 
   //
   // One more file open for the filesystem.
   //
   InsertTailList (&VirtioFs->OpenFiles, &VirtioFsFile->OpenFilesEntry);
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 16/48] OvmfPkg/VirtioFsDxe: add helper for appending and sanitizing paths
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (14 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 15/48] OvmfPkg/VirtioFsDxe: flush, sync, release and forget in Close() / Delete() Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 17/48] OvmfPkg/VirtioFsDxe: manage path lifecycle in OpenVolume, Close, Delete Laszlo Ersek
                   ` (32 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

EFI_FILE_PROTOCOL.Open() -- for opening files -- and
EFI_FILE_PROTOCOL.SetInfo() --  for renaming files -- will require us to
append a relative UEFI pathname to an absolute base pathname. In turn,
components of the resultant pathnames will have to be sent to virtiofsd,
which does not consume UEFI-style pathnames.

We're going to maintain the base pathnames in canonical POSIX format:
- absolute (starts with "/"),
- dot (.) and dot-dot (..) components resolved/removed,
- uses forward slashes,
- sequences of slashes collapsed,
- printable ASCII character set,
- CHAR8 encoding,
- no trailing slash except for the root directory itself,
- length at most VIRTIO_FS_MAX_PATHNAME_LENGTH.

Add a helper function that can append a UEFI pathname to such a base
pathname, and produce the result in conformance with the same invariants.

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>
---
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h   |  32 ++
 OvmfPkg/VirtioFsDxe/Helpers.c       | 474 ++++++++++++++++++++
 3 files changed, 507 insertions(+)

diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index 15e21772c8ac..0c92bccdac86 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -99,16 +99,17 @@ [Sources]
   SimpleFsRead.c
   SimpleFsSetInfo.c
   SimpleFsSetPosition.c
   SimpleFsWrite.c
   VirtioFsDxe.h
 
 [LibraryClasses]
   BaseLib
+  BaseMemoryLib
   DebugLib
   MemoryAllocationLib
   UefiBootServicesTableLib
   UefiDriverEntryPoint
   VirtioLib
 
 [Protocols]
   gEfiComponentName2ProtocolGuid        ## PRODUCES
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 1cbd27d8fb52..f4fed64c7217 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -17,16 +17,40 @@
 #include <Protocol/VirtioDevice.h>     // VIRTIO_DEVICE_PROTOCOL
 #include <Uefi/UefiBaseType.h>         // EFI_EVENT
 
 #define VIRTIO_FS_SIG SIGNATURE_64 ('V', 'I', 'R', 'T', 'I', 'O', 'F', 'S')
 
 #define VIRTIO_FS_FILE_SIG \
   SIGNATURE_64 ('V', 'I', 'O', 'F', 'S', 'F', 'I', 'L')
 
+//
+// The following limit applies to two kinds of pathnames.
+//
+// - The length of a POSIX-style, canonical pathname *at rest* never exceeds
+//   VIRTIO_FS_MAX_PATHNAME_LENGTH. (Length is defined as the number of CHAR8
+//   elements in the canonical pathname, excluding the terminating '\0'.) This
+//   is an invariant that is ensured for canonical pathnames created, and that
+//   is assumed about canonical pathname inputs (which all originate
+//   internally).
+//
+// - If the length of a UEFI-style pathname *argument*, originating directly or
+//   indirectly from the EFI_FILE_PROTOCOL caller, exceeds
+//   VIRTIO_FS_MAX_PATHNAME_LENGTH, then the argument is rejected. (Length is
+//   defined as the number of CHAR16 elements in the UEFI-style pathname,
+//   excluding the terminating L'\0'.) This is a restriction that's checked on
+//   external UEFI-style pathname inputs.
+//
+// The limit is not expected to be a practical limitation; it's only supposed
+// to prevent attempts at overflowing size calculations. For both kinds of
+// pathnames, separate limits could be used; a common limit is used purely for
+// simplicity.
+//
+#define VIRTIO_FS_MAX_PATHNAME_LENGTH ((UINTN)65535)
+
 //
 // Filesystem label encoded in UCS-2, transformed from the UTF-8 representation
 // in "VIRTIO_FS_CONFIG.Tag", and NUL-terminated. Only the printable ASCII code
 // points (U+0020 through U+007E) are supported.
 //
 typedef CHAR16 VIRTIO_FS_LABEL[VIRTIO_FS_TAG_BYTES + 1];
 
 //
@@ -187,16 +211,24 @@ VirtioFsFuseCheckResponse (
   OUT UINTN                         *TailBufferFill
   );
 
 EFI_STATUS
 VirtioFsErrnoToEfiStatus (
   IN INT32 Errno
   );
 
+EFI_STATUS
+VirtioFsAppendPath (
+  IN     CHAR8   *LhsPath8,
+  IN     CHAR16  *RhsPath16,
+     OUT CHAR8   **ResultPath8,
+     OUT BOOLEAN *RootEscape
+  );
+
 //
 // Wrapper functions for FUSE commands (primitives).
 //
 
 EFI_STATUS
 VirtioFsFuseForget (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    NodeId
diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
index 00f762142746..4a7b59332ca9 100644
--- a/OvmfPkg/VirtioFsDxe/Helpers.c
+++ b/OvmfPkg/VirtioFsDxe/Helpers.c
@@ -1,16 +1,19 @@
 /** @file
   Initialization and helper routines for the Virtio Filesystem device.
 
   Copyright (C) 2020, Red Hat, Inc.
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
+#include <Library/BaseLib.h>             // StrLen()
+#include <Library/BaseMemoryLib.h>       // CopyMem()
+#include <Library/MemoryAllocationLib.h> // AllocatePool()
 #include <Library/VirtioLib.h>           // Virtio10WriteFeatures()
 
 #include "VirtioFsDxe.h"
 
 /**
   Read the Virtio Filesystem device configuration structure in full.
 
   @param[in] Virtio   The Virtio protocol underlying the VIRTIO_FS object.
@@ -1110,8 +1113,479 @@ VirtioFsErrnoToEfiStatus (
     return EFI_NOT_STARTED;
 
   default:
     break;
   }
 
   return EFI_DEVICE_ERROR;
 }
+
+//
+// Parser states for canonicalizing a POSIX pathname.
+//
+typedef enum {
+  ParserInit,   // just starting
+  ParserEnd,    // finished
+  ParserSlash,  // slash(es) seen
+  ParserDot,    // one dot seen since last slash
+  ParserDotDot, // two dots seen since last slash
+  ParserNormal, // a different sequence seen
+} PARSER_STATE;
+
+/**
+  Strip the trailing slash from the parser's output buffer, unless the trailing
+  slash stands for the root directory.
+
+  @param[in] Buffer        The parser's output buffer. Only used for
+                           sanity-checking.
+
+  @param[in,out] Position  On entry, points at the next character to produce
+                           (i.e., right past the end of the output written by
+                           the parser thus far). The last character in the
+                           parser's output buffer is a slash. On return, the
+                           slash is stripped, by decrementing Position by one.
+                           If this action would remove the slash character
+                           standing for the root directory, then the function
+                           has no effect.
+**/
+STATIC
+VOID
+ParserStripSlash (
+  IN     CHAR8 *Buffer,
+  IN OUT UINTN *Position
+  )
+{
+  ASSERT (*Position >= 1);
+  ASSERT (Buffer[*Position - 1] == '/');
+  if (*Position == 1) {
+    return;
+  }
+  (*Position)--;
+}
+
+/**
+  Produce one character in the parser's output buffer.
+
+  @param[out] Buffer       The parser's output buffer. On return, Char8 will
+                           have been written.
+
+  @param[in,out] Position  On entry, points at the next character to produce
+                           (i.e., right past the end of the output written by
+                           the parser thus far). On return, Position is
+                           incremented by one.
+
+  @param[in] Size          Total allocated size of the parser's output buffer.
+                           Used for sanity-checking.
+
+  @param[in] Char8         The character to place in the output buffer.
+**/
+STATIC
+VOID
+ParserCopy (
+     OUT CHAR8 *Buffer,
+  IN OUT UINTN *Position,
+  IN     UINTN Size,
+  IN     CHAR8 Char8
+  )
+{
+  ASSERT (*Position < Size);
+  Buffer[(*Position)++] = Char8;
+}
+
+/**
+  Rewind the last single-dot in the parser's output buffer.
+
+  @param[in] Buffer        The parser's output buffer. Only used for
+                           sanity-checking.
+
+  @param[in,out] Position  On entry, points at the next character to produce
+                           (i.e., right past the end of the output written by
+                           the parser thus far); the parser's output buffer
+                           ends with the characters ('/', '.'). On return, the
+                           dot is rewound by decrementing Position by one; a
+                           slash character will reside at the new end of the
+                           parser's output buffer.
+**/
+STATIC
+VOID
+ParserRewindDot (
+  IN     CHAR8 *Buffer,
+  IN OUT UINTN *Position
+  )
+{
+  ASSERT (*Position >= 2);
+  ASSERT (Buffer[*Position - 1] == '.');
+  ASSERT (Buffer[*Position - 2] == '/');
+  (*Position)--;
+}
+
+/**
+  Rewind the last dot-dot in the parser's output buffer.
+
+  @param[in] Buffer        The parser's output buffer. Only used for
+                           sanity-checking.
+
+  @param[in,out] Position  On entry, points at the next character to produce
+                           (i.e., right past the end of the output written by
+                           the parser thus far); the parser's output buffer
+                           ends with the characters ('/', '.', '.'). On return,
+                           the ('.', '.') pair is rewound unconditionally, by
+                           decrementing Position by two; a slash character
+                           resides at the new end of the parser's output
+                           buffer.
+
+                           If this slash character stands for the root
+                           directory, then RootEscape is set to TRUE.
+
+                           Otherwise (i.e., if this slash character is not the
+                           one standing for the root directory), then the slash
+                           character, and the pathname component preceding it,
+                           are removed by decrementing Position further. In
+                           this case, the slash character preceding the removed
+                           pathname component will reside at the new end of the
+                           parser's output buffer.
+
+  @param[out] RootEscape   Set to TRUE on output if the dot-dot component tries
+                           to escape the root directory, as described above.
+                           Otherwise, RootEscape is not modified.
+**/
+STATIC
+VOID
+ParserRewindDotDot (
+  IN     CHAR8   *Buffer,
+  IN OUT UINTN   *Position,
+     OUT BOOLEAN *RootEscape
+
+  )
+{
+  ASSERT (*Position >= 3);
+  ASSERT (Buffer[*Position - 1] == '.');
+  ASSERT (Buffer[*Position - 2] == '.');
+  ASSERT (Buffer[*Position - 3] == '/');
+  (*Position) -= 2;
+
+  if (*Position == 1) {
+    //
+    // Root directory slash reached; don't try to climb higher.
+    //
+    *RootEscape = TRUE;
+    return;
+  }
+
+  //
+  // Skip slash.
+  //
+  (*Position)--;
+  //
+  // Scan until next slash to the left.
+  //
+  do {
+    ASSERT (*Position > 0);
+    (*Position)--;
+  } while (Buffer[*Position] != '/');
+  (*Position)++;
+}
+
+/**
+  Append the UEFI-style RhsPath16 to the POSIX-style, canonical format
+  LhsPath8. Output the POSIX-style, canonical format result in ResultPath, as a
+  dynamically allocated string.
+
+  Canonicalization (aka sanitization) establishes the following properties:
+  - ResultPath is absolute (starts with "/"),
+  - dot (.) and dot-dot (..) components are resolved/eliminated in ResultPath,
+    with the usual semantics,
+  - ResultPath uses forward slashes,
+  - sequences of slashes are squashed in ResultPath,
+  - the printable ASCII character set covers ResultPath,
+  - CHAR8 encoding is used in ResultPath,
+  - no trailing slash present in ResultPath except for the standalone root
+    directory,
+  - the length of ResultPath is at most VIRTIO_FS_MAX_PATHNAME_LENGTH.
+
+  Any dot-dot in RhsPath16 that would remove the root directory is dropped, and
+  reported through RootEscape, without failing the function call.
+
+  @param[in] LhsPath8      Identifies the base directory. The caller is
+                           responsible for ensuring that LhsPath8 conform to
+                           the above canonical pathname format on entry.
+
+  @param[in] RhsPath16     Identifies the desired file with a UEFI-style CHAR16
+                           pathname. If RhsPath16 starts with a backslash, then
+                           RhsPath16 is considered absolute, and LhsPath8 is
+                           ignored; RhsPath16 is sanitized in isolation, for
+                           producing ResultPath8. Otherwise (i.e., if RhsPath16
+                           is relative), RhsPath16 is transliterated to CHAR8,
+                           and naively appended to LhsPath8. The resultant
+                           fused pathname is then sanitized, to produce
+                           ResultPath8.
+
+  @param[out] ResultPath8  The POSIX-style, canonical format pathname that
+                           leads to the file desired by the caller. After use,
+                           the caller is responsible for freeing ResultPath8.
+
+  @param[out] RootEscape   Set to TRUE if at least one dot-dot component in
+                           RhsPath16 attempted to escape the root directory;
+                           set to FALSE otherwise.
+
+  @retval EFI_SUCCESS            ResultPath8 has been produced. RootEscape has
+                                 been output.
+
+  @retval EFI_INVALID_PARAMETER  RhsPath16 is zero-length.
+
+  @retval EFI_INVALID_PARAMETER  RhsPath16 failed the
+                                 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
+
+  @retval EFI_OUT_OF_RESOURCES   Memory allocation failed.
+
+  @retval EFI_OUT_OF_RESOURCES   ResultPath8 would have failed the
+                                 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
+
+  @retval EFI_UNSUPPORTED        RhsPath16 contains a character that either
+                                 falls outside of the printable ASCII set, or
+                                 is a forward slash.
+**/
+EFI_STATUS
+VirtioFsAppendPath (
+  IN     CHAR8   *LhsPath8,
+  IN     CHAR16  *RhsPath16,
+     OUT CHAR8   **ResultPath8,
+     OUT BOOLEAN *RootEscape
+  )
+{
+  UINTN        RhsLen;
+  CHAR8        *RhsPath8;
+  UINTN        Idx;
+  EFI_STATUS   Status;
+  UINTN        SizeToSanitize;
+  CHAR8        *BufferToSanitize;
+  CHAR8        *SanitizedBuffer;
+  PARSER_STATE State;
+  UINTN        SanitizedPosition;
+
+  //
+  // Appending an empty pathname is not allowed.
+  //
+  RhsLen = StrLen (RhsPath16);
+  if (RhsLen == 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+  //
+  // Enforce length restriction on RhsPath16.
+  //
+  if (RhsLen > VIRTIO_FS_MAX_PATHNAME_LENGTH) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Transliterate RhsPath16 to RhsPath8 by:
+  // - rejecting RhsPath16 if a character outside of printable ASCII is seen,
+  // - rejecting RhsPath16 if a forward slash is seen,
+  // - replacing backslashes with forward slashes,
+  // - casting the characters from CHAR16 to CHAR8.
+  //
+  RhsPath8 = AllocatePool (RhsLen + 1);
+  if (RhsPath8 == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  for (Idx = 0; RhsPath16[Idx] != L'\0'; Idx++) {
+    if (RhsPath16[Idx] < 0x20 || RhsPath16[Idx] > 0x7E ||
+        RhsPath16[Idx] == L'/') {
+      Status = EFI_UNSUPPORTED;
+      goto FreeRhsPath8;
+    }
+    RhsPath8[Idx] = (CHAR8)((RhsPath16[Idx] == L'\\') ? L'/' : RhsPath16[Idx]);
+  }
+  RhsPath8[Idx++] = '\0';
+
+  //
+  // Now prepare the input for the canonicalization (squashing of sequences of
+  // forward slashes, and eliminating . (dot) and .. (dot-dot) pathname
+  // components).
+  //
+  // The sanitized path can never be longer than the naive concatenation of the
+  // left hand side and right hand side paths, so we'll use the catenated size
+  // for allocating the sanitized output too.
+  //
+  if (RhsPath8[0] == '/') {
+    //
+    // If the right hand side path is absolute, then it is not appended to the
+    // left hand side path -- it *replaces* the left hand side path.
+    //
+    SizeToSanitize = RhsLen + 1;
+    BufferToSanitize = RhsPath8;
+  } else {
+    //
+    // If the right hand side path is relative, then it is appended (naively)
+    // to the left hand side.
+    //
+    UINTN LhsLen;
+
+    LhsLen = AsciiStrLen (LhsPath8);
+    SizeToSanitize = LhsLen + 1 + RhsLen + 1;
+    BufferToSanitize = AllocatePool (SizeToSanitize);
+    if (BufferToSanitize == NULL) {
+      Status = EFI_OUT_OF_RESOURCES;
+      goto FreeRhsPath8;
+    }
+    CopyMem (BufferToSanitize, LhsPath8, LhsLen);
+    BufferToSanitize[LhsLen] = '/';
+    CopyMem (BufferToSanitize + LhsLen + 1, RhsPath8, RhsLen + 1);
+  }
+
+  //
+  // Allocate the output buffer.
+  //
+  SanitizedBuffer = AllocatePool (SizeToSanitize);
+  if (SanitizedBuffer == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto FreeBufferToSanitize;
+  }
+
+  //
+  // State machine for parsing the input and producing the canonical output
+  // follows.
+  //
+  *RootEscape       = FALSE;
+  Idx               = 0;
+  State             = ParserInit;
+  SanitizedPosition = 0;
+  do {
+    CHAR8 Chr8;
+
+    ASSERT (Idx < SizeToSanitize);
+    Chr8 = BufferToSanitize[Idx++];
+
+    switch (State) {
+    case ParserInit: // just starting
+      ASSERT (Chr8 == '/');
+      ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
+      State = ParserSlash;
+      break;
+
+    case ParserSlash: // slash(es) seen
+      switch (Chr8) {
+      case '\0':
+        ParserStripSlash (SanitizedBuffer, &SanitizedPosition);
+        ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
+        State = ParserEnd;
+        break;
+      case '/':
+        //
+        // skip & stay in same state
+        //
+        break;
+      case '.':
+        ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
+        State = ParserDot;
+        break;
+      default:
+        ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
+        State = ParserNormal;
+        break;
+      }
+      break;
+
+    case ParserDot: // one dot seen since last slash
+      switch (Chr8) {
+      case '\0':
+        ParserRewindDot (SanitizedBuffer, &SanitizedPosition);
+        ParserStripSlash (SanitizedBuffer, &SanitizedPosition);
+        ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
+        State = ParserEnd;
+        break;
+      case '/':
+        ParserRewindDot (SanitizedBuffer, &SanitizedPosition);
+        State = ParserSlash;
+        break;
+      case '.':
+        ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
+        State = ParserDotDot;
+        break;
+      default:
+        ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
+        State = ParserNormal;
+        break;
+      }
+      break;
+
+    case ParserDotDot: // two dots seen since last slash
+      switch (Chr8) {
+      case '\0':
+        ParserRewindDotDot (SanitizedBuffer, &SanitizedPosition, RootEscape);
+        ParserStripSlash (SanitizedBuffer, &SanitizedPosition);
+        ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
+        State = ParserEnd;
+        break;
+      case '/':
+        ParserRewindDotDot (SanitizedBuffer, &SanitizedPosition, RootEscape);
+        State = ParserSlash;
+        break;
+      case '.':
+        //
+        // fall through
+        //
+      default:
+        ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
+        State = ParserNormal;
+        break;
+      }
+      break;
+
+    case ParserNormal: // a different sequence seen
+      switch (Chr8) {
+      case '\0':
+        ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
+        State = ParserEnd;
+        break;
+      case '/':
+        ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
+        State = ParserSlash;
+        break;
+      case '.':
+        //
+        // fall through
+        //
+      default:
+        //
+        // copy and stay in same state
+        //
+        ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
+        break;
+      }
+      break;
+
+    default:
+      ASSERT (FALSE);
+      break;
+    }
+  } while (State != ParserEnd);
+
+  //
+  // Ensure length invariant on ResultPath8.
+  //
+  ASSERT (SanitizedPosition >= 2);
+  if (SanitizedPosition - 1 > VIRTIO_FS_MAX_PATHNAME_LENGTH) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto FreeSanitizedBuffer;
+  }
+
+  *ResultPath8    = SanitizedBuffer;
+  SanitizedBuffer = NULL;
+  Status          = EFI_SUCCESS;
+  //
+  // Fall through.
+  //
+FreeSanitizedBuffer:
+  if (SanitizedBuffer != NULL) {
+    FreePool (SanitizedBuffer);
+  }
+
+FreeBufferToSanitize:
+  if (RhsPath8[0] != '/') {
+    FreePool (BufferToSanitize);
+  }
+
+FreeRhsPath8:
+  FreePool (RhsPath8);
+  return Status;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 17/48] OvmfPkg/VirtioFsDxe: manage path lifecycle in OpenVolume, Close, Delete
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (15 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 16/48] OvmfPkg/VirtioFsDxe: add helper for appending and sanitizing paths Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 18/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_OPEN Laszlo Ersek
                   ` (31 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Add a canonical pathname field to VIRTIO_FS_FILE.

Initialize the new field in EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume().

Release the new field in EFI_FILE_PROTOCOL.Close() and
EFI_FILE_PROTOCOL.Delete().

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>
---
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h        |  1 +
 OvmfPkg/VirtioFsDxe/SimpleFsClose.c      |  1 +
 OvmfPkg/VirtioFsDxe/SimpleFsDelete.c     |  1 +
 OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c | 13 ++++++++++++-
 4 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index f4fed64c7217..487d215c7f38 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -132,16 +132,17 @@ typedef struct {
 //
 typedef struct {
   UINT64            Signature;
   EFI_FILE_PROTOCOL SimpleFile;
   BOOLEAN           IsDirectory;
   BOOLEAN           IsOpenForWriting;
   VIRTIO_FS         *OwnerFs;
   LIST_ENTRY        OpenFilesEntry;
+  CHAR8             *CanonicalPathname;
   //
   // In the FUSE wire protocol, every request except FUSE_INIT refers to a
   // file, namely by the "VIRTIO_FS_FUSE_REQUEST.NodeId" field; that is, by the
   // inode number of the file. However, some of the FUSE requests that we need
   // for some of the EFI_FILE_PROTOCOL member functions require an open file
   // handle *in addition* to the inode number. For simplicity, whenever a
   // VIRTIO_FS_FILE object is created, primarily defined by its NodeId field,
   // we also *open* the referenced file at once, and save the returned file
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsClose.c b/OvmfPkg/VirtioFsDxe/SimpleFsClose.c
index bc91ad726b2c..04b4f2c382d7 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsClose.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsClose.c
@@ -54,11 +54,12 @@ VirtioFsSimpleFileClose (
     VirtioFsFuseForget (VirtioFs, VirtioFsFile->NodeId);
   }
 
   //
   // One fewer file left open for the owner filesystem.
   //
   RemoveEntryList (&VirtioFsFile->OpenFilesEntry);
 
+  FreePool (VirtioFsFile->CanonicalPathname);
   FreePool (VirtioFsFile);
   return EFI_SUCCESS;
 }
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c b/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c
index bbad64bf7886..e2fc2d72dfeb 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c
@@ -58,11 +58,12 @@ VirtioFsSimpleFileDelete (
     VirtioFsFuseForget (VirtioFs, VirtioFsFile->NodeId);
   }
 
   //
   // One fewer file left open for the owner filesystem.
   //
   RemoveEntryList (&VirtioFsFile->OpenFilesEntry);
 
+  FreePool (VirtioFsFile->CanonicalPathname);
   FreePool (VirtioFsFile);
   return Status;
 }
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c b/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
index 67d2deb6bdf2..9c0ab434c186 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
@@ -23,32 +23,39 @@ EFIAPI
 VirtioFsOpenVolume (
   IN  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
   OUT EFI_FILE_PROTOCOL               **Root
   )
 {
   VIRTIO_FS      *VirtioFs;
   VIRTIO_FS_FILE *VirtioFsFile;
   EFI_STATUS     Status;
+  CHAR8          *CanonicalPathname;
   UINT64         RootDirHandle;
 
   VirtioFs = VIRTIO_FS_FROM_SIMPLE_FS (This);
 
   VirtioFsFile = AllocatePool (sizeof *VirtioFsFile);
   if (VirtioFsFile == NULL) {
     return EFI_OUT_OF_RESOURCES;
   }
 
+  CanonicalPathname = AllocateCopyPool (sizeof "/", "/");
+  if (CanonicalPathname == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto FreeVirtioFsFile;
+  }
+
   //
   // Open the root directory.
   //
   Status = VirtioFsFuseOpenDir (VirtioFs, VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID,
              &RootDirHandle);
   if (EFI_ERROR (Status)) {
-    goto FreeVirtioFsFile;
+    goto FreeCanonicalPathname;
   }
 
   //
   // Populate the new VIRTIO_FS_FILE object.
   //
   VirtioFsFile->Signature              = VIRTIO_FS_FILE_SIG;
   VirtioFsFile->SimpleFile.Revision    = EFI_FILE_PROTOCOL_REVISION;
   VirtioFsFile->SimpleFile.Open        = VirtioFsSimpleFileOpen;
@@ -59,24 +66,28 @@ VirtioFsOpenVolume (
   VirtioFsFile->SimpleFile.GetPosition = VirtioFsSimpleFileGetPosition;
   VirtioFsFile->SimpleFile.SetPosition = VirtioFsSimpleFileSetPosition;
   VirtioFsFile->SimpleFile.GetInfo     = VirtioFsSimpleFileGetInfo;
   VirtioFsFile->SimpleFile.SetInfo     = VirtioFsSimpleFileSetInfo;
   VirtioFsFile->SimpleFile.Flush       = VirtioFsSimpleFileFlush;
   VirtioFsFile->IsDirectory            = TRUE;
   VirtioFsFile->IsOpenForWriting       = FALSE;
   VirtioFsFile->OwnerFs                = VirtioFs;
+  VirtioFsFile->CanonicalPathname      = CanonicalPathname;
   VirtioFsFile->NodeId                 = VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID;
   VirtioFsFile->FuseHandle             = RootDirHandle;
 
   //
   // One more file open for the filesystem.
   //
   InsertTailList (&VirtioFs->OpenFiles, &VirtioFsFile->OpenFilesEntry);
 
   *Root = &VirtioFsFile->SimpleFile;
   return EFI_SUCCESS;
 
+FreeCanonicalPathname:
+  FreePool (CanonicalPathname);
+
 FreeVirtioFsFile:
   FreePool (VirtioFsFile);
 
   return Status;
 }
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 18/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_OPEN
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (16 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 17/48] OvmfPkg/VirtioFsDxe: manage path lifecycle in OpenVolume, Close, Delete Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 19/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_MKDIR Laszlo Ersek
                   ` (30 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Add the VirtioFsFuseOpen() function, for sending the FUSE_OPEN command to
the Virtio Filesystem device.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  35 +++---
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |   8 ++
 OvmfPkg/VirtioFsDxe/FuseOpen.c              | 126 ++++++++++++++++++++
 4 files changed, 156 insertions(+), 14 deletions(-)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index fec2f4be531f..7b6f30335577 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -76,21 +76,28 @@ typedef struct {
 #define VIRTIO_FS_FUSE_MAJOR  7
 #define VIRTIO_FS_FUSE_MINOR 31
 
 //
 // The inode number of the root directory.
 //
 #define VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID 1
 
+//
+// Flags for VirtioFsFuseOpOpen.
+//
+#define VIRTIO_FS_FUSE_OPEN_REQ_F_RDONLY 0
+#define VIRTIO_FS_FUSE_OPEN_REQ_F_RDWR   2
+
 //
 // FUSE operation codes.
 //
 typedef enum {
   VirtioFsFuseOpForget      =  2,
+  VirtioFsFuseOpOpen        = 14,
   VirtioFsFuseOpRelease     = 18,
   VirtioFsFuseOpFsync       = 20,
   VirtioFsFuseOpFlush       = 25,
   VirtioFsFuseOpInit        = 26,
   VirtioFsFuseOpOpenDir     = 27,
   VirtioFsFuseOpReleaseDir  = 29,
   VirtioFsFuseOpFsyncDir    = 30,
 } VIRTIO_FS_FUSE_OPCODE;
@@ -118,16 +125,30 @@ typedef struct {
 
 //
 // Header for VirtioFsFuseOpForget.
 //
 typedef struct {
   UINT64 NumberOfLookups;
 } VIRTIO_FS_FUSE_FORGET_REQUEST;
 
+//
+// Headers for VirtioFsFuseOpOpen and VirtioFsFuseOpOpenDir.
+//
+typedef struct {
+  UINT32 Flags;
+  UINT32 Unused;
+} VIRTIO_FS_FUSE_OPEN_REQUEST;
+
+typedef struct {
+  UINT64 FileHandle;
+  UINT32 OpenFlags;
+  UINT32 Padding;
+} VIRTIO_FS_FUSE_OPEN_RESPONSE;
+
 //
 // Header for VirtioFsFuseOpRelease and VirtioFsFuseOpReleaseDir.
 //
 typedef struct {
   UINT64 FileHandle;
   UINT32 Flags;
   UINT32 ReleaseFlags;
   UINT64 LockOwner;
@@ -170,25 +191,11 @@ typedef struct {
   UINT16 MaxBackground;
   UINT16 CongestionThreshold;
   UINT32 MaxWrite;
   UINT32 TimeGran;
   UINT16 MaxPages;
   UINT16 MapAlignment;
   UINT32 Unused[8];
 } VIRTIO_FS_FUSE_INIT_RESPONSE;
-
-//
-// Headers for VirtioFsFuseOpOpenDir.
-//
-typedef struct {
-  UINT32 Flags;
-  UINT32 Unused;
-} VIRTIO_FS_FUSE_OPEN_REQUEST;
-
-typedef struct {
-  UINT64 FileHandle;
-  UINT32 OpenFlags;
-  UINT32 Padding;
-} VIRTIO_FS_FUSE_OPEN_RESPONSE;
 #pragma pack ()
 
 #endif // VIRTIO_FS_H_
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index 0c92bccdac86..dc9a12820f3d 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -81,16 +81,17 @@ [Packages]
   OvmfPkg/OvmfPkg.dec
 
 [Sources]
   DriverBinding.c
   FuseFlush.c
   FuseForget.c
   FuseFsync.c
   FuseInit.c
+  FuseOpen.c
   FuseOpenDir.c
   FuseRelease.c
   Helpers.c
   SimpleFsClose.c
   SimpleFsDelete.c
   SimpleFsFlush.c
   SimpleFsGetInfo.c
   SimpleFsGetPosition.c
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 487d215c7f38..c13258a2e08f 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -230,16 +230,24 @@ VirtioFsAppendPath (
 //
 
 EFI_STATUS
 VirtioFsFuseForget (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    NodeId
   );
 
+EFI_STATUS
+VirtioFsFuseOpen (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId,
+  IN     BOOLEAN   ReadWrite,
+     OUT UINT64    *FuseHandle
+  );
+
 EFI_STATUS
 VirtioFsFuseReleaseFileOrDir (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    NodeId,
   IN     UINT64    FuseHandle,
   IN     BOOLEAN   IsDir
   );
 
diff --git a/OvmfPkg/VirtioFsDxe/FuseOpen.c b/OvmfPkg/VirtioFsDxe/FuseOpen.c
new file mode 100644
index 000000000000..b731580729b6
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/FuseOpen.c
@@ -0,0 +1,126 @@
+/** @file
+  FUSE_OPEN wrapper for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+/**
+  Send a FUSE_OPEN request to the Virtio Filesystem device, for opening a
+  regular file.
+
+  The function may only be called after VirtioFsFuseInitSession() returns
+  successfully and before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_OPEN
+                           request to. On output, the FUSE request counter
+                           "VirtioFs->RequestId" will have been incremented.
+
+  @param[in] NodeId        The inode number of the regular file to open.
+
+  @param[in] ReadWrite     If TRUE, open the regular file in read-write mode.
+                           If FALSE, open the regular file in read-only mode.
+
+  @param[out] FuseHandle   The open handle to the regular file, returned by the
+                           Virtio Filesystem device.
+
+  @retval EFI_SUCCESS  The regular file has been opened.
+
+  @return              The "errno" value mapped to an EFI_STATUS code, if the
+                       Virtio Filesystem device explicitly reported an error.
+
+  @return              Error codes propagated from VirtioFsSgListsValidate(),
+                       VirtioFsFuseNewRequest(), VirtioFsSgListsSubmit(),
+                       VirtioFsFuseCheckResponse().
+**/
+EFI_STATUS
+VirtioFsFuseOpen (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId,
+  IN     BOOLEAN   ReadWrite,
+     OUT UINT64    *FuseHandle
+  )
+{
+  VIRTIO_FS_FUSE_REQUEST        CommonReq;
+  VIRTIO_FS_FUSE_OPEN_REQUEST   OpenReq;
+  VIRTIO_FS_IO_VECTOR           ReqIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST ReqSgList;
+  VIRTIO_FS_FUSE_RESPONSE       CommonResp;
+  VIRTIO_FS_FUSE_OPEN_RESPONSE  OpenResp;
+  VIRTIO_FS_IO_VECTOR           RespIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST RespSgList;
+  EFI_STATUS                    Status;
+
+  //
+  // Set up the scatter-gather lists.
+  //
+  ReqIoVec[0].Buffer = &CommonReq;
+  ReqIoVec[0].Size   = sizeof CommonReq;
+  ReqIoVec[1].Buffer = &OpenReq;
+  ReqIoVec[1].Size   = sizeof OpenReq;
+  ReqSgList.IoVec    = ReqIoVec;
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);
+
+  RespIoVec[0].Buffer = &CommonResp;
+  RespIoVec[0].Size   = sizeof CommonResp;
+  RespIoVec[1].Buffer = &OpenResp;
+  RespIoVec[1].Size   = sizeof OpenResp;
+  RespSgList.IoVec    = RespIoVec;
+  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);
+
+  //
+  // Validate the scatter-gather lists; calculate the total transfer sizes.
+  //
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the common request header.
+  //
+  Status = VirtioFsFuseNewRequest (VirtioFs, &CommonReq, ReqSgList.TotalSize,
+             VirtioFsFuseOpOpen, NodeId);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the FUSE_OPEN-specific fields.
+  //
+  OpenReq.Flags  = (ReadWrite ?
+                    VIRTIO_FS_FUSE_OPEN_REQ_F_RDWR :
+                    VIRTIO_FS_FUSE_OPEN_REQ_F_RDONLY);
+  OpenReq.Unused = 0;
+
+  //
+  // Submit the request.
+  //
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Verify the response (all response buffers are fixed size).
+  //
+  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);
+  if (EFI_ERROR (Status)) {
+    if (Status == EFI_DEVICE_ERROR) {
+      DEBUG ((DEBUG_ERROR, "%a: Label=\"%s\" NodeId=%Lu ReadWrite=%d "
+        "Errno=%d\n", __FUNCTION__, VirtioFs->Label, NodeId, ReadWrite,
+        CommonResp.Error));
+      Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
+    }
+    return Status;
+  }
+
+  //
+  // Output the open handle.
+  //
+  *FuseHandle = OpenResp.FileHandle;
+  return EFI_SUCCESS;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 19/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_MKDIR
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (17 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 18/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_OPEN Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 20/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_CREATE Laszlo Ersek
                   ` (29 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Add the VirtioFsFuseMkDir() function, for sending the FUSE_MKDIR command
to the Virtio Filesystem device.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  55 ++++++++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |   8 ++
 OvmfPkg/VirtioFsDxe/FuseMkDir.c             | 134 ++++++++++++++++++++
 4 files changed, 198 insertions(+)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index 7b6f30335577..9ffeb9c8c954 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -76,27 +76,35 @@ typedef struct {
 #define VIRTIO_FS_FUSE_MAJOR  7
 #define VIRTIO_FS_FUSE_MINOR 31
 
 //
 // The inode number of the root directory.
 //
 #define VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID 1
 
+//
+// File mode bitmasks.
+//
+#define VIRTIO_FS_FUSE_MODE_PERM_RWXU 0000700u
+#define VIRTIO_FS_FUSE_MODE_PERM_RWXG 0000070u
+#define VIRTIO_FS_FUSE_MODE_PERM_RWXO 0000007u
+
 //
 // Flags for VirtioFsFuseOpOpen.
 //
 #define VIRTIO_FS_FUSE_OPEN_REQ_F_RDONLY 0
 #define VIRTIO_FS_FUSE_OPEN_REQ_F_RDWR   2
 
 //
 // FUSE operation codes.
 //
 typedef enum {
   VirtioFsFuseOpForget      =  2,
+  VirtioFsFuseOpMkDir       =  9,
   VirtioFsFuseOpOpen        = 14,
   VirtioFsFuseOpRelease     = 18,
   VirtioFsFuseOpFsync       = 20,
   VirtioFsFuseOpFlush       = 25,
   VirtioFsFuseOpInit        = 26,
   VirtioFsFuseOpOpenDir     = 27,
   VirtioFsFuseOpReleaseDir  = 29,
   VirtioFsFuseOpFsyncDir    = 30,
@@ -118,23 +126,70 @@ typedef struct {
 } VIRTIO_FS_FUSE_REQUEST;
 
 typedef struct {
   UINT32 Len;
   INT32  Error;
   UINT64 Unique;
 } VIRTIO_FS_FUSE_RESPONSE;
 
+//
+// Structure with which the Virtio Filesystem device reports a NodeId to the
+// FUSE client (i.e., to the Virtio Filesystem driver). This structure is a
+// part of the response headers for operations that inform the FUSE client of
+// an inode.
+//
+typedef struct {
+  UINT64 NodeId;
+  UINT64 Generation;
+  UINT64 EntryValid;
+  UINT64 AttrValid;
+  UINT32 EntryValidNsec;
+  UINT32 AttrValidNsec;
+} VIRTIO_FS_FUSE_NODE_RESPONSE;
+
+//
+// Structure describing the host-side attributes of an inode. This structure is
+// a part of the response headers for operations that inform the FUSE client of
+// an inode.
+//
+typedef struct {
+  UINT64 Ino;
+  UINT64 Size;
+  UINT64 Blocks;
+  UINT64 Atime;
+  UINT64 Mtime;
+  UINT64 Ctime;
+  UINT32 AtimeNsec;
+  UINT32 MtimeNsec;
+  UINT32 CtimeNsec;
+  UINT32 Mode;
+  UINT32 Nlink;
+  UINT32 Uid;
+  UINT32 Gid;
+  UINT32 Rdev;
+  UINT32 Blksize;
+  UINT32 Padding;
+} VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE;
+
 //
 // Header for VirtioFsFuseOpForget.
 //
 typedef struct {
   UINT64 NumberOfLookups;
 } VIRTIO_FS_FUSE_FORGET_REQUEST;
 
+//
+// Header for VirtioFsFuseOpMkDir.
+//
+typedef struct {
+  UINT32 Mode;
+  UINT32 Umask;
+} VIRTIO_FS_FUSE_MKDIR_REQUEST;
+
 //
 // Headers for VirtioFsFuseOpOpen and VirtioFsFuseOpOpenDir.
 //
 typedef struct {
   UINT32 Flags;
   UINT32 Unused;
 } VIRTIO_FS_FUSE_OPEN_REQUEST;
 
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index dc9a12820f3d..eebb86f7c275 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -81,16 +81,17 @@ [Packages]
   OvmfPkg/OvmfPkg.dec
 
 [Sources]
   DriverBinding.c
   FuseFlush.c
   FuseForget.c
   FuseFsync.c
   FuseInit.c
+  FuseMkDir.c
   FuseOpen.c
   FuseOpenDir.c
   FuseRelease.c
   Helpers.c
   SimpleFsClose.c
   SimpleFsDelete.c
   SimpleFsFlush.c
   SimpleFsGetInfo.c
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index c13258a2e08f..8fd56bdbf6e1 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -230,16 +230,24 @@ VirtioFsAppendPath (
 //
 
 EFI_STATUS
 VirtioFsFuseForget (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    NodeId
   );
 
+EFI_STATUS
+VirtioFsFuseMkDir (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    ParentNodeId,
+  IN     CHAR8     *Name,
+     OUT UINT64    *NodeId
+  );
+
 EFI_STATUS
 VirtioFsFuseOpen (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    NodeId,
   IN     BOOLEAN   ReadWrite,
      OUT UINT64    *FuseHandle
   );
 
diff --git a/OvmfPkg/VirtioFsDxe/FuseMkDir.c b/OvmfPkg/VirtioFsDxe/FuseMkDir.c
new file mode 100644
index 000000000000..541ff330cd1e
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/FuseMkDir.c
@@ -0,0 +1,134 @@
+/** @file
+  FUSE_MKDIR wrapper for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h> // AsciiStrSize()
+
+#include "VirtioFsDxe.h"
+
+/**
+  Send a FUSE_MKDIR request to the Virtio Filesystem device, for creating a
+  directory.
+
+  The function may only be called after VirtioFsFuseInitSession() returns
+  successfully and before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_MKDIR
+                           request to. On output, the FUSE request counter
+                           "VirtioFs->RequestId" will have been incremented.
+
+  @param[in] ParentNodeId  The inode number of the direct parent directory of
+                           the directory to create.
+
+  @param[in] Name          The single-component filename of the directory to
+                           create, under the parent directory identified by
+                           ParentNodeId.
+
+  @param[out] NodeId       The inode number of the new directory.
+
+  @retval EFI_SUCCESS  The directory has been created.
+
+  @return              The "errno" value mapped to an EFI_STATUS code, if the
+                       Virtio Filesystem device explicitly reported an error.
+
+  @return              Error codes propagated from VirtioFsSgListsValidate(),
+                       VirtioFsFuseNewRequest(), VirtioFsSgListsSubmit(),
+                       VirtioFsFuseCheckResponse().
+**/
+EFI_STATUS
+VirtioFsFuseMkDir (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    ParentNodeId,
+  IN     CHAR8     *Name,
+     OUT UINT64    *NodeId
+  )
+{
+  VIRTIO_FS_FUSE_REQUEST             CommonReq;
+  VIRTIO_FS_FUSE_MKDIR_REQUEST       MkDirReq;
+  VIRTIO_FS_IO_VECTOR                ReqIoVec[3];
+  VIRTIO_FS_SCATTER_GATHER_LIST      ReqSgList;
+  VIRTIO_FS_FUSE_RESPONSE            CommonResp;
+  VIRTIO_FS_FUSE_NODE_RESPONSE       NodeResp;
+  VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE AttrResp;
+  VIRTIO_FS_IO_VECTOR                RespIoVec[3];
+  VIRTIO_FS_SCATTER_GATHER_LIST      RespSgList;
+  EFI_STATUS                         Status;
+
+  //
+  // Set up the scatter-gather lists.
+  //
+  ReqIoVec[0].Buffer = &CommonReq;
+  ReqIoVec[0].Size   = sizeof CommonReq;
+  ReqIoVec[1].Buffer = &MkDirReq;
+  ReqIoVec[1].Size   = sizeof MkDirReq;
+  ReqIoVec[2].Buffer = Name;
+  ReqIoVec[2].Size   = AsciiStrSize (Name);
+  ReqSgList.IoVec    = ReqIoVec;
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);
+
+  RespIoVec[0].Buffer = &CommonResp;
+  RespIoVec[0].Size   = sizeof CommonResp;
+  RespIoVec[1].Buffer = &NodeResp;
+  RespIoVec[1].Size   = sizeof NodeResp;
+  RespIoVec[2].Buffer = &AttrResp;
+  RespIoVec[2].Size   = sizeof AttrResp;
+  RespSgList.IoVec    = RespIoVec;
+  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);
+
+  //
+  // Validate the scatter-gather lists; calculate the total transfer sizes.
+  //
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the common request header.
+  //
+  Status = VirtioFsFuseNewRequest (VirtioFs, &CommonReq, ReqSgList.TotalSize,
+             VirtioFsFuseOpMkDir, ParentNodeId);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the FUSE_MKDIR-specific fields.
+  //
+  MkDirReq.Mode  = (VIRTIO_FS_FUSE_MODE_PERM_RWXU |
+                    VIRTIO_FS_FUSE_MODE_PERM_RWXG |
+                    VIRTIO_FS_FUSE_MODE_PERM_RWXO);
+  MkDirReq.Umask = 0;
+
+  //
+  // Submit the request.
+  //
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Verify the response (all response buffers are fixed size).
+  //
+  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);
+  if (EFI_ERROR (Status)) {
+    if (Status == EFI_DEVICE_ERROR) {
+      DEBUG ((DEBUG_ERROR, "%a: Label=\"%s\" ParentNodeId=%Lu Name=\"%a\" "
+        "Errno=%d\n", __FUNCTION__, VirtioFs->Label, ParentNodeId, Name,
+        CommonResp.Error));
+      Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
+    }
+    return Status;
+  }
+
+  //
+  // Output the NodeId of the new directory.
+  //
+  *NodeId = NodeResp.NodeId;
+  return EFI_SUCCESS;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 20/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_CREATE
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (18 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 19/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_MKDIR Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 21/48] OvmfPkg/VirtioFsDxe: convert FUSE inode attributes to EFI_FILE_INFO Laszlo Ersek
                   ` (28 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Add the VirtioFsFuseOpenOrCreate() function, for sending the FUSE_CREATE
command to the Virtio Filesystem device.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  17 +++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |   9 ++
 OvmfPkg/VirtioFsDxe/FuseOpenOrCreate.c      | 155 ++++++++++++++++++++
 4 files changed, 182 insertions(+)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index 9ffeb9c8c954..63aced229e9b 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -80,18 +80,24 @@ typedef struct {
 // The inode number of the root directory.
 //
 #define VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID 1
 
 //
 // File mode bitmasks.
 //
 #define VIRTIO_FS_FUSE_MODE_PERM_RWXU 0000700u
+#define VIRTIO_FS_FUSE_MODE_PERM_RUSR 0000400u
+#define VIRTIO_FS_FUSE_MODE_PERM_WUSR 0000200u
 #define VIRTIO_FS_FUSE_MODE_PERM_RWXG 0000070u
+#define VIRTIO_FS_FUSE_MODE_PERM_RGRP 0000040u
+#define VIRTIO_FS_FUSE_MODE_PERM_WGRP 0000020u
 #define VIRTIO_FS_FUSE_MODE_PERM_RWXO 0000007u
+#define VIRTIO_FS_FUSE_MODE_PERM_ROTH 0000004u
+#define VIRTIO_FS_FUSE_MODE_PERM_WOTH 0000002u
 
 //
 // Flags for VirtioFsFuseOpOpen.
 //
 #define VIRTIO_FS_FUSE_OPEN_REQ_F_RDONLY 0
 #define VIRTIO_FS_FUSE_OPEN_REQ_F_RDWR   2
 
 //
@@ -103,16 +109,17 @@ typedef enum {
   VirtioFsFuseOpOpen        = 14,
   VirtioFsFuseOpRelease     = 18,
   VirtioFsFuseOpFsync       = 20,
   VirtioFsFuseOpFlush       = 25,
   VirtioFsFuseOpInit        = 26,
   VirtioFsFuseOpOpenDir     = 27,
   VirtioFsFuseOpReleaseDir  = 29,
   VirtioFsFuseOpFsyncDir    = 30,
+  VirtioFsFuseOpCreate      = 35,
 } VIRTIO_FS_FUSE_OPCODE;
 
 #pragma pack (1)
 //
 // Request-response headers common to all request types.
 //
 typedef struct {
   UINT32 Len;
@@ -246,11 +253,21 @@ typedef struct {
   UINT16 MaxBackground;
   UINT16 CongestionThreshold;
   UINT32 MaxWrite;
   UINT32 TimeGran;
   UINT16 MaxPages;
   UINT16 MapAlignment;
   UINT32 Unused[8];
 } VIRTIO_FS_FUSE_INIT_RESPONSE;
+
+//
+// Header for VirtioFsFuseOpCreate.
+//
+typedef struct {
+  UINT32 Flags;
+  UINT32 Mode;
+  UINT32 Umask;
+  UINT32 Padding;
+} VIRTIO_FS_FUSE_CREATE_REQUEST;
 #pragma pack ()
 
 #endif // VIRTIO_FS_H_
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index eebb86f7c275..b942baa4a772 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -84,16 +84,17 @@ [Sources]
   DriverBinding.c
   FuseFlush.c
   FuseForget.c
   FuseFsync.c
   FuseInit.c
   FuseMkDir.c
   FuseOpen.c
   FuseOpenDir.c
+  FuseOpenOrCreate.c
   FuseRelease.c
   Helpers.c
   SimpleFsClose.c
   SimpleFsDelete.c
   SimpleFsFlush.c
   SimpleFsGetInfo.c
   SimpleFsGetPosition.c
   SimpleFsOpen.c
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 8fd56bdbf6e1..795cf4ee5d7d 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -281,16 +281,25 @@ VirtioFsFuseInitSession (
 
 EFI_STATUS
 VirtioFsFuseOpenDir (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    NodeId,
      OUT UINT64    *FuseHandle
   );
 
+EFI_STATUS
+VirtioFsFuseOpenOrCreate (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    ParentNodeId,
+  IN     CHAR8     *Name,
+     OUT UINT64    *NodeId,
+     OUT UINT64    *FuseHandle
+  );
+
 //
 // EFI_SIMPLE_FILE_SYSTEM_PROTOCOL member functions for the Virtio Filesystem
 // driver.
 //
 
 EFI_STATUS
 EFIAPI
 VirtioFsOpenVolume (
diff --git a/OvmfPkg/VirtioFsDxe/FuseOpenOrCreate.c b/OvmfPkg/VirtioFsDxe/FuseOpenOrCreate.c
new file mode 100644
index 000000000000..60ab002bcd99
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/FuseOpenOrCreate.c
@@ -0,0 +1,155 @@
+/** @file
+  FUSE_CREATE wrapper for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h> // AsciiStrSize()
+
+#include "VirtioFsDxe.h"
+
+/**
+  Send a FUSE_CREATE request to the Virtio Filesystem device, for opening a
+  regular file with (O_RDWR | O_CREAT) semantics.
+
+  The function may only be called after VirtioFsFuseInitSession() returns
+  successfully and before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_CREATE
+                           request to. On output, the FUSE request counter
+                           "VirtioFs->RequestId" will have been incremented.
+
+  @param[in] ParentNodeId  The inode number of the direct parent directory of
+                           the regular file to open or create.
+
+  @param[in] Name          The single-component filename of the regular file to
+                           open or create, under the parent directory
+                           identified by ParentNodeId.
+
+  @param[out] NodeId       The inode number of the regular file, returned by
+                           the Virtio Filesystem device.
+
+  @param[out] FuseHandle   The open file handle returned by the Virtio
+                           Filesystem device.
+
+  @retval EFI_SUCCESS  The regular file has been opened, and (if necessary)
+                       created.
+
+  @return              The "errno" value mapped to an EFI_STATUS code, if the
+                       Virtio Filesystem device explicitly reported an error.
+
+  @return              Error codes propagated from VirtioFsSgListsValidate(),
+                       VirtioFsFuseNewRequest(), VirtioFsSgListsSubmit(),
+                       VirtioFsFuseCheckResponse().
+**/
+EFI_STATUS
+VirtioFsFuseOpenOrCreate (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    ParentNodeId,
+  IN     CHAR8     *Name,
+     OUT UINT64    *NodeId,
+     OUT UINT64    *FuseHandle
+  )
+{
+  VIRTIO_FS_FUSE_REQUEST             CommonReq;
+  VIRTIO_FS_FUSE_CREATE_REQUEST      CreateReq;
+  VIRTIO_FS_IO_VECTOR                ReqIoVec[3];
+  VIRTIO_FS_SCATTER_GATHER_LIST      ReqSgList;
+  VIRTIO_FS_FUSE_RESPONSE            CommonResp;
+  VIRTIO_FS_FUSE_NODE_RESPONSE       NodeResp;
+  VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE AttrResp;
+  VIRTIO_FS_FUSE_OPEN_RESPONSE       OpenResp;
+  VIRTIO_FS_IO_VECTOR                RespIoVec[4];
+  VIRTIO_FS_SCATTER_GATHER_LIST      RespSgList;
+  EFI_STATUS                         Status;
+
+  //
+  // Set up the scatter-gather lists.
+  //
+  ReqIoVec[0].Buffer = &CommonReq;
+  ReqIoVec[0].Size   = sizeof CommonReq;
+  ReqIoVec[1].Buffer = &CreateReq;
+  ReqIoVec[1].Size   = sizeof CreateReq;
+  ReqIoVec[2].Buffer = Name;
+  ReqIoVec[2].Size   = AsciiStrSize (Name);
+  ReqSgList.IoVec    = ReqIoVec;
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);
+
+  RespIoVec[0].Buffer = &CommonResp;
+  RespIoVec[0].Size   = sizeof CommonResp;
+  RespIoVec[1].Buffer = &NodeResp;
+  RespIoVec[1].Size   = sizeof NodeResp;
+  RespIoVec[2].Buffer = &AttrResp;
+  RespIoVec[2].Size   = sizeof AttrResp;
+  RespIoVec[3].Buffer = &OpenResp;
+  RespIoVec[3].Size   = sizeof OpenResp;
+  RespSgList.IoVec    = RespIoVec;
+  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);
+
+  //
+  // Validate the scatter-gather lists; calculate the total transfer sizes.
+  //
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the common request header.
+  //
+  Status = VirtioFsFuseNewRequest (VirtioFs, &CommonReq, ReqSgList.TotalSize,
+             VirtioFsFuseOpCreate, ParentNodeId);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the FUSE_CREATE-specific fields.
+  //
+  // VIRTIO_FS_FUSE_OPEN_REQ_F_RDWR is why this request can never open a
+  // directory (EISDIR). And VIRTIO_FS_FUSE_OPEN_REQ_F_RDWR is consistent with
+  // the only OpenMode of EFI_FILE_PROTOCOL.Open() that enables filesystem
+  // object creation -- that is, Create/Read/Write.
+  //
+  CreateReq.Flags   = VIRTIO_FS_FUSE_OPEN_REQ_F_RDWR;
+  CreateReq.Mode    = (VIRTIO_FS_FUSE_MODE_PERM_RUSR |
+                       VIRTIO_FS_FUSE_MODE_PERM_WUSR |
+                       VIRTIO_FS_FUSE_MODE_PERM_RGRP |
+                       VIRTIO_FS_FUSE_MODE_PERM_WGRP |
+                       VIRTIO_FS_FUSE_MODE_PERM_ROTH |
+                       VIRTIO_FS_FUSE_MODE_PERM_WOTH);
+  CreateReq.Umask   = 0;
+  CreateReq.Padding = 0;
+
+  //
+  // Submit the request.
+  //
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Verify the response (all response buffers are fixed size).
+  //
+  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);
+  if (EFI_ERROR (Status)) {
+    if (Status == EFI_DEVICE_ERROR) {
+      DEBUG ((DEBUG_ERROR, "%a: Label=\"%s\" ParentNodeId=%Lu Name=\"%a\" "
+        "Errno=%d\n", __FUNCTION__, VirtioFs->Label, ParentNodeId, Name,
+        CommonResp.Error));
+      Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
+    }
+    return Status;
+  }
+
+  //
+  // Output the NodeId of the (possibly new) regular file. Also output the open
+  // file handle.
+  //
+  *NodeId     = NodeResp.NodeId;
+  *FuseHandle = OpenResp.FileHandle;
+  return EFI_SUCCESS;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 21/48] OvmfPkg/VirtioFsDxe: convert FUSE inode attributes to EFI_FILE_INFO
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (19 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 20/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_CREATE Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:10 ` [edk2 PATCH 22/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_LOOKUP Laszlo Ersek
                   ` (27 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Introduce the VirtioFsFuseAttrToEfiFileInfo() function, for converting
FUSE inode attributes to EFI_FILE_INFO.

The EpochToEfiTime() function from EmbeddedPkg's TimeBaseLib proves
invaluable for converting the file access times.

This is the first time we consume TimeBaseLib in OvmfPkg, so add the
necessary lib class resolution. We need not modify any ArmVirtPkg DSC
files: see commit af5fed90bfbf ("ArmPlatformPkg,ArmVirtPkg: delete
redundant PL031 functions", 2017-05-10).

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>
---
 OvmfPkg/OvmfPkgIa32.dsc                     |   1 +
 OvmfPkg/OvmfPkgIa32X64.dsc                  |   1 +
 OvmfPkg/OvmfPkgX64.dsc                      |   1 +
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |   3 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   2 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |   7 ++
 OvmfPkg/VirtioFsDxe/Helpers.c               | 119 ++++++++++++++++++++
 7 files changed, 134 insertions(+)

diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index 4ff70674fb6e..26a013ec353e 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -120,16 +120,17 @@ [SkuIds]
 [LibraryClasses]
   PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
   TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLib.inf
   ResetSystemLib|OvmfPkg/Library/ResetSystemLib/BaseResetSystemLib.inf
   PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
   BaseMemoryLib|MdePkg/Library/BaseMemoryLibRepStr/BaseMemoryLibRepStr.inf
   BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
   SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf
+  TimeBaseLib|EmbeddedPkg/Library/TimeBaseLib/TimeBaseLib.inf
   BmpSupportLib|MdeModulePkg/Library/BaseBmpSupportLib/BaseBmpSupportLib.inf
   SynchronizationLib|MdePkg/Library/BaseSynchronizationLib/BaseSynchronizationLib.inf
   CpuLib|MdePkg/Library/BaseCpuLib/BaseCpuLib.inf
   PerformanceLib|MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf
   PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf
   CacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf
   UefiDecompressLib|MdePkg/Library/BaseUefiDecompressLib/BaseUefiDecompressLib.inf
   UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index d40a59183c79..10579fe46c5b 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -124,16 +124,17 @@ [SkuIds]
 [LibraryClasses]
   PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
   TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLib.inf
   ResetSystemLib|OvmfPkg/Library/ResetSystemLib/BaseResetSystemLib.inf
   PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
   BaseMemoryLib|MdePkg/Library/BaseMemoryLibRepStr/BaseMemoryLibRepStr.inf
   BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
   SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf
+  TimeBaseLib|EmbeddedPkg/Library/TimeBaseLib/TimeBaseLib.inf
   BmpSupportLib|MdeModulePkg/Library/BaseBmpSupportLib/BaseBmpSupportLib.inf
   SynchronizationLib|MdePkg/Library/BaseSynchronizationLib/BaseSynchronizationLib.inf
   CpuLib|MdePkg/Library/BaseCpuLib/BaseCpuLib.inf
   PerformanceLib|MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf
   PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf
   CacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf
   UefiDecompressLib|MdePkg/Library/BaseUefiDecompressLib/BaseUefiDecompressLib.inf
   UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index ec7886235acf..c9235e48ad62 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -124,16 +124,17 @@ [SkuIds]
 [LibraryClasses]
   PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
   TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLib.inf
   ResetSystemLib|OvmfPkg/Library/ResetSystemLib/BaseResetSystemLib.inf
   PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
   BaseMemoryLib|MdePkg/Library/BaseMemoryLibRepStr/BaseMemoryLibRepStr.inf
   BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
   SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf
+  TimeBaseLib|EmbeddedPkg/Library/TimeBaseLib/TimeBaseLib.inf
   BmpSupportLib|MdeModulePkg/Library/BaseBmpSupportLib/BaseBmpSupportLib.inf
   SynchronizationLib|MdePkg/Library/BaseSynchronizationLib/BaseSynchronizationLib.inf
   CpuLib|MdePkg/Library/BaseCpuLib/BaseCpuLib.inf
   PerformanceLib|MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf
   PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf
   CacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf
   UefiDecompressLib|MdePkg/Library/BaseUefiDecompressLib/BaseUefiDecompressLib.inf
   UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf
diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index 63aced229e9b..5d1d990a2d83 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -79,16 +79,19 @@ typedef struct {
 //
 // The inode number of the root directory.
 //
 #define VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID 1
 
 //
 // File mode bitmasks.
 //
+#define VIRTIO_FS_FUSE_MODE_TYPE_MASK 0170000u
+#define VIRTIO_FS_FUSE_MODE_TYPE_REG  0100000u
+#define VIRTIO_FS_FUSE_MODE_TYPE_DIR  0040000u
 #define VIRTIO_FS_FUSE_MODE_PERM_RWXU 0000700u
 #define VIRTIO_FS_FUSE_MODE_PERM_RUSR 0000400u
 #define VIRTIO_FS_FUSE_MODE_PERM_WUSR 0000200u
 #define VIRTIO_FS_FUSE_MODE_PERM_RWXG 0000070u
 #define VIRTIO_FS_FUSE_MODE_PERM_RGRP 0000040u
 #define VIRTIO_FS_FUSE_MODE_PERM_WGRP 0000020u
 #define VIRTIO_FS_FUSE_MODE_PERM_RWXO 0000007u
 #define VIRTIO_FS_FUSE_MODE_PERM_ROTH 0000004u
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index b942baa4a772..7d7272188465 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -72,16 +72,17 @@
 [Defines]
   INF_VERSION                           = 1.29
   BASE_NAME                             = VirtioFsDxe
   FILE_GUID                             = 7BD9DDF7-8B83-488E-AEC9-24C78610289C
   MODULE_TYPE                           = UEFI_DRIVER
   ENTRY_POINT                           = VirtioFsEntryPoint
 
 [Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
   MdePkg/MdePkg.dec
   OvmfPkg/OvmfPkg.dec
 
 [Sources]
   DriverBinding.c
   FuseFlush.c
   FuseForget.c
   FuseFsync.c
@@ -105,16 +106,17 @@ [Sources]
   SimpleFsWrite.c
   VirtioFsDxe.h
 
 [LibraryClasses]
   BaseLib
   BaseMemoryLib
   DebugLib
   MemoryAllocationLib
+  TimeBaseLib
   UefiBootServicesTableLib
   UefiDriverEntryPoint
   VirtioLib
 
 [Protocols]
   gEfiComponentName2ProtocolGuid        ## PRODUCES
   gEfiDriverBindingProtocolGuid         ## PRODUCES
   gEfiSimpleFileSystemProtocolGuid      ## BY_START
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 795cf4ee5d7d..6cc5257bab40 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -6,16 +6,17 @@
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
 #ifndef VIRTIO_FS_DXE_H_
 #define VIRTIO_FS_DXE_H_
 
 #include <Base.h>                      // SIGNATURE_64()
+#include <Guid/FileInfo.h>             // EFI_FILE_INFO
 #include <IndustryStandard/VirtioFs.h> // VIRTIO_FS_TAG_BYTES
 #include <Library/DebugLib.h>          // CR()
 #include <Protocol/SimpleFileSystem.h> // EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
 #include <Protocol/VirtioDevice.h>     // VIRTIO_DEVICE_PROTOCOL
 #include <Uefi/UefiBaseType.h>         // EFI_EVENT
 
 #define VIRTIO_FS_SIG SIGNATURE_64 ('V', 'I', 'R', 'T', 'I', 'O', 'F', 'S')
 
@@ -220,16 +221,22 @@ VirtioFsErrnoToEfiStatus (
 EFI_STATUS
 VirtioFsAppendPath (
   IN     CHAR8   *LhsPath8,
   IN     CHAR16  *RhsPath16,
      OUT CHAR8   **ResultPath8,
      OUT BOOLEAN *RootEscape
   );
 
+EFI_STATUS
+VirtioFsFuseAttrToEfiFileInfo (
+  IN     VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr,
+     OUT EFI_FILE_INFO                      *FileInfo
+  );
+
 //
 // Wrapper functions for FUSE commands (primitives).
 //
 
 EFI_STATUS
 VirtioFsFuseForget (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    NodeId
diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
index 4a7b59332ca9..fa656bb535b8 100644
--- a/OvmfPkg/VirtioFsDxe/Helpers.c
+++ b/OvmfPkg/VirtioFsDxe/Helpers.c
@@ -4,16 +4,17 @@
   Copyright (C) 2020, Red Hat, Inc.
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
 #include <Library/BaseLib.h>             // StrLen()
 #include <Library/BaseMemoryLib.h>       // CopyMem()
 #include <Library/MemoryAllocationLib.h> // AllocatePool()
+#include <Library/TimeBaseLib.h>         // EpochToEfiTime()
 #include <Library/VirtioLib.h>           // Virtio10WriteFeatures()
 
 #include "VirtioFsDxe.h"
 
 /**
   Read the Virtio Filesystem device configuration structure in full.
 
   @param[in] Virtio   The Virtio protocol underlying the VIRTIO_FS object.
@@ -1584,8 +1585,126 @@ VirtioFsAppendPath (
   if (RhsPath8[0] != '/') {
     FreePool (BufferToSanitize);
   }
 
 FreeRhsPath8:
   FreePool (RhsPath8);
   return Status;
 }
+
+/**
+  Convert select fields of a VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
+  corresponding fields in EFI_FILE_INFO.
+
+  @param[in] FuseAttr   The VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
+                        convert the relevant fields from.
+
+  @param[out] FileInfo  The EFI_FILE_INFO structure to modify. Importantly, the
+                        FileInfo->Size and FileInfo->FileName fields are not
+                        overwritten.
+
+  @retval EFI_SUCCESS      Conversion successful.
+
+  @retval EFI_UNSUPPORTED  The allocated size of the file is inexpressible in
+                           EFI_FILE_INFO.
+
+  @retval EFI_UNSUPPORTED  One of the file access times is inexpressible in
+                           EFI_FILE_INFO.
+
+  @retval EFI_UNSUPPORTED  The file type is inexpressible in EFI_FILE_INFO.
+
+  @retval EFI_UNSUPPORTED  The file is a regular file that has multiple names
+                           on the host side (i.e., its hard link count is
+                           greater than one).
+**/
+EFI_STATUS
+VirtioFsFuseAttrToEfiFileInfo (
+  IN     VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr,
+     OUT EFI_FILE_INFO                      *FileInfo
+  )
+{
+  UINT64   EpochTime[3];
+  EFI_TIME *ConvertedTime[ARRAY_SIZE (EpochTime)];
+  UINTN    Idx;
+
+  FileInfo->FileSize = FuseAttr->Size;
+
+  //
+  // The unit for FuseAttr->Blocks is 512B.
+  //
+  if (FuseAttr->Blocks >= BIT55) {
+    return EFI_UNSUPPORTED;
+  }
+  FileInfo->PhysicalSize = LShiftU64 (FuseAttr->Blocks, 9);
+
+  //
+  // Convert the timestamps. File creation time is not tracked by the Virtio
+  // Filesystem device, so set FileInfo->CreateTime from FuseAttr->Mtime as
+  // well.
+  //
+  EpochTime[0]     = FuseAttr->Mtime;
+  EpochTime[1]     = FuseAttr->Atime;
+  EpochTime[2]     = FuseAttr->Mtime;
+  ConvertedTime[0] = &FileInfo->CreateTime;
+  ConvertedTime[1] = &FileInfo->LastAccessTime;
+  ConvertedTime[2] = &FileInfo->ModificationTime;
+
+  for (Idx = 0; Idx < ARRAY_SIZE (EpochTime); Idx++) {
+    //
+    // EpochToEfiTime() takes a UINTN for seconds.
+    //
+    if (EpochTime[Idx] > MAX_UINTN) {
+      return EFI_UNSUPPORTED;
+    }
+    //
+    // Set the following fields in the converted time: Year, Month, Day, Hour,
+    // Minute, Second, Nanosecond.
+    //
+    EpochToEfiTime ((UINTN)EpochTime[Idx], ConvertedTime[Idx]);
+    //
+    // The times are all expressed in UTC. Consequently, they are not affected
+    // by daylight saving.
+    //
+    ConvertedTime[Idx]->TimeZone = 0;
+    ConvertedTime[Idx]->Daylight = 0;
+    //
+    // Clear the padding fields.
+    //
+    ConvertedTime[Idx]->Pad1 = 0;
+    ConvertedTime[Idx]->Pad2 = 0;
+  }
+
+  //
+  // Set the attributes.
+  //
+  switch (FuseAttr->Mode & VIRTIO_FS_FUSE_MODE_TYPE_MASK) {
+  case VIRTIO_FS_FUSE_MODE_TYPE_DIR:
+    FileInfo->Attribute = EFI_FILE_DIRECTORY;
+    break;
+  case VIRTIO_FS_FUSE_MODE_TYPE_REG:
+    FileInfo->Attribute = 0;
+    break;
+  default:
+    //
+    // Other file types are not supported.
+    //
+    return EFI_UNSUPPORTED;
+  }
+  //
+  // Report the regular file or directory as read-only if all classes lack
+  // write permission.
+  //
+  if ((FuseAttr->Mode & (VIRTIO_FS_FUSE_MODE_PERM_WUSR |
+                         VIRTIO_FS_FUSE_MODE_PERM_WGRP |
+                         VIRTIO_FS_FUSE_MODE_PERM_WOTH)) == 0) {
+    FileInfo->Attribute |= EFI_FILE_READ_ONLY;
+  }
+
+  //
+  // A hard link count greater than 1 is not supported for regular files.
+  //
+  if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) == 0 && FuseAttr->Nlink > 1) {
+    return EFI_UNSUPPORTED;
+  }
+
+  return EFI_SUCCESS;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 22/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_LOOKUP
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (20 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 21/48] OvmfPkg/VirtioFsDxe: convert FUSE inode attributes to EFI_FILE_INFO Laszlo Ersek
@ 2020-12-16 21:10 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 23/48] OvmfPkg/VirtioFsDxe: split canon. path into last parent + last component Laszlo Ersek
                   ` (26 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:10 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Add the VirtioFsFuseLookup() function, for sending the FUSE_LOOKUP command
to the Virtio Filesystem device.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |   6 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |   9 ++
 OvmfPkg/VirtioFsDxe/FuseLookup.c            | 148 ++++++++++++++++++++
 4 files changed, 164 insertions(+)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index 5d1d990a2d83..8a07b3d2eb93 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -76,16 +76,21 @@ typedef struct {
 #define VIRTIO_FS_FUSE_MAJOR  7
 #define VIRTIO_FS_FUSE_MINOR 31
 
 //
 // The inode number of the root directory.
 //
 #define VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID 1
 
+//
+// Distinguished errno values.
+//
+#define VIRTIO_FS_FUSE_ERRNO_ENOENT (-2)
+
 //
 // File mode bitmasks.
 //
 #define VIRTIO_FS_FUSE_MODE_TYPE_MASK 0170000u
 #define VIRTIO_FS_FUSE_MODE_TYPE_REG  0100000u
 #define VIRTIO_FS_FUSE_MODE_TYPE_DIR  0040000u
 #define VIRTIO_FS_FUSE_MODE_PERM_RWXU 0000700u
 #define VIRTIO_FS_FUSE_MODE_PERM_RUSR 0000400u
@@ -102,16 +107,17 @@ typedef struct {
 //
 #define VIRTIO_FS_FUSE_OPEN_REQ_F_RDONLY 0
 #define VIRTIO_FS_FUSE_OPEN_REQ_F_RDWR   2
 
 //
 // FUSE operation codes.
 //
 typedef enum {
+  VirtioFsFuseOpLookup      =  1,
   VirtioFsFuseOpForget      =  2,
   VirtioFsFuseOpMkDir       =  9,
   VirtioFsFuseOpOpen        = 14,
   VirtioFsFuseOpRelease     = 18,
   VirtioFsFuseOpFsync       = 20,
   VirtioFsFuseOpFlush       = 25,
   VirtioFsFuseOpInit        = 26,
   VirtioFsFuseOpOpenDir     = 27,
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index 7d7272188465..3552ece6b945 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -82,16 +82,17 @@ [Packages]
   OvmfPkg/OvmfPkg.dec
 
 [Sources]
   DriverBinding.c
   FuseFlush.c
   FuseForget.c
   FuseFsync.c
   FuseInit.c
+  FuseLookup.c
   FuseMkDir.c
   FuseOpen.c
   FuseOpenDir.c
   FuseOpenOrCreate.c
   FuseRelease.c
   Helpers.c
   SimpleFsClose.c
   SimpleFsDelete.c
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 6cc5257bab40..b2e4adce098b 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -231,16 +231,25 @@ VirtioFsFuseAttrToEfiFileInfo (
   IN     VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr,
      OUT EFI_FILE_INFO                      *FileInfo
   );
 
 //
 // Wrapper functions for FUSE commands (primitives).
 //
 
+EFI_STATUS
+VirtioFsFuseLookup (
+  IN OUT VIRTIO_FS                          *VirtioFs,
+  IN     UINT64                             DirNodeId,
+  IN     CHAR8                              *Name,
+     OUT UINT64                             *NodeId,
+     OUT VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr
+  );
+
 EFI_STATUS
 VirtioFsFuseForget (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    NodeId
   );
 
 EFI_STATUS
 VirtioFsFuseMkDir (
diff --git a/OvmfPkg/VirtioFsDxe/FuseLookup.c b/OvmfPkg/VirtioFsDxe/FuseLookup.c
new file mode 100644
index 000000000000..5c9a825e1725
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/FuseLookup.c
@@ -0,0 +1,148 @@
+/** @file
+  FUSE_LOOKUP wrapper for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h> // AsciiStrSize()
+
+#include "VirtioFsDxe.h"
+
+/**
+  Send a FUSE_LOOKUP request to the Virtio Filesystem device, for resolving a
+  filename to an inode.
+
+  The function returns EFI_NOT_FOUND exclusively if the Virtio Filesystem
+  device explicitly responds with ENOENT -- "No such file or directory".
+
+  The function may only be called after VirtioFsFuseInitSession() returns
+  successfully and before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_LOOKUP
+                           request to. On output, the FUSE request counter
+                           "VirtioFs->RequestId" will have been incremented.
+
+  @param[in] DirNodeId     The inode number of the directory in which Name
+                           should be resolved to an inode.
+
+  @param[in] Name          The single-component filename to resolve in the
+                           directory identified by DirNodeId.
+
+  @param[out] NodeId       The inode number which Name has been resolved to.
+
+  @param[out] FuseAttr     The VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object
+                           describing the properties of the resolved inode.
+
+  @retval EFI_SUCCESS    Filename to inode resolution successful.
+
+  @retval EFI_NOT_FOUND  The Virtio Filesystem device explicitly reported
+                         ENOENT -- "No such file or directory".
+
+  @return                The "errno" value mapped to an EFI_STATUS code, if the
+                         Virtio Filesystem device explicitly reported an error
+                         different from ENOENT. If said mapping resulted in
+                         EFI_NOT_FOUND, it is remapped to EFI_DEVICE_ERROR.
+
+  @return                Error codes propagated from VirtioFsSgListsValidate(),
+                         VirtioFsFuseNewRequest(), VirtioFsSgListsSubmit(),
+                         VirtioFsFuseCheckResponse(). EFI_NOT_FOUND is remapped
+                         to EFI_DEVICE_ERROR.
+**/
+EFI_STATUS
+VirtioFsFuseLookup (
+  IN OUT VIRTIO_FS                          *VirtioFs,
+  IN     UINT64                             DirNodeId,
+  IN     CHAR8                              *Name,
+     OUT UINT64                             *NodeId,
+     OUT VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr
+  )
+{
+  VIRTIO_FS_FUSE_REQUEST        CommonReq;
+  VIRTIO_FS_IO_VECTOR           ReqIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST ReqSgList;
+  VIRTIO_FS_FUSE_RESPONSE       CommonResp;
+  VIRTIO_FS_FUSE_NODE_RESPONSE  NodeResp;
+  VIRTIO_FS_IO_VECTOR           RespIoVec[3];
+  VIRTIO_FS_SCATTER_GATHER_LIST RespSgList;
+  EFI_STATUS                    Status;
+
+  //
+  // Set up the scatter-gather lists.
+  //
+  ReqIoVec[0].Buffer = &CommonReq;
+  ReqIoVec[0].Size   = sizeof CommonReq;
+  ReqIoVec[1].Buffer = Name;
+  ReqIoVec[1].Size   = AsciiStrSize (Name);
+  ReqSgList.IoVec    = ReqIoVec;
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);
+
+  RespIoVec[0].Buffer = &CommonResp;
+  RespIoVec[0].Size   = sizeof CommonResp;
+  RespIoVec[1].Buffer = &NodeResp;
+  RespIoVec[1].Size   = sizeof NodeResp;
+  RespIoVec[2].Buffer = FuseAttr;
+  RespIoVec[2].Size   = sizeof *FuseAttr;
+  RespSgList.IoVec    = RespIoVec;
+  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);
+
+  //
+  // Validate the scatter-gather lists; calculate the total transfer sizes.
+  //
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    goto Fail;
+  }
+
+  //
+  // Populate the common request header.
+  //
+  Status = VirtioFsFuseNewRequest (VirtioFs, &CommonReq, ReqSgList.TotalSize,
+             VirtioFsFuseOpLookup, DirNodeId);
+  if (EFI_ERROR (Status)) {
+    goto Fail;
+  }
+
+  //
+  // Submit the request.
+  //
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    goto Fail;
+  }
+
+  //
+  // Verify the response (all response buffers are fixed size).
+  //
+  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);
+  if (EFI_ERROR (Status)) {
+    if (Status == EFI_DEVICE_ERROR) {
+      DEBUG ((
+        ((CommonResp.Error == VIRTIO_FS_FUSE_ERRNO_ENOENT) ?
+         DEBUG_VERBOSE :
+         DEBUG_ERROR),
+        "%a: Label=\"%s\" DirNodeId=%Lu Name=\"%a\" Errno=%d\n",
+        __FUNCTION__,
+        VirtioFs->Label,
+        DirNodeId,
+        Name,
+        CommonResp.Error
+        ));
+      if (CommonResp.Error == VIRTIO_FS_FUSE_ERRNO_ENOENT) {
+        return EFI_NOT_FOUND;
+      }
+      Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
+    }
+    goto Fail;
+  }
+
+  //
+  // Output the NodeId to which Name has been resolved to.
+  //
+  *NodeId = NodeResp.NodeId;
+  return EFI_SUCCESS;
+
+Fail:
+  return (Status == EFI_NOT_FOUND) ? EFI_DEVICE_ERROR : Status;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 23/48] OvmfPkg/VirtioFsDxe: split canon. path into last parent + last component
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (21 preceding siblings ...)
  2020-12-16 21:10 ` [edk2 PATCH 22/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_LOOKUP Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 24/48] OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_UNLINK / FUSE_RMDIR Laszlo Ersek
                   ` (25 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Given a canonical pathname (as defined by VirtioFsAppendPath()), different
from "/", introduce a helper function for:

- looking up the NodeId of the most specific parent directory, and

- exposing the last component stand-alone (which is therefore a direct
  child of said parent directory).

This splitting operation will be necessary in multiple subsequent patches.

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>
---
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h |   8 ++
 OvmfPkg/VirtioFsDxe/Helpers.c     | 131 ++++++++++++++++++++
 2 files changed, 139 insertions(+)

diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index b2e4adce098b..6ae5c36f7fd5 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -221,16 +221,24 @@ VirtioFsErrnoToEfiStatus (
 EFI_STATUS
 VirtioFsAppendPath (
   IN     CHAR8   *LhsPath8,
   IN     CHAR16  *RhsPath16,
      OUT CHAR8   **ResultPath8,
      OUT BOOLEAN *RootEscape
   );
 
+EFI_STATUS
+VirtioFsLookupMostSpecificParentDir (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN OUT CHAR8     *Path,
+     OUT UINT64    *DirNodeId,
+     OUT CHAR8     **LastComponent
+  );
+
 EFI_STATUS
 VirtioFsFuseAttrToEfiFileInfo (
   IN     VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr,
      OUT EFI_FILE_INFO                      *FileInfo
   );
 
 //
 // Wrapper functions for FUSE commands (primitives).
diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
index fa656bb535b8..6adc0341dee6 100644
--- a/OvmfPkg/VirtioFsDxe/Helpers.c
+++ b/OvmfPkg/VirtioFsDxe/Helpers.c
@@ -1586,16 +1586,147 @@ VirtioFsAppendPath (
     FreePool (BufferToSanitize);
   }
 
 FreeRhsPath8:
   FreePool (RhsPath8);
   return Status;
 }
 
+/**
+  For a given canonical pathname (as defined at VirtioFsAppendPath()), look up
+  the NodeId of the most specific parent directory, plus output a pointer to
+  the last pathname component (which is therefore a direct child of said parent
+  directory).
+
+  The function may only be called after VirtioFsFuseInitSession() returns
+  successfully and before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs    The Virtio Filesystem device to send FUSE_LOOKUP
+                             and FUSE_FORGET requests to. On output, the FUSE
+                             request counter "VirtioFs->RequestId" will have
+                             been incremented several times.
+
+  @param[in,out] Path        The canonical pathname (as defined in the
+                             description of VirtioFsAppendPath()) to split.
+                             Path is modified in-place temporarily; however, on
+                             return (successful or otherwise), Path reassumes
+                             its original contents.
+
+  @param[out] DirNodeId      The NodeId of the most specific parent directory
+                             identified by Path. The caller is responsible for
+                             sending a FUSE_FORGET request to the Virtio
+                             Filesystem device for DirNodeId -- unless
+                             DirNodeId equals VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID
+                             --, when DirNodeId's use ends.
+
+  @param[out] LastComponent  A pointer into Path, pointing at the start of the
+                             last pathname component.
+
+  @retval EFI_SUCCESS            Splitting successful.
+
+  @retval EFI_INVALID_PARAMETER  Path is "/".
+
+  @retval EFI_ACCESS_DENIED      One of the components on Path before the last
+                                 is not a directory.
+
+  @return                        Error codes propagated from
+                                 VirtioFsFuseLookup() and
+                                 VirtioFsFuseAttrToEfiFileInfo().
+**/
+EFI_STATUS
+VirtioFsLookupMostSpecificParentDir (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN OUT CHAR8     *Path,
+     OUT UINT64    *DirNodeId,
+     OUT CHAR8     **LastComponent
+  )
+{
+  UINT64     ParentDirNodeId;
+  CHAR8      *Slash;
+  EFI_STATUS Status;
+  UINT64     NextDirNodeId;
+
+  if (AsciiStrCmp (Path, "/") == 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ParentDirNodeId = VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID;
+  Slash           = Path;
+  for (;;) {
+    CHAR8                              *NextSlash;
+    VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
+    EFI_FILE_INFO                      FileInfo;
+
+    //
+    // Find the slash (if any) that terminates the next pathname component.
+    //
+    NextSlash = AsciiStrStr (Slash + 1, "/");
+    if (NextSlash == NULL) {
+      break;
+    }
+
+    //
+    // Temporarily replace the found slash character with a NUL in-place, for
+    // easy construction of the single-component filename that we need to look
+    // up.
+    //
+    *NextSlash = '\0';
+    Status = VirtioFsFuseLookup (VirtioFs, ParentDirNodeId, Slash + 1,
+               &NextDirNodeId, &FuseAttr);
+    *NextSlash = '/';
+
+    //
+    // We're done with the directory inode that was the basis for the lookup.
+    //
+    if (ParentDirNodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
+      VirtioFsFuseForget (VirtioFs, ParentDirNodeId);
+    }
+
+    //
+    // If we couldn't look up the next *non-final* pathname component, bail.
+    //
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    //
+    // Lookup successful; now check if the next (non-final) component is a
+    // directory. If not, bail.
+    //
+    Status = VirtioFsFuseAttrToEfiFileInfo (&FuseAttr, &FileInfo);
+    if (EFI_ERROR (Status)) {
+      goto ForgetNextDirNodeId;
+    }
+    if ((FileInfo.Attribute & EFI_FILE_DIRECTORY) == 0) {
+      Status = EFI_ACCESS_DENIED;
+      goto ForgetNextDirNodeId;
+    }
+
+    //
+    // Advance.
+    //
+    ParentDirNodeId = NextDirNodeId;
+    Slash           = NextSlash;
+  }
+
+  //
+  // ParentDirNodeId corresponds to the last containing directory. The
+  // remaining single-component filename represents a direct child under that
+  // directory. Said filename starts at (Slash + 1).
+  //
+  *DirNodeId     = ParentDirNodeId;
+  *LastComponent = Slash + 1;
+  return EFI_SUCCESS;
+
+ForgetNextDirNodeId:
+  VirtioFsFuseForget (VirtioFs, NextDirNodeId);
+  return Status;
+}
+
 /**
   Convert select fields of a VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
   corresponding fields in EFI_FILE_INFO.
 
   @param[in] FuseAttr   The VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
                         convert the relevant fields from.
 
   @param[out] FileInfo  The EFI_FILE_INFO structure to modify. Importantly, the
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 24/48] OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_UNLINK / FUSE_RMDIR
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (22 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 23/48] OvmfPkg/VirtioFsDxe: split canon. path into last parent + last component Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 25/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_GETATTR Laszlo Ersek
                   ` (24 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

The FUSE_UNLINK and FUSE_RMDIR commands only differ in the opcode. Add a
common function for wrapping both.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |   2 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |   8 ++
 OvmfPkg/VirtioFsDxe/FuseUnlink.c            | 114 ++++++++++++++++++++
 4 files changed, 125 insertions(+)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index 8a07b3d2eb93..f49452830abc 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -110,16 +110,18 @@ typedef struct {
 
 //
 // FUSE operation codes.
 //
 typedef enum {
   VirtioFsFuseOpLookup      =  1,
   VirtioFsFuseOpForget      =  2,
   VirtioFsFuseOpMkDir       =  9,
+  VirtioFsFuseOpUnlink      = 10,
+  VirtioFsFuseOpRmDir       = 11,
   VirtioFsFuseOpOpen        = 14,
   VirtioFsFuseOpRelease     = 18,
   VirtioFsFuseOpFsync       = 20,
   VirtioFsFuseOpFlush       = 25,
   VirtioFsFuseOpInit        = 26,
   VirtioFsFuseOpOpenDir     = 27,
   VirtioFsFuseOpReleaseDir  = 29,
   VirtioFsFuseOpFsyncDir    = 30,
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index 3552ece6b945..2332aa3ee551 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -88,16 +88,17 @@ [Sources]
   FuseFsync.c
   FuseInit.c
   FuseLookup.c
   FuseMkDir.c
   FuseOpen.c
   FuseOpenDir.c
   FuseOpenOrCreate.c
   FuseRelease.c
+  FuseUnlink.c
   Helpers.c
   SimpleFsClose.c
   SimpleFsDelete.c
   SimpleFsFlush.c
   SimpleFsGetInfo.c
   SimpleFsGetPosition.c
   SimpleFsOpen.c
   SimpleFsOpenVolume.c
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 6ae5c36f7fd5..0e4f2109eb02 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -262,16 +262,24 @@ VirtioFsFuseForget (
 EFI_STATUS
 VirtioFsFuseMkDir (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    ParentNodeId,
   IN     CHAR8     *Name,
      OUT UINT64    *NodeId
   );
 
+EFI_STATUS
+VirtioFsFuseRemoveFileOrDir (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    ParentNodeId,
+  IN     CHAR8     *Name,
+  IN     BOOLEAN   IsDir
+  );
+
 EFI_STATUS
 VirtioFsFuseOpen (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    NodeId,
   IN     BOOLEAN   ReadWrite,
      OUT UINT64    *FuseHandle
   );
 
diff --git a/OvmfPkg/VirtioFsDxe/FuseUnlink.c b/OvmfPkg/VirtioFsDxe/FuseUnlink.c
new file mode 100644
index 000000000000..8f84edbe9431
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/FuseUnlink.c
@@ -0,0 +1,114 @@
+/** @file
+  FUSE_UNLINK / FUSE_RMDIR wrapper for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h> // AsciiStrSize()
+
+#include "VirtioFsDxe.h"
+
+/**
+  Remove a regular file or a directory, by sending the FUSE_UNLINK or
+  FUSE_RMDIR request to the Virtio Filesystem device.
+
+  The function may only be called after VirtioFsFuseInitSession() returns
+  successfully and before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_UNLINK
+                           / FUSE_RMDIR request to. On output, the FUSE request
+                           counter "VirtioFs->RequestId" will have been
+                           incremented.
+
+  @param[in] ParentNodeId  The inode number of the directory in which Name
+                           should be removed.
+
+  @param[in] Name          The single-component filename to remove in the
+                           directory identified by ParentNodeId.
+
+  @param[in] IsDir         TRUE if Name refers to a directory, FALSE otherwise.
+
+  @retval EFI_SUCCESS  The file or directory has been removed.
+
+  @return              The "errno" value mapped to an EFI_STATUS code, if the
+                       Virtio Filesystem device explicitly reported an error.
+
+  @return              Error codes propagated from VirtioFsSgListsValidate(),
+                       VirtioFsFuseNewRequest(), VirtioFsSgListsSubmit(),
+                       VirtioFsFuseCheckResponse().
+**/
+EFI_STATUS
+VirtioFsFuseRemoveFileOrDir (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    ParentNodeId,
+  IN     CHAR8     *Name,
+  IN     BOOLEAN   IsDir
+  )
+{
+  VIRTIO_FS_FUSE_REQUEST        CommonReq;
+  VIRTIO_FS_IO_VECTOR           ReqIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST ReqSgList;
+  VIRTIO_FS_FUSE_RESPONSE       CommonResp;
+  VIRTIO_FS_IO_VECTOR           RespIoVec[1];
+  VIRTIO_FS_SCATTER_GATHER_LIST RespSgList;
+  EFI_STATUS                    Status;
+
+  //
+  // Set up the scatter-gather lists.
+  //
+  ReqIoVec[0].Buffer = &CommonReq;
+  ReqIoVec[0].Size   = sizeof CommonReq;
+  ReqIoVec[1].Buffer = Name;
+  ReqIoVec[1].Size   = AsciiStrSize (Name);
+  ReqSgList.IoVec    = ReqIoVec;
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);
+
+  RespIoVec[0].Buffer = &CommonResp;
+  RespIoVec[0].Size   = sizeof CommonResp;
+  RespSgList.IoVec    = RespIoVec;
+  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);
+
+  //
+  // Validate the scatter-gather lists; calculate the total transfer sizes.
+  //
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the common request header.
+  //
+  Status = VirtioFsFuseNewRequest (
+             VirtioFs,
+             &CommonReq,
+             ReqSgList.TotalSize,
+             IsDir ? VirtioFsFuseOpRmDir : VirtioFsFuseOpUnlink,
+             ParentNodeId
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Submit the request.
+  //
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Verify the response (all response buffers are fixed size).
+  //
+  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);
+  if (Status == EFI_DEVICE_ERROR) {
+    DEBUG ((DEBUG_ERROR, "%a: Label=\"%s\" ParentNodeId=%Lu Name=\"%a\" "
+      "IsDir=%d Errno=%d\n", __FUNCTION__, VirtioFs->Label, ParentNodeId, Name,
+      IsDir, CommonResp.Error));
+    Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
+  }
+  return Status;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 25/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_GETATTR
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (23 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 24/48] OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_UNLINK / FUSE_RMDIR Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 26/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Open() Laszlo Ersek
                   ` (23 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Add the VirtioFsFuseGetAttr() function, for sending the FUSE_GETATTR
command to the Virtio Filesystem device.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  16 +++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |   7 ++
 OvmfPkg/VirtioFsDxe/FuseGetAttr.c           | 116 ++++++++++++++++++++
 4 files changed, 140 insertions(+)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index f49452830abc..efcf57941c59 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -109,16 +109,17 @@ typedef struct {
 #define VIRTIO_FS_FUSE_OPEN_REQ_F_RDWR   2
 
 //
 // FUSE operation codes.
 //
 typedef enum {
   VirtioFsFuseOpLookup      =  1,
   VirtioFsFuseOpForget      =  2,
+  VirtioFsFuseOpGetAttr     =  3,
   VirtioFsFuseOpMkDir       =  9,
   VirtioFsFuseOpUnlink      = 10,
   VirtioFsFuseOpRmDir       = 11,
   VirtioFsFuseOpOpen        = 14,
   VirtioFsFuseOpRelease     = 18,
   VirtioFsFuseOpFsync       = 20,
   VirtioFsFuseOpFlush       = 25,
   VirtioFsFuseOpInit        = 26,
@@ -190,16 +191,31 @@ typedef struct {
 
 //
 // Header for VirtioFsFuseOpForget.
 //
 typedef struct {
   UINT64 NumberOfLookups;
 } VIRTIO_FS_FUSE_FORGET_REQUEST;
 
+//
+// Headers for VirtioFsFuseOpGetAttr.
+//
+typedef struct {
+  UINT32 GetAttrFlags;
+  UINT32 Dummy;
+  UINT64 FileHandle;
+} VIRTIO_FS_FUSE_GETATTR_REQUEST;
+
+typedef struct {
+  UINT64 AttrValid;
+  UINT32 AttrValidNsec;
+  UINT32 Dummy;
+} VIRTIO_FS_FUSE_GETATTR_RESPONSE;
+
 //
 // Header for VirtioFsFuseOpMkDir.
 //
 typedef struct {
   UINT32 Mode;
   UINT32 Umask;
 } VIRTIO_FS_FUSE_MKDIR_REQUEST;
 
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index 2332aa3ee551..233c0c5c0f9a 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -81,16 +81,17 @@ [Packages]
   MdePkg/MdePkg.dec
   OvmfPkg/OvmfPkg.dec
 
 [Sources]
   DriverBinding.c
   FuseFlush.c
   FuseForget.c
   FuseFsync.c
+  FuseGetAttr.c
   FuseInit.c
   FuseLookup.c
   FuseMkDir.c
   FuseOpen.c
   FuseOpenDir.c
   FuseOpenOrCreate.c
   FuseRelease.c
   FuseUnlink.c
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 0e4f2109eb02..3bf64c0146fa 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -254,16 +254,23 @@ VirtioFsFuseLookup (
   );
 
 EFI_STATUS
 VirtioFsFuseForget (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    NodeId
   );
 
+EFI_STATUS
+VirtioFsFuseGetAttr (
+  IN OUT VIRTIO_FS                          *VirtioFs,
+  IN     UINT64                             NodeId,
+     OUT VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr
+  );
+
 EFI_STATUS
 VirtioFsFuseMkDir (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    ParentNodeId,
   IN     CHAR8     *Name,
      OUT UINT64    *NodeId
   );
 
diff --git a/OvmfPkg/VirtioFsDxe/FuseGetAttr.c b/OvmfPkg/VirtioFsDxe/FuseGetAttr.c
new file mode 100644
index 000000000000..29d8ec8190e1
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/FuseGetAttr.c
@@ -0,0 +1,116 @@
+/** @file
+  FUSE_GETATTR wrapper for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+/**
+  Send a FUSE_GETATTR request to the Virtio Filesystem device, for fetching the
+  attributes of an inode.
+
+  The function may only be called after VirtioFsFuseInitSession() returns
+  successfully and before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device to send the
+                           FUSE_GETATTR request to. On output, the FUSE request
+                           counter "VirtioFs->RequestId" will have been
+                           incremented.
+
+  @param[in] NodeId        The inode number for which the attributes should be
+                           retrieved.
+
+  @param[out] FuseAttr     The VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object
+                           describing the properties of the inode.
+
+  @retval EFI_SUCCESS  FuseAttr has been filled in.
+
+  @return              The "errno" value mapped to an EFI_STATUS code, if the
+                       Virtio Filesystem device explicitly reported an error.
+
+  @return              Error codes propagated from VirtioFsSgListsValidate(),
+                       VirtioFsFuseNewRequest(), VirtioFsSgListsSubmit(),
+                       VirtioFsFuseCheckResponse().
+**/
+EFI_STATUS
+VirtioFsFuseGetAttr (
+  IN OUT VIRTIO_FS                          *VirtioFs,
+  IN     UINT64                             NodeId,
+     OUT VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr
+  )
+{
+  VIRTIO_FS_FUSE_REQUEST          CommonReq;
+  VIRTIO_FS_FUSE_GETATTR_REQUEST  GetAttrReq;
+  VIRTIO_FS_IO_VECTOR             ReqIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST   ReqSgList;
+  VIRTIO_FS_FUSE_RESPONSE         CommonResp;
+  VIRTIO_FS_FUSE_GETATTR_RESPONSE GetAttrResp;
+  VIRTIO_FS_IO_VECTOR             RespIoVec[3];
+  VIRTIO_FS_SCATTER_GATHER_LIST   RespSgList;
+  EFI_STATUS                      Status;
+
+  //
+  // Set up the scatter-gather lists.
+  //
+  ReqIoVec[0].Buffer = &CommonReq;
+  ReqIoVec[0].Size   = sizeof CommonReq;
+  ReqIoVec[1].Buffer = &GetAttrReq;
+  ReqIoVec[1].Size   = sizeof GetAttrReq;
+  ReqSgList.IoVec    = ReqIoVec;
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);
+
+  RespIoVec[0].Buffer = &CommonResp;
+  RespIoVec[0].Size   = sizeof CommonResp;
+  RespIoVec[1].Buffer = &GetAttrResp;
+  RespIoVec[1].Size   = sizeof GetAttrResp;
+  RespIoVec[2].Buffer = FuseAttr;
+  RespIoVec[2].Size   = sizeof *FuseAttr;
+  RespSgList.IoVec    = RespIoVec;
+  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);
+
+  //
+  // Validate the scatter-gather lists; calculate the total transfer sizes.
+  //
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the common request header.
+  //
+  Status = VirtioFsFuseNewRequest (VirtioFs, &CommonReq, ReqSgList.TotalSize,
+             VirtioFsFuseOpGetAttr, NodeId);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the FUSE_GETATTR-specific fields.
+  //
+  GetAttrReq.GetAttrFlags = 0;
+  GetAttrReq.Dummy        = 0;
+  GetAttrReq.FileHandle   = 0;
+
+  //
+  // Submit the request.
+  //
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Verify the response (all response buffers are fixed size).
+  //
+  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);
+  if (Status == EFI_DEVICE_ERROR) {
+    DEBUG ((DEBUG_ERROR, "%a: Label=\"%s\" NodeId=%Lu Errno=%d\n",
+      __FUNCTION__, VirtioFs->Label, NodeId, CommonResp.Error));
+    Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
+  }
+  return Status;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 26/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Open()
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (24 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 25/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_GETATTR Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 27/48] OvmfPkg/VirtioFsDxe: erase the dir. entry in EFI_FILE_PROTOCOL.Delete() Laszlo Ersek
                   ` (22 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Using the functions introduced previously, we can now implement
VirtioFsSimpleFileOpen().

This lets the "MKDIR" command to work in the UEFI shell, for example.

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>
---
 OvmfPkg/VirtioFsDxe/SimpleFsOpen.c | 480 +++++++++++++++++++-
 1 file changed, 479 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsOpen.c b/OvmfPkg/VirtioFsDxe/SimpleFsOpen.c
index f0e249184079..2649c796ac97 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsOpen.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsOpen.c
@@ -1,22 +1,500 @@
 /** @file
   EFI_FILE_PROTOCOL.Open() member function for the Virtio Filesystem driver.
 
   Copyright (C) 2020, Red Hat, Inc.
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
+#include <Library/BaseLib.h>             // AsciiStrCmp()
+#include <Library/MemoryAllocationLib.h> // AllocatePool()
+
 #include "VirtioFsDxe.h"
 
+/**
+  Open the root directory, possibly for writing.
+
+  @param[in,out] VirtioFs    The Virtio Filesystem device whose root directory
+                             should be opened.
+
+  @param[out] NewHandle      The new EFI_FILE_PROTOCOL instance through which
+                             the root directory can be accessed.
+
+  @param[in] OpenForWriting  TRUE if the root directory should be opened for
+                             read-write access. FALSE if the root directory
+                             should be opened for read-only access. Opening the
+                             root directory for read-write access is useful for
+                             calling EFI_FILE_PROTOCOL.Flush() or
+                             EFI_FILE_PROTOCOL.SetInfo() later, for syncing or
+                             touching the root directory, respectively.
+
+  @retval EFI_SUCCESS        The root directory has been opened successfully.
+
+  @retval EFI_ACCESS_DENIED  OpenForWriting is TRUE, but the root directory is
+                             marked as read-only.
+
+  @return                    Error codes propagated from underlying functions.
+**/
+STATIC
+EFI_STATUS
+OpenRootDirectory (
+  IN OUT VIRTIO_FS         *VirtioFs,
+     OUT EFI_FILE_PROTOCOL **NewHandle,
+  IN     BOOLEAN           OpenForWriting
+  )
+{
+  EFI_STATUS     Status;
+  VIRTIO_FS_FILE *NewVirtioFsFile;
+
+  //
+  // VirtioFsOpenVolume() opens the root directory for read-only access. If the
+  // current request is to open the root directory for read-write access, so
+  // that EFI_FILE_PROTOCOL.Flush() or EFI_FILE_PROTOCOL.SetInfo()+timestamps
+  // can be used on the root directory later, then we have to check for write
+  // permission first.
+  //
+  if (OpenForWriting) {
+    VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
+    EFI_FILE_INFO                      FileInfo;
+
+    Status = VirtioFsFuseGetAttr (VirtioFs, VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID,
+               &FuseAttr);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    Status = VirtioFsFuseAttrToEfiFileInfo (&FuseAttr, &FileInfo);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    if ((FileInfo.Attribute & EFI_FILE_READ_ONLY) != 0) {
+      return EFI_ACCESS_DENIED;
+    }
+  }
+
+  Status = VirtioFsOpenVolume (&VirtioFs->SimpleFs, NewHandle);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  NewVirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (*NewHandle);
+  NewVirtioFsFile->IsOpenForWriting = OpenForWriting;
+  return EFI_SUCCESS;
+}
+
+/**
+  Open an existent regular file or non-root directory.
+
+  @param[in,out] VirtioFs      The Virtio Filesystem device on which the
+                               regular file or directory should be opened.
+
+  @param[in] DirNodeId         The inode number of the immediate parent
+                               directory of the regular file or directory to
+                               open.
+
+  @param[in] Name              The single-component filename of the regular
+                               file or directory to open, under the immediate
+                               parent directory identified by DirNodeId.
+
+  @param[in] OpenForWriting    TRUE if the regular file or directory should be
+                               opened for read-write access. FALSE if the
+                               regular file or directory should be opened for
+                               read-only access. Opening a directory for
+                               read-write access is useful for deleting,
+                               renaming, syncing or touching the directory
+                               later.
+
+  @param[out] NodeId           The inode number of the regular file or
+                               directory, returned by the Virtio Filesystem
+                               device.
+
+  @param[out] FuseHandle       The open handle to the regular file or
+                               directory, returned by the Virtio Filesystem
+                               device.
+
+  @param[out] NodeIsDirectory  Set to TRUE on output if Name was found to refer
+                               to a directory. Set to FALSE if Name was found
+                               to refer to a regular file.
+
+  @retval EFI_SUCCESS        The regular file or directory has been looked up
+                             and opened successfully.
+
+  @retval EFI_ACCESS_DENIED  OpenForWriting is TRUE, but the regular file or
+                             directory is marked read-only.
+
+  @retval EFI_NOT_FOUND      A directory entry called Name was not found in the
+                             directory identified by DirNodeId. (EFI_NOT_FOUND
+                             is not returned for any other condition.)
+
+  @return                    Errors propagated from underlying functions. If
+                             the error code to propagate were EFI_NOT_FOUND, it
+                             is remapped to EFI_DEVICE_ERROR.
+**/
+STATIC
+EFI_STATUS
+OpenExistentFileOrDirectory (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    DirNodeId,
+  IN     CHAR8     *Name,
+  IN     BOOLEAN   OpenForWriting,
+     OUT UINT64    *NodeId,
+     OUT UINT64    *FuseHandle,
+     OUT BOOLEAN   *NodeIsDirectory
+  )
+{
+  EFI_STATUS                         Status;
+  UINT64                             ResolvedNodeId;
+  VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
+  EFI_FILE_INFO                      FileInfo;
+  BOOLEAN                            IsDirectory;
+  UINT64                             NewFuseHandle;
+
+  Status = VirtioFsFuseLookup (VirtioFs, DirNodeId, Name, &ResolvedNodeId,
+             &FuseAttr);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  Status = VirtioFsFuseAttrToEfiFileInfo (&FuseAttr, &FileInfo);
+  if (EFI_ERROR (Status)) {
+    goto ForgetResolvedNodeId;
+  }
+
+  if (OpenForWriting && (FileInfo.Attribute & EFI_FILE_READ_ONLY) != 0) {
+    Status = EFI_ACCESS_DENIED;
+    goto ForgetResolvedNodeId;
+  }
+
+  IsDirectory = (BOOLEAN)((FileInfo.Attribute & EFI_FILE_DIRECTORY) != 0);
+  if (IsDirectory) {
+    //
+    // If OpenForWriting is TRUE here, that's not passed to
+    // VirtioFsFuseOpenDir(); it does not affect the FUSE_OPENDIR request we
+    // send. OpenForWriting=TRUE will only permit attempts to delete, rename,
+    // flush (sync), and touch the directory.
+    //
+    Status = VirtioFsFuseOpenDir (VirtioFs, ResolvedNodeId, &NewFuseHandle);
+  } else {
+    Status = VirtioFsFuseOpen (VirtioFs, ResolvedNodeId, OpenForWriting,
+               &NewFuseHandle);
+  }
+  if (EFI_ERROR (Status)) {
+    goto ForgetResolvedNodeId;
+  }
+
+  *NodeId          = ResolvedNodeId;
+  *FuseHandle      = NewFuseHandle;
+  *NodeIsDirectory = IsDirectory;
+  return EFI_SUCCESS;
+
+ForgetResolvedNodeId:
+  VirtioFsFuseForget (VirtioFs, ResolvedNodeId);
+  return (Status == EFI_NOT_FOUND) ? EFI_DEVICE_ERROR : Status;
+}
+
+/**
+  Create a directory.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device on which the directory
+                           should be created.
+
+  @param[in] DirNodeId     The inode number of the immediate parent directory
+                           of the directory to create.
+
+  @param[in] Name          The single-component filename of the directory to
+                           create, under the immediate parent directory
+                           identified by DirNodeId.
+
+  @param[out] NodeId       The inode number of the directory created, returned
+                           by the Virtio Filesystem device.
+
+  @param[out] FuseHandle   The open handle to the directory created, returned
+                           by the Virtio Filesystem device.
+
+  @retval EFI_SUCCESS  The directory has been created successfully.
+
+  @return              Errors propagated from underlying functions.
+**/
+STATIC
+EFI_STATUS
+CreateDirectory (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    DirNodeId,
+  IN     CHAR8     *Name,
+     OUT UINT64    *NodeId,
+     OUT UINT64    *FuseHandle
+  )
+{
+  EFI_STATUS Status;
+  UINT64     NewChildDirNodeId;
+  UINT64     NewFuseHandle;
+
+  Status = VirtioFsFuseMkDir (VirtioFs, DirNodeId, Name, &NewChildDirNodeId);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = VirtioFsFuseOpenDir (VirtioFs, NewChildDirNodeId, &NewFuseHandle);
+  if (EFI_ERROR (Status)) {
+    goto RemoveNewChildDir;
+  }
+
+  *NodeId     = NewChildDirNodeId;
+  *FuseHandle = NewFuseHandle;
+  return EFI_SUCCESS;
+
+RemoveNewChildDir:
+  VirtioFsFuseRemoveFileOrDir (VirtioFs, DirNodeId, Name, TRUE /* IsDir */);
+  VirtioFsFuseForget (VirtioFs, NewChildDirNodeId);
+  return Status;
+}
+
+/**
+  Create a regular file.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device on which the regular
+                           file should be created.
+
+  @param[in] DirNodeId     The inode number of the immediate parent directory
+                           of the regular file to create.
+
+  @param[in] Name          The single-component filename of the regular file to
+                           create, under the immediate parent directory
+                           identified by DirNodeId.
+
+  @param[out] NodeId       The inode number of the regular file created,
+                           returned by the Virtio Filesystem device.
+
+  @param[out] FuseHandle   The open handle to the regular file created,
+                           returned by the Virtio Filesystem device.
+
+  @retval EFI_SUCCESS  The regular file has been created successfully.
+
+  @return              Errors propagated from underlying functions.
+**/
+STATIC
+EFI_STATUS
+CreateRegularFile (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    DirNodeId,
+  IN     CHAR8     *Name,
+     OUT UINT64    *NodeId,
+     OUT UINT64    *FuseHandle
+  )
+{
+  return VirtioFsFuseOpenOrCreate (VirtioFs, DirNodeId, Name, NodeId,
+           FuseHandle);
+}
+
 EFI_STATUS
 EFIAPI
 VirtioFsSimpleFileOpen (
   IN     EFI_FILE_PROTOCOL *This,
      OUT EFI_FILE_PROTOCOL **NewHandle,
   IN     CHAR16            *FileName,
   IN     UINT64            OpenMode,
   IN     UINT64            Attributes
   )
 {
-  return EFI_NO_MEDIA;
+  VIRTIO_FS_FILE *VirtioFsFile;
+  VIRTIO_FS      *VirtioFs;
+  BOOLEAN        OpenForWriting;
+  BOOLEAN        PermitCreation;
+  BOOLEAN        CreateDirectoryIfCreating;
+  VIRTIO_FS_FILE *NewVirtioFsFile;
+  EFI_STATUS     Status;
+  CHAR8          *NewCanonicalPath;
+  BOOLEAN        RootEscape;
+  UINT64         DirNodeId;
+  CHAR8          *LastComponent;
+  UINT64         NewNodeId;
+  UINT64         NewFuseHandle;
+  BOOLEAN        NewNodeIsDirectory;
+
+  VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
+  VirtioFs     = VirtioFsFile->OwnerFs;
+
+  //
+  // Validate OpenMode.
+  //
+  switch (OpenMode) {
+  case EFI_FILE_MODE_READ:
+    OpenForWriting = FALSE;
+    PermitCreation = FALSE;
+    break;
+  case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE:
+    OpenForWriting = TRUE;
+    PermitCreation = FALSE;
+    break;
+  case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE:
+    OpenForWriting = TRUE;
+    PermitCreation = TRUE;
+    break;
+  default:
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Validate the Attributes requested for the case when the file ends up being
+  // created, provided creation is permitted.
+  //
+  if (PermitCreation) {
+    if ((Attributes & ~EFI_FILE_VALID_ATTR) != 0) {
+      //
+      // Unknown attribute requested.
+      //
+      return EFI_INVALID_PARAMETER;
+    }
+
+    ASSERT (OpenForWriting);
+    if ((Attributes & EFI_FILE_READ_ONLY) != 0) {
+      DEBUG ((
+        DEBUG_ERROR,
+        ("%a: Label=\"%s\" CanonicalPathname=\"%a\" FileName=\"%s\" "
+         "OpenMode=0x%Lx Attributes=0x%Lx: nonsensical request to possibly "
+         "create a file marked read-only, for read-write access\n"),
+        __FUNCTION__,
+        VirtioFs->Label,
+        VirtioFsFile->CanonicalPathname,
+        FileName,
+        OpenMode,
+        Attributes
+        ));
+      return EFI_INVALID_PARAMETER;
+    }
+    CreateDirectoryIfCreating = (BOOLEAN)((Attributes &
+                                           EFI_FILE_DIRECTORY) != 0);
+  }
+
+  //
+  // Referring to a file relative to a regular file makes no sense (or at least
+  // it cannot be implemented consistently with how a file is referred to
+  // relative to a directory).
+  //
+  if (!VirtioFsFile->IsDirectory) {
+    DEBUG ((
+      DEBUG_ERROR,
+      ("%a: Label=\"%s\" CanonicalPathname=\"%a\" FileName=\"%s\": "
+       "nonsensical request to open a file or directory relative to a regular "
+       "file\n"),
+      __FUNCTION__,
+      VirtioFs->Label,
+      VirtioFsFile->CanonicalPathname,
+      FileName
+      ));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Allocate the new VIRTIO_FS_FILE object.
+  //
+  NewVirtioFsFile = AllocatePool (sizeof *NewVirtioFsFile);
+  if (NewVirtioFsFile == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Create the canonical pathname at which the desired file is expected to
+  // exist.
+  //
+  Status = VirtioFsAppendPath (VirtioFsFile->CanonicalPathname, FileName,
+             &NewCanonicalPath, &RootEscape);
+  if (EFI_ERROR (Status)) {
+    goto FreeNewVirtioFsFile;
+  }
+  if (RootEscape) {
+    Status = EFI_ACCESS_DENIED;
+    goto FreeNewCanonicalPath;
+  }
+
+  //
+  // If the desired file is the root directory, just open the volume one more
+  // time, without looking up anything.
+  //
+  if (AsciiStrCmp (NewCanonicalPath, "/") == 0) {
+    FreePool (NewCanonicalPath);
+    FreePool (NewVirtioFsFile);
+    return OpenRootDirectory (VirtioFs, NewHandle, OpenForWriting);
+  }
+
+  //
+  // Split the new canonical pathname into most specific parent directory
+  // (given by DirNodeId) and last pathname component (i.e., immediate child
+  // within that parent directory).
+  //
+  Status = VirtioFsLookupMostSpecificParentDir (VirtioFs, NewCanonicalPath,
+             &DirNodeId, &LastComponent);
+  if (EFI_ERROR (Status)) {
+    goto FreeNewCanonicalPath;
+  }
+
+  //
+  // Try to open LastComponent directly under DirNodeId, as an existent regular
+  // file or directory.
+  //
+  Status = OpenExistentFileOrDirectory (VirtioFs, DirNodeId, LastComponent,
+             OpenForWriting, &NewNodeId, &NewFuseHandle, &NewNodeIsDirectory);
+  //
+  // If LastComponent could not be found under DirNodeId, but the request
+  // allows us to create a new entry, attempt creating the requested regular
+  // file or directory.
+  //
+  if (Status == EFI_NOT_FOUND && PermitCreation) {
+    ASSERT (OpenForWriting);
+    if (CreateDirectoryIfCreating) {
+      Status = CreateDirectory (VirtioFs, DirNodeId, LastComponent, &NewNodeId,
+                 &NewFuseHandle);
+    } else {
+      Status = CreateRegularFile (VirtioFs, DirNodeId, LastComponent,
+                 &NewNodeId, &NewFuseHandle);
+    }
+    NewNodeIsDirectory = CreateDirectoryIfCreating;
+  }
+
+  //
+  // Regardless of the branch taken, we're done with DirNodeId.
+  //
+  if (DirNodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
+    VirtioFsFuseForget (VirtioFs, DirNodeId);
+  }
+
+  if (EFI_ERROR (Status)) {
+    goto FreeNewCanonicalPath;
+  }
+
+  //
+  // Populate the new VIRTIO_FS_FILE object.
+  //
+  NewVirtioFsFile->Signature              = VIRTIO_FS_FILE_SIG;
+  NewVirtioFsFile->SimpleFile.Revision    = EFI_FILE_PROTOCOL_REVISION;
+  NewVirtioFsFile->SimpleFile.Open        = VirtioFsSimpleFileOpen;
+  NewVirtioFsFile->SimpleFile.Close       = VirtioFsSimpleFileClose;
+  NewVirtioFsFile->SimpleFile.Delete      = VirtioFsSimpleFileDelete;
+  NewVirtioFsFile->SimpleFile.Read        = VirtioFsSimpleFileRead;
+  NewVirtioFsFile->SimpleFile.Write       = VirtioFsSimpleFileWrite;
+  NewVirtioFsFile->SimpleFile.GetPosition = VirtioFsSimpleFileGetPosition;
+  NewVirtioFsFile->SimpleFile.SetPosition = VirtioFsSimpleFileSetPosition;
+  NewVirtioFsFile->SimpleFile.GetInfo     = VirtioFsSimpleFileGetInfo;
+  NewVirtioFsFile->SimpleFile.SetInfo     = VirtioFsSimpleFileSetInfo;
+  NewVirtioFsFile->SimpleFile.Flush       = VirtioFsSimpleFileFlush;
+  NewVirtioFsFile->IsDirectory            = NewNodeIsDirectory;
+  NewVirtioFsFile->IsOpenForWriting       = OpenForWriting;
+  NewVirtioFsFile->OwnerFs                = VirtioFs;
+  NewVirtioFsFile->CanonicalPathname      = NewCanonicalPath;
+  NewVirtioFsFile->NodeId                 = NewNodeId;
+  NewVirtioFsFile->FuseHandle             = NewFuseHandle;
+
+  //
+  // One more file is now open for the filesystem.
+  //
+  InsertTailList (&VirtioFs->OpenFiles, &NewVirtioFsFile->OpenFilesEntry);
+
+  *NewHandle = &NewVirtioFsFile->SimpleFile;
+  return EFI_SUCCESS;
+
+FreeNewCanonicalPath:
+  FreePool (NewCanonicalPath);
+
+FreeNewVirtioFsFile:
+  FreePool (NewVirtioFsFile);
+
+  return Status;
 }
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 27/48] OvmfPkg/VirtioFsDxe: erase the dir. entry in EFI_FILE_PROTOCOL.Delete()
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (25 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 26/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Open() Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 28/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_STATFS Laszlo Ersek
                   ` (21 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

At this point, the infrastructure is available for looking up the directly
containing directory of the file in EFI_FILE_PROTOCOL.Delete(), and to
remove the file in that directory by last pathname component. Do so.

The "RM" UEFI shell command will start working only later in the series;
the shell needs more EFI_FILE_PROTOCOL members to function before it calls
Delete().

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>
---
 OvmfPkg/VirtioFsDxe/SimpleFsDelete.c | 44 ++++++++++++++++++--
 1 file changed, 41 insertions(+), 3 deletions(-)

diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c b/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c
index e2fc2d72dfeb..76cfee5bceb1 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c
@@ -41,19 +41,57 @@ VirtioFsSimpleFileDelete (
   VirtioFsFuseReleaseFileOrDir (VirtioFs, VirtioFsFile->NodeId,
     VirtioFsFile->FuseHandle, VirtioFsFile->IsDirectory);
 
   //
   // VirtioFsFile->FuseHandle is gone at this point, but VirtioFsFile->NodeId
   // is still valid. Continue with removing the file or directory. The result
   // of this operation determines the return status of the function.
   //
-  // TODO
-  //
-  Status = EFI_WARN_DELETE_FAILURE;
+  if (VirtioFsFile->IsOpenForWriting) {
+    UINT64 ParentNodeId;
+    CHAR8  *LastComponent;
+
+    //
+    // Split our canonical pathname into most specific parent directory
+    // (identified by NodeId), and single-component filename within that
+    // directory. If This stands for the root directory "/", then the following
+    // function call will gracefully fail.
+    //
+    Status = VirtioFsLookupMostSpecificParentDir (
+               VirtioFs,
+               VirtioFsFile->CanonicalPathname,
+               &ParentNodeId,
+               &LastComponent
+               );
+    if (!EFI_ERROR (Status)) {
+      //
+      // Attempt the actual removal. Regardless of the outcome, ParentNodeId
+      // must be forgotten right after (unless it stands for the root
+      // directory).
+      //
+      Status = VirtioFsFuseRemoveFileOrDir (
+                 VirtioFs,
+                 ParentNodeId,
+                 LastComponent,
+                 VirtioFsFile->IsDirectory
+                 );
+      if (ParentNodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
+        VirtioFsFuseForget (VirtioFs, ParentNodeId);
+      }
+    }
+    if (EFI_ERROR (Status)) {
+      //
+      // Map any failure to the spec-mandated warning code.
+      //
+      Status = EFI_WARN_DELETE_FAILURE;
+    }
+  } else {
+    Status = EFI_WARN_DELETE_FAILURE;
+  }
 
   //
   // Finally, if we've known VirtioFsFile->NodeId from a lookup, then we should
   // also ask the server to forget it *once*.
   //
   if (VirtioFsFile->NodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
     VirtioFsFuseForget (VirtioFs, VirtioFsFile->NodeId);
   }
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 28/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_STATFS
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (26 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 27/48] OvmfPkg/VirtioFsDxe: erase the dir. entry in EFI_FILE_PROTOCOL.Delete() Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 29/48] OvmfPkg/VirtioFsDxe: add helper for formatting UEFI basenames Laszlo Ersek
                   ` (20 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Add the VirtioFsFuseStatFs() function, for sending the FUSE_STATFS command
to the Virtio Filesystem device.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  17 ++++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |   7 ++
 OvmfPkg/VirtioFsDxe/FuseStatFs.c            | 102 ++++++++++++++++++++
 4 files changed, 127 insertions(+)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index efcf57941c59..800d3651e12b 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -114,16 +114,17 @@ typedef struct {
 typedef enum {
   VirtioFsFuseOpLookup      =  1,
   VirtioFsFuseOpForget      =  2,
   VirtioFsFuseOpGetAttr     =  3,
   VirtioFsFuseOpMkDir       =  9,
   VirtioFsFuseOpUnlink      = 10,
   VirtioFsFuseOpRmDir       = 11,
   VirtioFsFuseOpOpen        = 14,
+  VirtioFsFuseOpStatFs      = 17,
   VirtioFsFuseOpRelease     = 18,
   VirtioFsFuseOpFsync       = 20,
   VirtioFsFuseOpFlush       = 25,
   VirtioFsFuseOpInit        = 26,
   VirtioFsFuseOpOpenDir     = 27,
   VirtioFsFuseOpReleaseDir  = 29,
   VirtioFsFuseOpFsyncDir    = 30,
   VirtioFsFuseOpCreate      = 35,
@@ -228,16 +229,32 @@ typedef struct {
 } VIRTIO_FS_FUSE_OPEN_REQUEST;
 
 typedef struct {
   UINT64 FileHandle;
   UINT32 OpenFlags;
   UINT32 Padding;
 } VIRTIO_FS_FUSE_OPEN_RESPONSE;
 
+//
+// Header for VirtioFsFuseOpStatFs.
+//
+typedef struct {
+  UINT64 Blocks;
+  UINT64 Bfree;
+  UINT64 Bavail;
+  UINT64 Files;
+  UINT64 Ffree;
+  UINT32 Bsize;
+  UINT32 Namelen;
+  UINT32 Frsize;
+  UINT32 Padding;
+  UINT32 Spare[6];
+} VIRTIO_FS_FUSE_STATFS_RESPONSE;
+
 //
 // Header for VirtioFsFuseOpRelease and VirtioFsFuseOpReleaseDir.
 //
 typedef struct {
   UINT64 FileHandle;
   UINT32 Flags;
   UINT32 ReleaseFlags;
   UINT64 LockOwner;
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index 233c0c5c0f9a..318449db3e63 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -89,16 +89,17 @@ [Sources]
   FuseGetAttr.c
   FuseInit.c
   FuseLookup.c
   FuseMkDir.c
   FuseOpen.c
   FuseOpenDir.c
   FuseOpenOrCreate.c
   FuseRelease.c
+  FuseStatFs.c
   FuseUnlink.c
   Helpers.c
   SimpleFsClose.c
   SimpleFsDelete.c
   SimpleFsFlush.c
   SimpleFsGetInfo.c
   SimpleFsGetPosition.c
   SimpleFsOpen.c
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 3bf64c0146fa..029c568b39c7 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -285,16 +285,23 @@ VirtioFsFuseRemoveFileOrDir (
 EFI_STATUS
 VirtioFsFuseOpen (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    NodeId,
   IN     BOOLEAN   ReadWrite,
      OUT UINT64    *FuseHandle
   );
 
+EFI_STATUS
+VirtioFsFuseStatFs (
+  IN OUT VIRTIO_FS                      *VirtioFs,
+  IN     UINT64                         NodeId,
+     OUT VIRTIO_FS_FUSE_STATFS_RESPONSE *FilesysAttr
+     );
+
 EFI_STATUS
 VirtioFsFuseReleaseFileOrDir (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    NodeId,
   IN     UINT64    FuseHandle,
   IN     BOOLEAN   IsDir
   );
 
diff --git a/OvmfPkg/VirtioFsDxe/FuseStatFs.c b/OvmfPkg/VirtioFsDxe/FuseStatFs.c
new file mode 100644
index 000000000000..e794f8977a48
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/FuseStatFs.c
@@ -0,0 +1,102 @@
+/** @file
+  FUSE_STATFS wrapper for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+/**
+  Send the FUSE_STATFS request to the Virtio Filesysem device, for retrieving
+  the attributes of the host-side filesystem that contains NodeId.
+
+  The function may only be called after VirtioFsFuseInitSession() returns
+  successfully and before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_STATFS
+                           request to. On output, the FUSE request counter
+                           "VirtioFs->RequestId" will have been incremented.
+
+  @param[in] NodeId        The inode whose containing filesystem is to be
+                           queried for its attributes.
+
+  @param[out] FilesysAttr  The VIRTIO_FS_FUSE_STATFS_RESPONSE object describing
+                           the filesystem that underlies NodeId.
+
+  @retval EFI_SUCCESS  FilesysAttr has been filled in.
+
+  @return              The "errno" value mapped to an EFI_STATUS code, if the
+                       Virtio Filesystem device explicitly reported an error.
+
+  @return              Error codes propagated from VirtioFsSgListsValidate(),
+                       VirtioFsFuseNewRequest(), VirtioFsSgListsSubmit(),
+                       VirtioFsFuseCheckResponse().
+**/
+EFI_STATUS
+VirtioFsFuseStatFs (
+  IN OUT VIRTIO_FS                      *VirtioFs,
+  IN     UINT64                         NodeId,
+     OUT VIRTIO_FS_FUSE_STATFS_RESPONSE *FilesysAttr
+     )
+{
+  VIRTIO_FS_FUSE_REQUEST        CommonReq;
+  VIRTIO_FS_IO_VECTOR           ReqIoVec[1];
+  VIRTIO_FS_SCATTER_GATHER_LIST ReqSgList;
+  VIRTIO_FS_FUSE_RESPONSE       CommonResp;
+  VIRTIO_FS_IO_VECTOR           RespIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST RespSgList;
+  EFI_STATUS                    Status;
+
+  //
+  // Set up the scatter-gather lists.
+  //
+  ReqIoVec[0].Buffer = &CommonReq;
+  ReqIoVec[0].Size   = sizeof CommonReq;
+  ReqSgList.IoVec    = ReqIoVec;
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);
+
+  RespIoVec[0].Buffer = &CommonResp;
+  RespIoVec[0].Size   = sizeof CommonResp;
+  RespIoVec[1].Buffer = FilesysAttr;
+  RespIoVec[1].Size   = sizeof *FilesysAttr;
+  RespSgList.IoVec    = RespIoVec;
+  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);
+
+  //
+  // Validate the scatter-gather lists; calculate the total transfer sizes.
+  //
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the common request header.
+  //
+  Status = VirtioFsFuseNewRequest (VirtioFs, &CommonReq, ReqSgList.TotalSize,
+             VirtioFsFuseOpStatFs, NodeId);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Submit the request.
+  //
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Verify the response (all response buffers are fixed size).
+  //
+  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);
+  if (Status == EFI_DEVICE_ERROR) {
+    DEBUG ((DEBUG_ERROR, "%a: Label=\"%s\" NodeId=%Lu Errno=%d\n",
+      __FUNCTION__, VirtioFs->Label, NodeId, CommonResp.Error));
+    Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
+  }
+  return Status;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 29/48] OvmfPkg/VirtioFsDxe: add helper for formatting UEFI basenames
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (27 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 28/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_STATFS Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 30/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.GetInfo() Laszlo Ersek
                   ` (19 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

The EFI_FILE_INFO structure, which is output by
EFI_FILE_PROTOCOL.GetInfo(), ends with a flexible CHAR16 array called
"FileName". Add the VirtioFsGetBasename() function, for determining the
required array size, and for filling the array as well.

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>
---
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h |  7 +++
 OvmfPkg/VirtioFsDxe/Helpers.c     | 61 ++++++++++++++++++++
 2 files changed, 68 insertions(+)

diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 029c568b39c7..d1b746c0d8cf 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -229,16 +229,23 @@ VirtioFsAppendPath (
 EFI_STATUS
 VirtioFsLookupMostSpecificParentDir (
   IN OUT VIRTIO_FS *VirtioFs,
   IN OUT CHAR8     *Path,
      OUT UINT64    *DirNodeId,
      OUT CHAR8     **LastComponent
   );
 
+EFI_STATUS
+VirtioFsGetBasename (
+  IN     CHAR8  *Path,
+     OUT CHAR16 *Basename     OPTIONAL,
+  IN OUT UINTN  *BasenameSize
+  );
+
 EFI_STATUS
 VirtioFsFuseAttrToEfiFileInfo (
   IN     VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr,
      OUT EFI_FILE_INFO                      *FileInfo
   );
 
 //
 // Wrapper functions for FUSE commands (primitives).
diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
index 6adc0341dee6..77f718e91233 100644
--- a/OvmfPkg/VirtioFsDxe/Helpers.c
+++ b/OvmfPkg/VirtioFsDxe/Helpers.c
@@ -1717,16 +1717,77 @@ VirtioFsLookupMostSpecificParentDir (
   *LastComponent = Slash + 1;
   return EFI_SUCCESS;
 
 ForgetNextDirNodeId:
   VirtioFsFuseForget (VirtioFs, NextDirNodeId);
   return Status;
 }
 
+/**
+  Format the last component of a canonical pathname into a caller-provided
+  CHAR16 array.
+
+  @param[in] Path              The canonical pathname (as defined in the
+                               description of VirtioFsAppendPath()) to format
+                               the last component of.
+
+  @param[out] Basename         If BasenameSize is zero on input, Basename may
+                               be NULL. Otherwise, Basename is allocated by the
+                               caller. On successful return, Basename contains
+                               the last component of Path, formatted as a
+                               NUL-terminated CHAR16 string. When Path is "/"
+                               on input, Basename is L"" on output.
+
+  @param[in,out] BasenameSize  On input, the number of bytes the caller
+                               provides in Basename. On output, regardless of
+                               return value, the number of bytes required for
+                               formatting Basename, including the terminating
+                               L'\0'.
+
+  @retval EFI_SUCCESS           Basename has been filled in.
+
+  @retval EFI_BUFFER_TOO_SMALL  BasenameSize was too small on input; Basename
+                                has not been modified.
+**/
+EFI_STATUS
+VirtioFsGetBasename (
+  IN     CHAR8  *Path,
+     OUT CHAR16 *Basename     OPTIONAL,
+  IN OUT UINTN  *BasenameSize
+  )
+{
+  UINTN AllocSize;
+  UINTN LastComponent;
+  UINTN Idx;
+  UINTN PathSize;
+
+  AllocSize = *BasenameSize;
+
+  LastComponent = MAX_UINTN;
+  for (Idx = 0; Path[Idx] != '\0'; Idx++) {
+    if (Path[Idx] == '/') {
+      LastComponent = Idx;
+    }
+  }
+  PathSize = Idx + 1;
+  ASSERT (LastComponent < MAX_UINTN);
+  LastComponent++;
+  *BasenameSize = (PathSize - LastComponent) * sizeof Basename[0];
+
+  if (*BasenameSize > AllocSize) {
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  for (Idx = LastComponent; Idx < PathSize; Idx++) {
+    Basename[Idx - LastComponent] = Path[Idx];
+  }
+  return EFI_SUCCESS;
+}
+
 /**
   Convert select fields of a VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
   corresponding fields in EFI_FILE_INFO.
 
   @param[in] FuseAttr   The VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
                         convert the relevant fields from.
 
   @param[out] FileInfo  The EFI_FILE_INFO structure to modify. Importantly, the
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 30/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.GetInfo()
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (28 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 29/48] OvmfPkg/VirtioFsDxe: add helper for formatting UEFI basenames Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 31/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.GetPosition, .SetPosition Laszlo Ersek
                   ` (18 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Using the functions introduced previously, we can now implement
VirtioFsSimpleFileGetInfo().

This allows the "VOL" command to work in the UEFI shell.

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>
---
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf   |   5 +
 OvmfPkg/VirtioFsDxe/SimpleFsGetInfo.c | 190 +++++++++++++++++++-
 2 files changed, 194 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index 318449db3e63..3cb5c101c3a6 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -120,8 +120,13 @@ [LibraryClasses]
   UefiDriverEntryPoint
   VirtioLib
 
 [Protocols]
   gEfiComponentName2ProtocolGuid        ## PRODUCES
   gEfiDriverBindingProtocolGuid         ## PRODUCES
   gEfiSimpleFileSystemProtocolGuid      ## BY_START
   gVirtioDeviceProtocolGuid             ## TO_START
+
+[Guids]
+  gEfiFileInfoGuid
+  gEfiFileSystemInfoGuid
+  gEfiFileSystemVolumeLabelInfoIdGuid
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsGetInfo.c b/OvmfPkg/VirtioFsDxe/SimpleFsGetInfo.c
index 6e870460c014..c8be1d502202 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsGetInfo.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsGetInfo.c
@@ -1,21 +1,209 @@
 /** @file
   EFI_FILE_PROTOCOL.GetInfo() member function for the Virtio Filesystem driver.
 
   Copyright (C) 2020, Red Hat, Inc.
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
+#include <Guid/FileSystemInfo.h>            // gEfiFileSystemInfoGuid
+#include <Guid/FileSystemVolumeLabelInfo.h> // gEfiFileSystemVolumeLabelInfo...
+#include <Library/BaseLib.h>                // StrSize()
+#include <Library/BaseMemoryLib.h>          // CompareGuid()
+
 #include "VirtioFsDxe.h"
 
+/**
+  Provide EFI_FILE_INFO about this particular file.
+**/
+STATIC
+EFI_STATUS
+GetFileInfo (
+  IN     EFI_FILE_PROTOCOL *This,
+  IN OUT UINTN             *BufferSize,
+     OUT VOID              *Buffer
+  )
+{
+  VIRTIO_FS_FILE                     *VirtioFsFile;
+  VIRTIO_FS                          *VirtioFs;
+  UINTN                              AllocSize;
+  UINTN                              BasenameSize;
+  EFI_STATUS                         Status;
+  EFI_FILE_INFO                      *FileInfo;
+  VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
+
+  VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
+  VirtioFs     = VirtioFsFile->OwnerFs;
+
+  AllocSize = *BufferSize;
+
+  //
+  // Calculate the needed size.
+  //
+  BasenameSize = 0;
+  Status = VirtioFsGetBasename (VirtioFsFile->CanonicalPathname, NULL,
+             &BasenameSize);
+  ASSERT (Status == EFI_BUFFER_TOO_SMALL);
+  *BufferSize = OFFSET_OF (EFI_FILE_INFO, FileName) + BasenameSize;
+
+  if (*BufferSize > AllocSize) {
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  //
+  // Set the structure size, and store the basename.
+  //
+  FileInfo = Buffer;
+  FileInfo->Size = *BufferSize;
+  Status = VirtioFsGetBasename (VirtioFsFile->CanonicalPathname,
+             FileInfo->FileName, &BasenameSize);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Fetch the file attributes, and convert them into the caller's buffer.
+  //
+  Status = VirtioFsFuseGetAttr (VirtioFs, VirtioFsFile->NodeId, &FuseAttr);
+  if (!EFI_ERROR (Status)) {
+    Status = VirtioFsFuseAttrToEfiFileInfo (&FuseAttr, FileInfo);
+  }
+  return (Status == EFI_BUFFER_TOO_SMALL) ? EFI_DEVICE_ERROR : Status;
+}
+
+/**
+  Provide EFI_FILE_SYSTEM_INFO about the filesystem this file lives on.
+**/
+STATIC
+EFI_STATUS
+GetFileSystemInfo (
+  IN     EFI_FILE_PROTOCOL *This,
+  IN OUT UINTN             *BufferSize,
+     OUT VOID              *Buffer
+  )
+{
+  VIRTIO_FS_FILE                 *VirtioFsFile;
+  VIRTIO_FS                      *VirtioFs;
+  UINTN                          AllocSize;
+  UINTN                          LabelSize;
+  EFI_STATUS                     Status;
+  VIRTIO_FS_FUSE_STATFS_RESPONSE FilesysAttr;
+  UINT64                         MaxBlocks;
+  EFI_FILE_SYSTEM_INFO           *FilesysInfo;
+
+  VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
+  VirtioFs     = VirtioFsFile->OwnerFs;
+
+  AllocSize = *BufferSize;
+
+  //
+  // Calculate the needed size.
+  //
+  LabelSize = StrSize (VirtioFs->Label);
+  *BufferSize = OFFSET_OF (EFI_FILE_SYSTEM_INFO, VolumeLabel) + LabelSize;
+
+  if (*BufferSize > AllocSize) {
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  //
+  // Fetch the filesystem attributes.
+  //
+  Status = VirtioFsFuseStatFs (VirtioFs, VirtioFsFile->NodeId, &FilesysAttr);
+  if (EFI_ERROR (Status)) {
+    return (Status == EFI_BUFFER_TOO_SMALL) ? EFI_DEVICE_ERROR : Status;
+  }
+  //
+  // Sanity checks...
+  //
+  if (FilesysAttr.Frsize != FilesysAttr.Bsize) {
+    return EFI_UNSUPPORTED;
+  }
+  if (FilesysAttr.Frsize == 0 || FilesysAttr.Blocks == 0 ||
+      FilesysAttr.Bavail > FilesysAttr.Blocks) {
+    return EFI_DEVICE_ERROR;
+  }
+  MaxBlocks = DivU64x32 (MAX_UINT64, FilesysAttr.Frsize);
+  if (FilesysAttr.Blocks > MaxBlocks || FilesysAttr.Bavail > MaxBlocks) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  //
+  // Fill in EFI_FILE_SYSTEM_INFO.
+  //
+  FilesysInfo             = Buffer;
+  FilesysInfo->Size       = *BufferSize;
+  FilesysInfo->ReadOnly   = FALSE;
+  FilesysInfo->VolumeSize = MultU64x32 (FilesysAttr.Blocks,
+                              FilesysAttr.Frsize);
+  FilesysInfo->FreeSpace  = MultU64x32 (FilesysAttr.Bavail,
+                              FilesysAttr.Frsize);
+  FilesysInfo->BlockSize  = FilesysAttr.Frsize;
+  CopyMem (FilesysInfo->VolumeLabel, VirtioFs->Label, LabelSize);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Return the filesystem label as EFI_FILE_SYSTEM_VOLUME_LABEL.
+**/
+STATIC
+EFI_STATUS
+GetFileSystemVolumeLabelInfo (
+  IN     EFI_FILE_PROTOCOL *This,
+  IN OUT UINTN             *BufferSize,
+     OUT VOID              *Buffer
+  )
+{
+  VIRTIO_FS_FILE               *VirtioFsFile;
+  VIRTIO_FS                    *VirtioFs;
+  UINTN                        AllocSize;
+  UINTN                        LabelSize;
+  EFI_FILE_SYSTEM_VOLUME_LABEL *FilesysVolumeLabel;
+
+  VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
+  VirtioFs     = VirtioFsFile->OwnerFs;
+
+  AllocSize = *BufferSize;
+
+  //
+  // Calculate the needed size.
+  //
+  LabelSize = StrSize (VirtioFs->Label);
+  *BufferSize = (OFFSET_OF (EFI_FILE_SYSTEM_VOLUME_LABEL, VolumeLabel) +
+                 LabelSize);
+
+  if (*BufferSize > AllocSize) {
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  //
+  // Store the label.
+  //
+  FilesysVolumeLabel = Buffer;
+  CopyMem (FilesysVolumeLabel->VolumeLabel, VirtioFs->Label, LabelSize);
+
+  return EFI_SUCCESS;
+}
+
 EFI_STATUS
 EFIAPI
 VirtioFsSimpleFileGetInfo (
   IN     EFI_FILE_PROTOCOL *This,
   IN     EFI_GUID          *InformationType,
   IN OUT UINTN             *BufferSize,
      OUT VOID              *Buffer
   )
 {
-  return EFI_NO_MEDIA;
+  if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
+    return GetFileInfo (This, BufferSize, Buffer);
+  }
+
+  if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
+    return GetFileSystemInfo (This, BufferSize, Buffer);
+  }
+
+  if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
+    return GetFileSystemVolumeLabelInfo (This, BufferSize, Buffer);
+  }
+
+  return EFI_UNSUPPORTED;
 }
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 31/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.GetPosition, .SetPosition
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (29 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 30/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.GetInfo() Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 32/48] OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_READ / FUSE_READDIRPLUS Laszlo Ersek
                   ` (17 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Using the functions introduced previously, we can now implement
VirtioFsSimpleFileGetPosition() and VirtioFsSimpleFileSetPosition().

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>
---
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h         |  1 +
 OvmfPkg/VirtioFsDxe/SimpleFsGetPosition.c |  9 ++++-
 OvmfPkg/VirtioFsDxe/SimpleFsOpen.c        |  1 +
 OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c  |  1 +
 OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c | 40 +++++++++++++++++++-
 5 files changed, 50 insertions(+), 2 deletions(-)

diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index d1b746c0d8cf..948fcfb6b6f3 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -134,16 +134,17 @@ typedef struct {
 typedef struct {
   UINT64            Signature;
   EFI_FILE_PROTOCOL SimpleFile;
   BOOLEAN           IsDirectory;
   BOOLEAN           IsOpenForWriting;
   VIRTIO_FS         *OwnerFs;
   LIST_ENTRY        OpenFilesEntry;
   CHAR8             *CanonicalPathname;
+  UINT64            FilePosition;
   //
   // In the FUSE wire protocol, every request except FUSE_INIT refers to a
   // file, namely by the "VIRTIO_FS_FUSE_REQUEST.NodeId" field; that is, by the
   // inode number of the file. However, some of the FUSE requests that we need
   // for some of the EFI_FILE_PROTOCOL member functions require an open file
   // handle *in addition* to the inode number. For simplicity, whenever a
   // VIRTIO_FS_FILE object is created, primarily defined by its NodeId field,
   // we also *open* the referenced file at once, and save the returned file
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsGetPosition.c b/OvmfPkg/VirtioFsDxe/SimpleFsGetPosition.c
index 2f40d2be2693..53212621e970 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsGetPosition.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsGetPosition.c
@@ -11,10 +11,17 @@
 
 EFI_STATUS
 EFIAPI
 VirtioFsSimpleFileGetPosition (
   IN     EFI_FILE_PROTOCOL *This,
      OUT UINT64            *Position
   )
 {
-  return EFI_DEVICE_ERROR;
+  VIRTIO_FS_FILE *VirtioFsFile;
+
+  VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
+  if (VirtioFsFile->IsDirectory) {
+    return EFI_UNSUPPORTED;
+  }
+  *Position = VirtioFsFile->FilePosition;
+  return EFI_SUCCESS;
 }
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsOpen.c b/OvmfPkg/VirtioFsDxe/SimpleFsOpen.c
index 2649c796ac97..7c50ce9c0e76 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsOpen.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsOpen.c
@@ -474,16 +474,17 @@ VirtioFsSimpleFileOpen (
   NewVirtioFsFile->SimpleFile.SetPosition = VirtioFsSimpleFileSetPosition;
   NewVirtioFsFile->SimpleFile.GetInfo     = VirtioFsSimpleFileGetInfo;
   NewVirtioFsFile->SimpleFile.SetInfo     = VirtioFsSimpleFileSetInfo;
   NewVirtioFsFile->SimpleFile.Flush       = VirtioFsSimpleFileFlush;
   NewVirtioFsFile->IsDirectory            = NewNodeIsDirectory;
   NewVirtioFsFile->IsOpenForWriting       = OpenForWriting;
   NewVirtioFsFile->OwnerFs                = VirtioFs;
   NewVirtioFsFile->CanonicalPathname      = NewCanonicalPath;
+  NewVirtioFsFile->FilePosition           = 0;
   NewVirtioFsFile->NodeId                 = NewNodeId;
   NewVirtioFsFile->FuseHandle             = NewFuseHandle;
 
   //
   // One more file is now open for the filesystem.
   //
   InsertTailList (&VirtioFs->OpenFiles, &NewVirtioFsFile->OpenFilesEntry);
 
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c b/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
index 9c0ab434c186..1181191d271b 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
@@ -67,16 +67,17 @@ VirtioFsOpenVolume (
   VirtioFsFile->SimpleFile.SetPosition = VirtioFsSimpleFileSetPosition;
   VirtioFsFile->SimpleFile.GetInfo     = VirtioFsSimpleFileGetInfo;
   VirtioFsFile->SimpleFile.SetInfo     = VirtioFsSimpleFileSetInfo;
   VirtioFsFile->SimpleFile.Flush       = VirtioFsSimpleFileFlush;
   VirtioFsFile->IsDirectory            = TRUE;
   VirtioFsFile->IsOpenForWriting       = FALSE;
   VirtioFsFile->OwnerFs                = VirtioFs;
   VirtioFsFile->CanonicalPathname      = CanonicalPathname;
+  VirtioFsFile->FilePosition           = 0;
   VirtioFsFile->NodeId                 = VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID;
   VirtioFsFile->FuseHandle             = RootDirHandle;
 
   //
   // One more file open for the filesystem.
   //
   InsertTailList (&VirtioFs->OpenFiles, &VirtioFsFile->OpenFilesEntry);
 
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c b/OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c
index ee8cb1f4e465..ac6285302652 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c
@@ -11,10 +11,48 @@
 
 EFI_STATUS
 EFIAPI
 VirtioFsSimpleFileSetPosition (
   IN EFI_FILE_PROTOCOL *This,
   IN UINT64            Position
   )
 {
-  return EFI_DEVICE_ERROR;
+  VIRTIO_FS_FILE                     *VirtioFsFile;
+  VIRTIO_FS                          *VirtioFs;
+  EFI_STATUS                         Status;
+  VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
+
+  VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
+
+  //
+  // Directories can only be rewound, per spec.
+  //
+  if (VirtioFsFile->IsDirectory) {
+    if (Position != 0) {
+      return EFI_UNSUPPORTED;
+    }
+    VirtioFsFile->FilePosition = 0;
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Regular file.
+  //
+  if (Position < MAX_UINT64) {
+    //
+    // Caller is requesting absolute file position.
+    //
+    VirtioFsFile->FilePosition = Position;
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Caller is requesting a seek to EOF.
+  //
+  VirtioFs = VirtioFsFile->OwnerFs;
+  Status = VirtioFsFuseGetAttr (VirtioFs, VirtioFsFile->NodeId, &FuseAttr);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  VirtioFsFile->FilePosition = FuseAttr.Size;
+  return EFI_SUCCESS;
 }
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 32/48] OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_READ / FUSE_READDIRPLUS
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (30 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 31/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.GetPosition, .SetPosition Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 33/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Read() for regular files Laszlo Ersek
                   ` (16 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Add the VirtioFsFuseReadFileOrDir() function, for sending the FUSE_READ or
FUSE_READDIRPLUS command to the Virtio Filesystem device.

Parsing the structured FUSE_READDIRPLUS output is complex, and cannot be
integrated into the wrapper function. Given that fact, FUSE_READ and
FUSE_READDIRPLUS turn out to need identical low-level handling, except for
the opcode. Hence the shared wrapper function.

(It's prudent to verify whether the FUSE server supports FUSE_READDIRPLUS,
so update the session init code accordingly.)

This is the first FUSE request wrapper function that deals with a variable
size tail buffer.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  67 +++++++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |  11 ++
 OvmfPkg/VirtioFsDxe/FuseInit.c              |  10 +-
 OvmfPkg/VirtioFsDxe/FuseRead.c              | 191 ++++++++++++++++++++
 5 files changed, 276 insertions(+), 4 deletions(-)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index 800d3651e12b..fa8c40019f4e 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -103,36 +103,71 @@ typedef struct {
 #define VIRTIO_FS_FUSE_MODE_PERM_WOTH 0000002u
 
 //
 // Flags for VirtioFsFuseOpOpen.
 //
 #define VIRTIO_FS_FUSE_OPEN_REQ_F_RDONLY 0
 #define VIRTIO_FS_FUSE_OPEN_REQ_F_RDWR   2
 
+//
+// Flags for VirtioFsFuseOpInit.
+//
+#define VIRTIO_FS_FUSE_INIT_REQ_F_DO_READDIRPLUS BIT13
+
+/**
+  Macro for calculating the size of a directory stream entry.
+
+  The macro may evaluate Namelen multiple times.
+
+  The macro evaluates to a UINTN value that is safe to cast to UINT32.
+
+  @param[in] Namelen  The size of the filename byte array that follows
+                      VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE in the directory
+                      stream, as reported by
+                      VIRTIO_FS_FUSE_STATFS_RESPONSE.Namelen or
+                      VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE.Namelen. The filename
+                      byte array is not NUL-terminated.
+
+  @retval 0  Namelen was zero or greater than SIZE_4KB.
+
+  @return    The number of bytes in the directory entry, including the
+             VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE header.
+**/
+#define VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE(Namelen)             \
+  ((Namelen) == 0 || (Namelen) > SIZE_4KB ?                          \
+   (UINTN)0 :                                                        \
+   ALIGN_VALUE (                                                     \
+     sizeof (VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE) + (UINTN)(Namelen), \
+     sizeof (UINT64)                                                 \
+     )                                                               \
+   )
+
 //
 // FUSE operation codes.
 //
 typedef enum {
   VirtioFsFuseOpLookup      =  1,
   VirtioFsFuseOpForget      =  2,
   VirtioFsFuseOpGetAttr     =  3,
   VirtioFsFuseOpMkDir       =  9,
   VirtioFsFuseOpUnlink      = 10,
   VirtioFsFuseOpRmDir       = 11,
   VirtioFsFuseOpOpen        = 14,
+  VirtioFsFuseOpRead        = 15,
   VirtioFsFuseOpStatFs      = 17,
   VirtioFsFuseOpRelease     = 18,
   VirtioFsFuseOpFsync       = 20,
   VirtioFsFuseOpFlush       = 25,
   VirtioFsFuseOpInit        = 26,
   VirtioFsFuseOpOpenDir     = 27,
   VirtioFsFuseOpReleaseDir  = 29,
   VirtioFsFuseOpFsyncDir    = 30,
   VirtioFsFuseOpCreate      = 35,
+  VirtioFsFuseOpReadDirPlus = 44,
 } VIRTIO_FS_FUSE_OPCODE;
 
 #pragma pack (1)
 //
 // Request-response headers common to all request types.
 //
 typedef struct {
   UINT32 Len;
@@ -229,16 +264,29 @@ typedef struct {
 } VIRTIO_FS_FUSE_OPEN_REQUEST;
 
 typedef struct {
   UINT64 FileHandle;
   UINT32 OpenFlags;
   UINT32 Padding;
 } VIRTIO_FS_FUSE_OPEN_RESPONSE;
 
+//
+// Header for VirtioFsFuseOpRead and VirtioFsFuseOpReadDirPlus.
+//
+typedef struct {
+  UINT64 FileHandle;
+  UINT64 Offset;
+  UINT32 Size;
+  UINT32 ReadFlags;
+  UINT64 LockOwner;
+  UINT32 Flags;
+  UINT32 Padding;
+} VIRTIO_FS_FUSE_READ_REQUEST;
+
 //
 // Header for VirtioFsFuseOpStatFs.
 //
 typedef struct {
   UINT64 Blocks;
   UINT64 Bfree;
   UINT64 Bavail;
   UINT64 Files;
@@ -307,11 +355,30 @@ typedef struct {
 // Header for VirtioFsFuseOpCreate.
 //
 typedef struct {
   UINT32 Flags;
   UINT32 Mode;
   UINT32 Umask;
   UINT32 Padding;
 } VIRTIO_FS_FUSE_CREATE_REQUEST;
+
+//
+// Header for VirtioFsFuseOpReadDirPlus.
+//
+// Diverging from the rest of the headers, this structure embeds other
+// structures. The reason is that a scatter list cannot be used to receive
+// NodeResp and AttrResp separately; the record below is followed by a variable
+// size filename byte array, and then such pairs are repeated a number of
+// times. Thus, later header start offsets depend on earlier filename array
+// sizes.
+//
+typedef struct {
+  VIRTIO_FS_FUSE_NODE_RESPONSE       NodeResp;
+  VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE AttrResp;
+  UINT64                             NodeId;
+  UINT64                             CookieForNextEntry;
+  UINT32                             Namelen;
+  UINT32                             Type;
+} VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE;
 #pragma pack ()
 
 #endif // VIRTIO_FS_H_
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index 3cb5c101c3a6..39c77c7c4ee9 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -88,16 +88,17 @@ [Sources]
   FuseFsync.c
   FuseGetAttr.c
   FuseInit.c
   FuseLookup.c
   FuseMkDir.c
   FuseOpen.c
   FuseOpenDir.c
   FuseOpenOrCreate.c
+  FuseRead.c
   FuseRelease.c
   FuseStatFs.c
   FuseUnlink.c
   Helpers.c
   SimpleFsClose.c
   SimpleFsDelete.c
   SimpleFsFlush.c
   SimpleFsGetInfo.c
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 948fcfb6b6f3..f5501af7d0a4 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -293,16 +293,27 @@ VirtioFsFuseRemoveFileOrDir (
 EFI_STATUS
 VirtioFsFuseOpen (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    NodeId,
   IN     BOOLEAN   ReadWrite,
      OUT UINT64    *FuseHandle
   );
 
+EFI_STATUS
+VirtioFsFuseReadFileOrDir (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId,
+  IN     UINT64    FuseHandle,
+  IN     BOOLEAN   IsDir,
+  IN     UINT64    Offset,
+  IN OUT UINT32    *Size,
+     OUT VOID      *Data
+  );
+
 EFI_STATUS
 VirtioFsFuseStatFs (
   IN OUT VIRTIO_FS                      *VirtioFs,
   IN     UINT64                         NodeId,
      OUT VIRTIO_FS_FUSE_STATFS_RESPONSE *FilesysAttr
      );
 
 EFI_STATUS
diff --git a/OvmfPkg/VirtioFsDxe/FuseInit.c b/OvmfPkg/VirtioFsDxe/FuseInit.c
index aa19dbdc05cb..7aa6ee75caf6 100644
--- a/OvmfPkg/VirtioFsDxe/FuseInit.c
+++ b/OvmfPkg/VirtioFsDxe/FuseInit.c
@@ -21,17 +21,18 @@
   before VirtioFsUninit() is called.
 
   @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_INIT
                            request to. The FUSE request counter
                            "VirtioFs->RequestId" is set to 1 on output.
 
   @retval EFI_SUCCESS      The FUSE session has been started.
 
-  @retval EFI_UNSUPPORTED  FUSE interface version negotiation failed.
+  @retval EFI_UNSUPPORTED  FUSE interface version or feature negotiation
+                           failed.
 
   @return                  The "errno" value mapped to an EFI_STATUS code, if
                            the Virtio Filesystem device explicitly reported an
                            error.
 
   @return                  Error codes propagated from
                            VirtioFsSgListsValidate(), VirtioFsFuseNewRequest(),
                            VirtioFsSgListsSubmit(),
@@ -92,17 +93,17 @@ VirtioFsFuseInitSession (
   }
 
   //
   // Populate the FUSE_INIT-specific fields.
   //
   InitReq.Major        = VIRTIO_FS_FUSE_MAJOR;
   InitReq.Minor        = VIRTIO_FS_FUSE_MINOR;
   InitReq.MaxReadahead = 0;
-  InitReq.Flags        = 0;
+  InitReq.Flags        = VIRTIO_FS_FUSE_INIT_REQ_F_DO_READDIRPLUS;
 
   //
   // Submit the request.
   //
   Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
   if (EFI_ERROR (Status)) {
     return Status;
   }
@@ -116,17 +117,18 @@ VirtioFsFuseInitSession (
       DEBUG ((DEBUG_ERROR, "%a: Label=\"%s\" Errno=%d\n", __FUNCTION__,
         VirtioFs->Label, CommonResp.Error));
       Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
     }
     return Status;
   }
 
   //
-  // Check FUSE interface version compatibility.
+  // Check FUSE interface version / feature compatibility.
   //
   if (InitResp.Major < InitReq.Major ||
-      (InitResp.Major == InitReq.Major && InitResp.Minor < InitReq.Minor)) {
+      (InitResp.Major == InitReq.Major && InitResp.Minor < InitReq.Minor) ||
+      (InitResp.Flags & VIRTIO_FS_FUSE_INIT_REQ_F_DO_READDIRPLUS) == 0) {
     return EFI_UNSUPPORTED;
   }
 
   return EFI_SUCCESS;
 }
diff --git a/OvmfPkg/VirtioFsDxe/FuseRead.c b/OvmfPkg/VirtioFsDxe/FuseRead.c
new file mode 100644
index 000000000000..1611e298b90d
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/FuseRead.c
@@ -0,0 +1,191 @@
+/** @file
+  FUSE_READ / FUSE_READDIRPLUS wrapper for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+/**
+  Read a chunk from a regular file or a directory stream, by sending the
+  FUSE_READ / FUSE_READDIRPLUS request to the Virtio Filesystem device.
+
+  The function may only be called after VirtioFsFuseInitSession() returns
+  successfully and before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_READ
+                           or FUSE_READDIRPLUS request to. On output, the FUSE
+                           request counter "VirtioFs->RequestId" will have been
+                           incremented.
+
+  @param[in] NodeId        The inode number of the regular file or directory
+                           stream to read from.
+
+  @param[in] FuseHandle    The open handle to the regular file or directory
+                           stream to read from.
+
+  @param[in] IsDir         TRUE if NodeId and FuseHandle refer to a directory,
+                           FALSE if NodeId and FuseHandle refer to a regular
+                           file.
+
+  @param[in] Offset        If IsDir is FALSE: the absolute file position at
+                           which to start reading.
+
+                           If IsDir is TRUE: the directory stream cookie at
+                           which to start or continue reading. The zero-valued
+                           cookie identifies the start of the directory stream.
+                           Further positions in the directory stream can be
+                           passed in from the CookieForNextEntry field of
+                           VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE.
+
+  @param[in,out] Size      On input, the number of bytes to read. On successful
+                           return, the number of bytes actually read, which may
+                           be smaller than the value on input.
+
+                           When reading a regular file (i.e., when IsDir is
+                           FALSE), EOF can be detected by passing in a nonzero
+                           Size, and finding a zero Size on output.
+
+                           When reading a directory stream (i.e., when IsDir is
+                           TRUE), Data consists of a sequence of variably-sized
+                           records (directory entries). A read operation
+                           returns the maximal sequence of records that fits in
+                           Size, without having to truncate a record. In order
+                           to guarantee progress, call
+
+                             VirtioFsFuseStatFs (VirtioFs, NodeId,
+                               &FilesysAttr)
+
+                           first, to learn the maximum Namelen for the
+                           directory stream. Then assign Size at least
+
+                             VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE (
+                               FilesysAttr.Namelen)
+
+                           on input. (Remember that
+                           VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE() may return
+                           0 if its Namelen argument is invalid.) EOF can be
+                           detected if Size is set on input like described
+                           above, and Size is zero on output.
+
+  @param[out] Data         Buffer to read the bytes from the regular file or
+                           the directory stream into. The caller is responsible
+                           for providing room for (at least) as many bytes in
+                           Data as Size is on input.
+
+  @retval EFI_SUCCESS  Read successful. The caller is responsible for checking
+                       Size to learn the actual byte count transferred.
+
+  @return              The "errno" value mapped to an EFI_STATUS code, if the
+                       Virtio Filesystem device explicitly reported an error.
+
+  @return              Error codes propagated from VirtioFsSgListsValidate(),
+                       VirtioFsFuseNewRequest(), VirtioFsSgListsSubmit(),
+                       VirtioFsFuseCheckResponse().
+**/
+EFI_STATUS
+VirtioFsFuseReadFileOrDir (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId,
+  IN     UINT64    FuseHandle,
+  IN     BOOLEAN   IsDir,
+  IN     UINT64    Offset,
+  IN OUT UINT32    *Size,
+     OUT VOID      *Data
+  )
+{
+  VIRTIO_FS_FUSE_REQUEST        CommonReq;
+  VIRTIO_FS_FUSE_READ_REQUEST   ReadReq;
+  VIRTIO_FS_IO_VECTOR           ReqIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST ReqSgList;
+  VIRTIO_FS_FUSE_RESPONSE       CommonResp;
+  VIRTIO_FS_IO_VECTOR           RespIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST RespSgList;
+  EFI_STATUS                    Status;
+  UINTN                         TailBufferFill;
+
+  //
+  // Set up the scatter-gather lists.
+  //
+  ReqIoVec[0].Buffer = &CommonReq;
+  ReqIoVec[0].Size   = sizeof CommonReq;
+  ReqIoVec[1].Buffer = &ReadReq;
+  ReqIoVec[1].Size   = sizeof ReadReq;
+  ReqSgList.IoVec    = ReqIoVec;
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);
+
+  RespIoVec[0].Buffer = &CommonResp;
+  RespIoVec[0].Size   = sizeof CommonResp;
+  RespIoVec[1].Buffer = Data;
+  RespIoVec[1].Size   = *Size;
+  RespSgList.IoVec    = RespIoVec;
+  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);
+
+  //
+  // Validate the scatter-gather lists; calculate the total transfer sizes.
+  //
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the common request header.
+  //
+  Status = VirtioFsFuseNewRequest (
+             VirtioFs,
+             &CommonReq,
+             ReqSgList.TotalSize,
+             IsDir ? VirtioFsFuseOpReadDirPlus : VirtioFsFuseOpRead,
+             NodeId
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the FUSE_READ- / FUSE_READDIRPLUS-specific fields.
+  //
+  ReadReq.FileHandle = FuseHandle;
+  ReadReq.Offset     = Offset;
+  ReadReq.Size       = *Size;
+  ReadReq.ReadFlags  = 0;
+  ReadReq.LockOwner  = 0;
+  ReadReq.Flags      = 0;
+  ReadReq.Padding    = 0;
+
+  //
+  // Submit the request.
+  //
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Verify the response. Note that TailBufferFill is variable.
+  //
+  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique,
+             &TailBufferFill);
+  if (EFI_ERROR (Status)) {
+    if (Status == EFI_DEVICE_ERROR) {
+      DEBUG ((DEBUG_ERROR, "%a: Label=\"%s\" NodeId=%Lu FuseHandle=%Lu "
+        "IsDir=%d Offset=0x%Lx Size=0x%x Data@%p Errno=%d\n", __FUNCTION__,
+        VirtioFs->Label, NodeId, FuseHandle, IsDir, Offset, *Size, Data,
+        CommonResp.Error));
+      Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
+    }
+    return Status;
+  }
+
+  //
+  // Report the actual transfer size.
+  //
+  // Integer overflow in the (UINT32) cast below is not possible; the
+  // VIRTIO_FS_SCATTER_GATHER_LIST functions would have caught that.
+  //
+  *Size = (UINT32)TailBufferFill;
+  return EFI_SUCCESS;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 33/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Read() for regular files
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (31 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 32/48] OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_READ / FUSE_READDIRPLUS Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 34/48] OvmfPkg/VirtioFsDxe: convert FUSE dirent filename to EFI_FILE_INFO Laszlo Ersek
                   ` (15 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Using the functions introduced previously, we can now implement
VirtioFsSimpleFileRead(); for regular files at first.

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>
---
 OvmfPkg/VirtioFsDxe/SimpleFsRead.c | 75 +++++++++++++++++++-
 1 file changed, 74 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsRead.c b/OvmfPkg/VirtioFsDxe/SimpleFsRead.c
index e737d5e33204..c4ad07c9aec4 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsRead.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsRead.c
@@ -3,18 +3,91 @@
 
   Copyright (C) 2020, Red Hat, Inc.
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
 #include "VirtioFsDxe.h"
 
+/**
+  Read from a regular file.
+**/
+STATIC
+EFI_STATUS
+ReadRegularFile (
+  IN OUT VIRTIO_FS_FILE *VirtioFsFile,
+  IN OUT UINTN          *BufferSize,
+     OUT VOID           *Buffer
+  )
+{
+  VIRTIO_FS                          *VirtioFs;
+  EFI_STATUS                         Status;
+  VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
+  UINTN                              Transferred;
+  UINTN                              Left;
+
+  VirtioFs = VirtioFsFile->OwnerFs;
+  //
+  // The UEFI spec forbids reads that start beyond the end of the file.
+  //
+  Status = VirtioFsFuseGetAttr (VirtioFs, VirtioFsFile->NodeId, &FuseAttr);
+  if (EFI_ERROR (Status) || VirtioFsFile->FilePosition > FuseAttr.Size) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  Status      = EFI_SUCCESS;
+  Transferred = 0;
+  Left        = *BufferSize;
+  while (Left > 0) {
+    UINT32 ReadSize;
+
+    //
+    // FUSE_READ cannot express a >=4GB buffer size.
+    //
+    ReadSize = (UINT32)MIN ((UINTN)MAX_UINT32, Left);
+    Status = VirtioFsFuseReadFileOrDir (
+               VirtioFs,
+               VirtioFsFile->NodeId,
+               VirtioFsFile->FuseHandle,
+               FALSE,                                    // IsDir
+               VirtioFsFile->FilePosition + Transferred,
+               &ReadSize,
+               (UINT8 *)Buffer + Transferred
+               );
+    if (EFI_ERROR (Status) || ReadSize == 0) {
+      break;
+    }
+    Transferred += ReadSize;
+    Left        -= ReadSize;
+  }
+
+  *BufferSize = Transferred;
+  VirtioFsFile->FilePosition += Transferred;
+  //
+  // If we managed to read some data, return success. If zero bytes were
+  // transferred due to zero-sized buffer on input or due to EOF on first read,
+  // return SUCCESS. Otherwise, return the error due to which zero bytes were
+  // transferred.
+  //
+  return (Transferred > 0) ? EFI_SUCCESS : Status;
+}
+
 EFI_STATUS
 EFIAPI
 VirtioFsSimpleFileRead (
   IN     EFI_FILE_PROTOCOL *This,
   IN OUT UINTN             *BufferSize,
      OUT VOID              *Buffer
   )
 {
-  return EFI_NO_MEDIA;
+  VIRTIO_FS_FILE *VirtioFsFile;
+  EFI_STATUS     Status;
+
+  VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
+
+  if (VirtioFsFile->IsDirectory) {
+    Status = EFI_NO_MEDIA;
+  } else {
+    Status = ReadRegularFile (VirtioFsFile, BufferSize, Buffer);
+  }
+  return Status;
 }
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 34/48] OvmfPkg/VirtioFsDxe: convert FUSE dirent filename to EFI_FILE_INFO
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (32 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 33/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Read() for regular files Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 35/48] OvmfPkg/VirtioFsDxe: add EFI_FILE_INFO cache fields to VIRTIO_FS_FILE Laszlo Ersek
                   ` (14 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Introduce the VirtioFsFuseDirentPlusToEfiFileInfo() function, for
converting the VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE filename byte array to
EFI_FILE_INFO.

This new function updates those EFI_FILE_INFO fields (Size, FileName) that
the earlier helper function VirtioFsFuseAttrToEfiFileInfo() does not set.

Both functions together will be able to fill in EFI_FILE_INFO completely,
from FUSE_READDIRPLUS.

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>
---
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h |  6 ++
 OvmfPkg/VirtioFsDxe/Helpers.c     | 79 ++++++++++++++++++++
 2 files changed, 85 insertions(+)

diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index f5501af7d0a4..2b419048c232 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -243,16 +243,22 @@ VirtioFsGetBasename (
   );
 
 EFI_STATUS
 VirtioFsFuseAttrToEfiFileInfo (
   IN     VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr,
      OUT EFI_FILE_INFO                      *FileInfo
   );
 
+EFI_STATUS
+VirtioFsFuseDirentPlusToEfiFileInfo (
+  IN     VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE *FuseDirent,
+  IN OUT EFI_FILE_INFO                      *FileInfo
+  );
+
 //
 // Wrapper functions for FUSE commands (primitives).
 //
 
 EFI_STATUS
 VirtioFsFuseLookup (
   IN OUT VIRTIO_FS                          *VirtioFs,
   IN     UINT64                             DirNodeId,
diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
index 77f718e91233..cdaa8557a17b 100644
--- a/OvmfPkg/VirtioFsDxe/Helpers.c
+++ b/OvmfPkg/VirtioFsDxe/Helpers.c
@@ -1895,8 +1895,87 @@ VirtioFsFuseAttrToEfiFileInfo (
   // A hard link count greater than 1 is not supported for regular files.
   //
   if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) == 0 && FuseAttr->Nlink > 1) {
     return EFI_UNSUPPORTED;
   }
 
   return EFI_SUCCESS;
 }
+
+/**
+  Convert a VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE filename to an EFI_FILE_INFO
+  filename.
+
+  @param[in] FuseDirent    The VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE object to
+                           convert the filename byte array from. The caller is
+                           responsible for ensuring that FuseDirent->Namelen
+                           describe valid storage.
+
+  @param[in,out] FileInfo  The EFI_FILE_INFO structure to modify. On input, the
+                           caller is responsible for setting FileInfo->Size
+                           according to the allocated size. On successful
+                           return, FileInfo->Size is reduced to reflect the
+                           filename converted into FileInfo->FileName.
+                           FileInfo->FileName is set from the filename byte
+                           array that directly follows the FuseDirent header
+                           object. Fields other than FileInfo->Size and
+                           FileInfo->FileName are not modified.
+
+  @retval EFI_SUCCESS            Conversion successful.
+
+  @retval EFI_INVALID_PARAMETER  VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE()
+                                 returns zero for FuseDirent->Namelen.
+
+  @retval EFI_BUFFER_TOO_SMALL   On input, FileInfo->Size does not provide
+                                 enough room for converting the filename byte
+                                 array from FuseDirent.
+
+  @retval EFI_UNSUPPORTED        The FuseDirent filename byte array contains a
+                                 byte that falls outside of the printable ASCII
+                                 range, or is a forward slash or a backslash.
+**/
+EFI_STATUS
+VirtioFsFuseDirentPlusToEfiFileInfo (
+  IN     VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE *FuseDirent,
+  IN OUT EFI_FILE_INFO                      *FileInfo
+  )
+{
+  UINTN  DirentSize;
+  UINTN  FileInfoSize;
+  UINT8  *DirentName;
+  UINT32 Idx;
+
+  DirentSize = VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE (FuseDirent->Namelen);
+  if (DirentSize == 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+  //
+  // We're now safe from overflow in the calculation below.
+  //
+  FileInfoSize = (OFFSET_OF (EFI_FILE_INFO, FileName) +
+                  ((UINTN)FuseDirent->Namelen + 1) * sizeof (CHAR16));
+  if (FileInfoSize > FileInfo->Size) {
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  //
+  // Convert the name.
+  //
+  DirentName = (UINT8 *)(FuseDirent + 1);
+  for (Idx = 0; Idx < FuseDirent->Namelen; Idx++) {
+    UINT8 NameByte;
+
+    NameByte = DirentName[Idx];
+    if (NameByte < 0x20 || NameByte > 0x7E ||
+        NameByte == '/' || NameByte == '\\') {
+      return EFI_UNSUPPORTED;
+    }
+    FileInfo->FileName[Idx] = (CHAR16)NameByte;
+  }
+  FileInfo->FileName[Idx++] = L'\0';
+  //
+  // Set the (possibly reduced) size.
+  //
+  FileInfo->Size = FileInfoSize;
+
+  return EFI_SUCCESS;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 35/48] OvmfPkg/VirtioFsDxe: add EFI_FILE_INFO cache fields to VIRTIO_FS_FILE
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (33 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 34/48] OvmfPkg/VirtioFsDxe: convert FUSE dirent filename to EFI_FILE_INFO Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 36/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Read() for directories Laszlo Ersek
                   ` (13 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

For reading through a directory stream with tolerable performance, we'll
have to call FUSE_READDIRPLUS each time with such a buffer that can
deliver a good number of variable size records
(VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE elements). Every time we'll do that,
we'll turn the whole bunch into an array of EFI_FILE_INFOs immediately.
EFI_FILE_PROTOCOL.Read() invocations (on directories) will be served from
this EFI_FILE_INFO cache.

Add the fields for the EFI_FILE_INFO cache to VIRTIO_FS_FILE:
- initialize them in Open() and OpenVolume(),
- release the cache in Close() and Delete(),
- also release the cache when the directory is rewound, in SetPosition().

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>
---
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h         | 19 +++++++++++++++++++
 OvmfPkg/VirtioFsDxe/SimpleFsClose.c       |  3 +++
 OvmfPkg/VirtioFsDxe/SimpleFsDelete.c      |  3 +++
 OvmfPkg/VirtioFsDxe/SimpleFsOpen.c        |  4 ++++
 OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c  |  4 ++++
 OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c |  9 +++++++++
 6 files changed, 42 insertions(+)

diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 2b419048c232..a704acdd520e 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -42,16 +42,21 @@
 //
 // The limit is not expected to be a practical limitation; it's only supposed
 // to prevent attempts at overflowing size calculations. For both kinds of
 // pathnames, separate limits could be used; a common limit is used purely for
 // simplicity.
 //
 #define VIRTIO_FS_MAX_PATHNAME_LENGTH ((UINTN)65535)
 
+//
+// Maximum value for VIRTIO_FS_FILE.NumFileInfo.
+//
+#define VIRTIO_FS_FILE_MAX_FILE_INFO 256
+
 //
 // Filesystem label encoded in UCS-2, transformed from the UTF-8 representation
 // in "VIRTIO_FS_CONFIG.Tag", and NUL-terminated. Only the printable ASCII code
 // points (U+0020 through U+007E) are supported.
 //
 typedef CHAR16 VIRTIO_FS_LABEL[VIRTIO_FS_TAG_BYTES + 1];
 
 //
@@ -149,16 +154,30 @@ typedef struct {
   // VIRTIO_FS_FILE object is created, primarily defined by its NodeId field,
   // we also *open* the referenced file at once, and save the returned file
   // handle in the FuseHandle field. This way, when an EFI_FILE_PROTOCOL member
   // function must send a FUSE request that needs the file handle *in addition*
   // to the inode number, FuseHandle will be at our disposal at once.
   //
   UINT64 NodeId;
   UINT64 FuseHandle;
+  //
+  // EFI_FILE_INFO objects cached for an in-flight directory read.
+  //
+  // For reading through a directory stream with tolerable performance, we have
+  // to call FUSE_READDIRPLUS each time with such a buffer that can deliver a
+  // good number of variable size records (VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE
+  // elements). Every time we do that, we turn the whole bunch into an array of
+  // EFI_FILE_INFOs immediately. EFI_FILE_PROTOCOL.Read() invocations (on
+  // directories) will be served from this EFI_FILE_INFO cache.
+  //
+  UINT8 *FileInfoArray;
+  UINTN SingleFileInfoSize;
+  UINTN NumFileInfo;
+  UINTN NextFileInfo;
 } VIRTIO_FS_FILE;
 
 #define VIRTIO_FS_FILE_FROM_SIMPLE_FILE(SimpleFileReference) \
   CR (SimpleFileReference, VIRTIO_FS_FILE, SimpleFile, VIRTIO_FS_FILE_SIG);
 
 #define VIRTIO_FS_FILE_FROM_OPEN_FILES_ENTRY(OpenFilesEntryReference) \
   CR (OpenFilesEntryReference, VIRTIO_FS_FILE, OpenFilesEntry, \
     VIRTIO_FS_FILE_SIG);
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsClose.c b/OvmfPkg/VirtioFsDxe/SimpleFsClose.c
index 04b4f2c382d7..4c1cf52112ce 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsClose.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsClose.c
@@ -55,11 +55,14 @@ VirtioFsSimpleFileClose (
   }
 
   //
   // One fewer file left open for the owner filesystem.
   //
   RemoveEntryList (&VirtioFsFile->OpenFilesEntry);
 
   FreePool (VirtioFsFile->CanonicalPathname);
+  if (VirtioFsFile->FileInfoArray != NULL) {
+    FreePool (VirtioFsFile->FileInfoArray);
+  }
   FreePool (VirtioFsFile);
   return EFI_SUCCESS;
 }
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c b/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c
index 76cfee5bceb1..76a868b3c24d 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsDelete.c
@@ -97,11 +97,14 @@ VirtioFsSimpleFileDelete (
   }
 
   //
   // One fewer file left open for the owner filesystem.
   //
   RemoveEntryList (&VirtioFsFile->OpenFilesEntry);
 
   FreePool (VirtioFsFile->CanonicalPathname);
+  if (VirtioFsFile->FileInfoArray != NULL) {
+    FreePool (VirtioFsFile->FileInfoArray);
+  }
   FreePool (VirtioFsFile);
   return Status;
 }
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsOpen.c b/OvmfPkg/VirtioFsDxe/SimpleFsOpen.c
index 7c50ce9c0e76..d73d23fe8665 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsOpen.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsOpen.c
@@ -477,16 +477,20 @@ VirtioFsSimpleFileOpen (
   NewVirtioFsFile->SimpleFile.Flush       = VirtioFsSimpleFileFlush;
   NewVirtioFsFile->IsDirectory            = NewNodeIsDirectory;
   NewVirtioFsFile->IsOpenForWriting       = OpenForWriting;
   NewVirtioFsFile->OwnerFs                = VirtioFs;
   NewVirtioFsFile->CanonicalPathname      = NewCanonicalPath;
   NewVirtioFsFile->FilePosition           = 0;
   NewVirtioFsFile->NodeId                 = NewNodeId;
   NewVirtioFsFile->FuseHandle             = NewFuseHandle;
+  NewVirtioFsFile->FileInfoArray          = NULL;
+  NewVirtioFsFile->SingleFileInfoSize     = 0;
+  NewVirtioFsFile->NumFileInfo            = 0;
+  NewVirtioFsFile->NextFileInfo           = 0;
 
   //
   // One more file is now open for the filesystem.
   //
   InsertTailList (&VirtioFs->OpenFiles, &NewVirtioFsFile->OpenFilesEntry);
 
   *NewHandle = &NewVirtioFsFile->SimpleFile;
   return EFI_SUCCESS;
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c b/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
index 1181191d271b..058e7a41ab03 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
@@ -70,16 +70,20 @@ VirtioFsOpenVolume (
   VirtioFsFile->SimpleFile.Flush       = VirtioFsSimpleFileFlush;
   VirtioFsFile->IsDirectory            = TRUE;
   VirtioFsFile->IsOpenForWriting       = FALSE;
   VirtioFsFile->OwnerFs                = VirtioFs;
   VirtioFsFile->CanonicalPathname      = CanonicalPathname;
   VirtioFsFile->FilePosition           = 0;
   VirtioFsFile->NodeId                 = VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID;
   VirtioFsFile->FuseHandle             = RootDirHandle;
+  VirtioFsFile->FileInfoArray          = NULL;
+  VirtioFsFile->SingleFileInfoSize     = 0;
+  VirtioFsFile->NumFileInfo            = 0;
+  VirtioFsFile->NextFileInfo           = 0;
 
   //
   // One more file open for the filesystem.
   //
   InsertTailList (&VirtioFs->OpenFiles, &VirtioFsFile->OpenFilesEntry);
 
   *Root = &VirtioFsFile->SimpleFile;
   return EFI_SUCCESS;
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c b/OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c
index ac6285302652..a548012d9c09 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c
@@ -2,16 +2,18 @@
   EFI_FILE_PROTOCOL.SetPosition() member function for the Virtio Filesystem
   driver.
 
   Copyright (C) 2020, Red Hat, Inc.
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
+#include <Library/MemoryAllocationLib.h> // FreePool()
+
 #include "VirtioFsDxe.h"
 
 EFI_STATUS
 EFIAPI
 VirtioFsSimpleFileSetPosition (
   IN EFI_FILE_PROTOCOL *This,
   IN UINT64            Position
   )
@@ -26,16 +28,23 @@ VirtioFsSimpleFileSetPosition (
   //
   // Directories can only be rewound, per spec.
   //
   if (VirtioFsFile->IsDirectory) {
     if (Position != 0) {
       return EFI_UNSUPPORTED;
     }
     VirtioFsFile->FilePosition = 0;
+    if (VirtioFsFile->FileInfoArray != NULL) {
+      FreePool (VirtioFsFile->FileInfoArray);
+      VirtioFsFile->FileInfoArray = NULL;
+    }
+    VirtioFsFile->SingleFileInfoSize = 0;
+    VirtioFsFile->NumFileInfo        = 0;
+    VirtioFsFile->NextFileInfo       = 0;
     return EFI_SUCCESS;
   }
 
   //
   // Regular file.
   //
   if (Position < MAX_UINT64) {
     //
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 36/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Read() for directories
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (34 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 35/48] OvmfPkg/VirtioFsDxe: add EFI_FILE_INFO cache fields to VIRTIO_FS_FILE Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 37/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Flush() Laszlo Ersek
                   ` (12 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Using the functions introduced previously, we can now implement
VirtioFsSimpleFileRead() for directories as well.

This patch completes the read-only support for virtio-fs. Commands like
"TYPE" and "DIR" work in the UEFI shell.

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>
---
 OvmfPkg/VirtioFsDxe/SimpleFsRead.c | 343 +++++++++++++++++++-
 1 file changed, 342 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsRead.c b/OvmfPkg/VirtioFsDxe/SimpleFsRead.c
index c4ad07c9aec4..d6368342a139 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsRead.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsRead.c
@@ -1,18 +1,359 @@
 /** @file
   EFI_FILE_PROTOCOL.Read() member function for the Virtio Filesystem driver.
 
   Copyright (C) 2020, Red Hat, Inc.
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
+#include <Library/BaseMemoryLib.h>       // CopyMem()
+#include <Library/MemoryAllocationLib.h> // AllocatePool()
+
 #include "VirtioFsDxe.h"
 
+/**
+  Populate a caller-allocated EFI_FILE_INFO object from
+  VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE.
+
+  @param[in] Dirent              The entry read from the directory stream. The
+                                 caller is responsible for ensuring that
+                                 Dirent->Namelen describe valid storage.
+
+  @param[in] SingleFileInfoSize  The allocated size of FileInfo.
+
+  @param[out] FileInfo           The EFI_FILE_INFO object to populate. On
+                                 success, all fields in FileInfo will be
+                                 updated, setting FileInfo->Size to the
+                                 actually used size (which will not exceed
+                                 SingleFileInfoSize).
+
+  @retval EFI_SUCCESS  FileInfo has been filled in.
+
+  @return              Error codes propagated from
+                       VirtioFsFuseDirentPlusToEfiFileInfo() and
+                       VirtioFsFuseAttrToEfiFileInfo(). The contents of
+                       FileInfo are indeterminate.
+**/
+STATIC
+EFI_STATUS
+PopulateFileInfo (
+  IN     VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE *Dirent,
+  IN     UINTN                              SingleFileInfoSize,
+     OUT EFI_FILE_INFO                      *FileInfo
+  )
+{
+  EFI_STATUS Status;
+
+  //
+  // Convert the name, set the actual size.
+  //
+  FileInfo->Size = SingleFileInfoSize;
+  Status = VirtioFsFuseDirentPlusToEfiFileInfo (Dirent, FileInfo);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  //
+  // Populate the scalar fields.
+  //
+  Status = VirtioFsFuseAttrToEfiFileInfo (&Dirent->AttrResp, FileInfo);
+  return Status;
+}
+
+/**
+  Refill the EFI_FILE_INFO cache from the directory stream.
+**/
+STATIC
+EFI_STATUS
+RefillFileInfoCache (
+  IN OUT VIRTIO_FS_FILE *VirtioFsFile
+  )
+{
+  VIRTIO_FS                      *VirtioFs;
+  EFI_STATUS                     Status;
+  VIRTIO_FS_FUSE_STATFS_RESPONSE FilesysAttr;
+  UINT32                         DirentBufSize;
+  UINT8                          *DirentBuf;
+  UINTN                          SingleFileInfoSize;
+  UINT8                          *FileInfoArray;
+  UINT64                         DirStreamCookie;
+  UINT64                         CacheEndsAtCookie;
+  UINTN                          NumFileInfo;
+
+  //
+  // Allocate a DirentBuf that can receive at least
+  // VIRTIO_FS_FILE_MAX_FILE_INFO directory entries, based on the maximum
+  // filename length supported by the filesystem. Note that the multiplication
+  // is safe from overflow due to the VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE()
+  // check.
+  //
+  VirtioFs = VirtioFsFile->OwnerFs;
+  Status = VirtioFsFuseStatFs (VirtioFs, VirtioFsFile->NodeId, &FilesysAttr);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  DirentBufSize = (UINT32)VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE (
+                            FilesysAttr.Namelen);
+  if (DirentBufSize == 0) {
+    return EFI_UNSUPPORTED;
+  }
+  DirentBufSize *= VIRTIO_FS_FILE_MAX_FILE_INFO;
+  DirentBuf = AllocatePool (DirentBufSize);
+  if (DirentBuf == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Allocate the EFI_FILE_INFO cache. A single EFI_FILE_INFO element is sized
+  // accordingly to the maximum filename length supported by the filesystem.
+  //
+  // Note that the calculation below cannot overflow, due to the filename limit
+  // imposed by the VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE() check above. The
+  // calculation takes the L'\0' character that we'll need to append into
+  // account.
+  //
+  SingleFileInfoSize = (OFFSET_OF (EFI_FILE_INFO, FileName) +
+                        ((UINTN)FilesysAttr.Namelen + 1) * sizeof (CHAR16));
+  FileInfoArray = AllocatePool (
+                    VIRTIO_FS_FILE_MAX_FILE_INFO * SingleFileInfoSize
+                    );
+  if (FileInfoArray == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto FreeDirentBuf;
+  }
+
+  //
+  // Pick up reading the directory stream where the previous cache ended.
+  //
+  DirStreamCookie   = VirtioFsFile->FilePosition;
+  CacheEndsAtCookie = VirtioFsFile->FilePosition;
+  NumFileInfo       = 0;
+  do {
+    UINT32 Remaining;
+    UINT32 Consumed;
+
+    //
+    // Fetch a chunk of the directory stream. The chunk may hold more entries
+    // than what we can fit in the cache. The chunk may also not entirely fill
+    // the cache, especially after filtering out entries that cannot be
+    // supported under UEFI (sockets, FIFOs, filenames with backslashes, etc).
+    //
+    Remaining = DirentBufSize;
+    Status = VirtioFsFuseReadFileOrDir (
+               VirtioFs,
+               VirtioFsFile->NodeId,
+               VirtioFsFile->FuseHandle,
+               TRUE,                     // IsDir
+               DirStreamCookie,          // Offset
+               &Remaining,               // Size
+               DirentBuf                 // Data
+               );
+    if (EFI_ERROR (Status)) {
+      goto FreeFileInfoArray;
+    }
+
+    if (Remaining == 0) {
+      //
+      // The directory stream ends.
+      //
+      break;
+    }
+
+    //
+    // Iterate over all records in DirentBuf. Primarily, forget them all.
+    // Secondarily, if a record proves transformable to EFI_FILE_INFO, add it
+    // to the EFI_FILE_INFO cache (unless the cache is full).
+    //
+    Consumed = 0;
+    while (Remaining >= sizeof (VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE)) {
+      VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE *Dirent;
+      UINT32                             DirentSize;
+
+      Dirent = (VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE *)(DirentBuf + Consumed);
+      DirentSize = (UINT32)VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE (
+                             Dirent->Namelen);
+      if (DirentSize == 0) {
+        //
+        // This means one of two things: (a) Dirent->Namelen is zero, or (b)
+        // (b) Dirent->Namelen is unsupportably large. (a) is just invalid for
+        // the Virtio Filesystem device to send, while (b) shouldn't happen
+        // because "FilesysAttr.Namelen" -- the maximum filename length
+        // supported by the filesystem -- proved acceptable above.
+        //
+        Status = EFI_PROTOCOL_ERROR;
+        goto FreeFileInfoArray;
+      }
+      if (DirentSize > Remaining) {
+        //
+        // Dirent->Namelen suggests that the filename byte array (plus any
+        // padding) are truncated. This should never happen; the Virtio
+        // Filesystem device is supposed to send complete entries only.
+        //
+        Status = EFI_PROTOCOL_ERROR;
+        goto FreeFileInfoArray;
+      }
+      if (Dirent->Namelen > FilesysAttr.Namelen) {
+        //
+        // This is possible without tripping the truncation check above, due to
+        // how entries are padded. The condition means that Dirent->Namelen is
+        // reportedly larger than the filesystem limit, without spilling into
+        // the next alignment bucket. Should never happen.
+        //
+        Status = EFI_PROTOCOL_ERROR;
+        goto FreeFileInfoArray;
+      }
+
+      //
+      // If we haven't filled the EFI_FILE_INFO cache yet, attempt transforming
+      // Dirent to EFI_FILE_INFO.
+      //
+      if (NumFileInfo < VIRTIO_FS_FILE_MAX_FILE_INFO) {
+        EFI_FILE_INFO *FileInfo;
+
+        FileInfo = (EFI_FILE_INFO *)(FileInfoArray +
+                                     (NumFileInfo * SingleFileInfoSize));
+        Status = PopulateFileInfo (Dirent, SingleFileInfoSize, FileInfo);
+        if (!EFI_ERROR (Status)) {
+          //
+          // Dirent has been transformed and cached successfully.
+          //
+          NumFileInfo++;
+          //
+          // The next time we refill the cache, restart reading the directory
+          // stream right after the entry that we've just transformed and
+          // cached.
+          //
+          CacheEndsAtCookie = Dirent->CookieForNextEntry;
+        }
+        //
+        // If Dirent wasn't transformable to an EFI_FILE_INFO, we'll just skip
+        // it.
+        //
+      }
+
+      //
+      // Make the Virtio Filesystem device forget the NodeId in this directory
+      // entry, as we'll need it no more. (The "." and ".." entries need no
+      // FUSE_FORGET requests, when returned by FUSE_READDIRPLUS -- and so the
+      // Virtio Filesystem device reports their NodeId fields as zero.)
+      //
+      if (Dirent->NodeResp.NodeId != 0) {
+        VirtioFsFuseForget (VirtioFs, Dirent->NodeResp.NodeId);
+      }
+
+      //
+      // Advance to the next entry in DirentBuf.
+      //
+      DirStreamCookie = Dirent->CookieForNextEntry;
+      Consumed += DirentSize;
+      Remaining -= DirentSize;
+    }
+
+    if (Remaining > 0) {
+      //
+      // This suggests that a VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE header was
+      // truncated. This should never happen; the Virtio Filesystem device is
+      // supposed to send complete entries only.
+      //
+      Status = EFI_PROTOCOL_ERROR;
+      goto FreeFileInfoArray;
+    }
+    //
+    // Fetch another DirentBuf from the directory stream, unless we've filled
+    // the EFI_FILE_INFO cache.
+    //
+  } while (NumFileInfo < VIRTIO_FS_FILE_MAX_FILE_INFO);
+
+  //
+  // Commit the results. (Note that the result may be an empty cache.)
+  //
+  if (VirtioFsFile->FileInfoArray != NULL) {
+    FreePool (VirtioFsFile->FileInfoArray);
+  }
+  VirtioFsFile->FileInfoArray      = FileInfoArray;
+  VirtioFsFile->SingleFileInfoSize = SingleFileInfoSize;
+  VirtioFsFile->NumFileInfo        = NumFileInfo;
+  VirtioFsFile->NextFileInfo       = 0;
+  VirtioFsFile->FilePosition       = CacheEndsAtCookie;
+
+  FreePool (DirentBuf);
+  return EFI_SUCCESS;
+
+FreeFileInfoArray:
+  FreePool (FileInfoArray);
+
+FreeDirentBuf:
+  FreePool (DirentBuf);
+
+  return Status;
+}
+
+/**
+  Read an entry from the EFI_FILE_INFO cache.
+**/
+STATIC
+EFI_STATUS
+ReadFileInfoCache (
+  IN OUT VIRTIO_FS_FILE *VirtioFsFile,
+  IN OUT UINTN          *BufferSize,
+     OUT VOID           *Buffer
+  )
+{
+  EFI_FILE_INFO *FileInfo;
+  UINTN         CallerAllocated;
+
+  //
+  // Refill the cache if needed. If the refill doesn't produce any new
+  // EFI_FILE_INFO, report End of Directory, by setting (*BufferSize) to 0.
+  //
+  if (VirtioFsFile->NextFileInfo == VirtioFsFile->NumFileInfo) {
+    EFI_STATUS Status;
+
+    Status = RefillFileInfoCache (VirtioFsFile);
+    if (EFI_ERROR (Status)) {
+      return (Status == EFI_BUFFER_TOO_SMALL) ? EFI_DEVICE_ERROR : Status;
+    }
+    if (VirtioFsFile->NumFileInfo == 0) {
+      *BufferSize = 0;
+      return EFI_SUCCESS;
+    }
+  }
+  FileInfo = (EFI_FILE_INFO *)(VirtioFsFile->FileInfoArray +
+                               (VirtioFsFile->NextFileInfo *
+                                VirtioFsFile->SingleFileInfoSize));
+
+  //
+  // Check if the caller is ready to accept FileInfo. If not, we'll just
+  // present the required size for now.
+  //
+  // (The (UINTN) cast below is safe because FileInfo->Size has been reduced
+  // from VirtioFsFile->SingleFileInfoSize, in
+  //
+  //   RefillFileInfoCache()
+  //     PopulateFileInfo()
+  //       VirtioFsFuseDirentPlusToEfiFileInfo()
+  //
+  // and VirtioFsFile->SingleFileInfoSize was computed from
+  // FilesysAttr.Namelen, which had been accepted by
+  // VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE().)
+  //
+  CallerAllocated = *BufferSize;
+  *BufferSize = (UINTN)FileInfo->Size;
+  if (CallerAllocated < *BufferSize) {
+    return EFI_BUFFER_TOO_SMALL;
+  }
+  //
+  // Output FileInfo, and remove it from the cache.
+  //
+  CopyMem (Buffer, FileInfo, *BufferSize);
+  VirtioFsFile->NextFileInfo++;
+  return EFI_SUCCESS;
+}
+
 /**
   Read from a regular file.
 **/
 STATIC
 EFI_STATUS
 ReadRegularFile (
   IN OUT VIRTIO_FS_FILE *VirtioFsFile,
   IN OUT UINTN          *BufferSize,
@@ -80,14 +421,14 @@ VirtioFsSimpleFileRead (
   )
 {
   VIRTIO_FS_FILE *VirtioFsFile;
   EFI_STATUS     Status;
 
   VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
 
   if (VirtioFsFile->IsDirectory) {
-    Status = EFI_NO_MEDIA;
+    Status = ReadFileInfoCache (VirtioFsFile, BufferSize, Buffer);
   } else {
     Status = ReadRegularFile (VirtioFsFile, BufferSize, Buffer);
   }
   return Status;
 }
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 37/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Flush()
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (35 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 36/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Read() for directories Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 38/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_WRITE Laszlo Ersek
                   ` (11 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

For directories, implement EFI_FILE_PROTOCOL.Flush() by sending the
FUSE_FSYNCDIR command to the Virtio Filesystem device.

For regular files, send FUSE_FLUSH, followed by FUSE_FSYNC.

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>
---
 OvmfPkg/VirtioFsDxe/SimpleFsFlush.c | 26 +++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsFlush.c b/OvmfPkg/VirtioFsDxe/SimpleFsFlush.c
index e48d92140f64..ba4a611e5a21 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsFlush.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsFlush.c
@@ -9,10 +9,34 @@
 #include "VirtioFsDxe.h"
 
 EFI_STATUS
 EFIAPI
 VirtioFsSimpleFileFlush (
   IN EFI_FILE_PROTOCOL *This
   )
 {
-  return EFI_NO_MEDIA;
+  VIRTIO_FS_FILE *VirtioFsFile;
+  VIRTIO_FS      *VirtioFs;
+  EFI_STATUS     Status;
+
+  VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
+  VirtioFs     = VirtioFsFile->OwnerFs;
+
+  if (!VirtioFsFile->IsOpenForWriting) {
+    return EFI_ACCESS_DENIED;
+  }
+
+  //
+  // FUSE_FLUSH is for regular files only.
+  //
+  if (!VirtioFsFile->IsDirectory) {
+    Status = VirtioFsFuseFlush (VirtioFs, VirtioFsFile->NodeId,
+               VirtioFsFile->FuseHandle);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  Status = VirtioFsFuseFsyncFileOrDir (VirtioFs, VirtioFsFile->NodeId,
+             VirtioFsFile->FuseHandle, VirtioFsFile->IsDirectory);
+  return Status;
 }
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 38/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_WRITE
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (36 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 37/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Flush() Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 39/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Write() Laszlo Ersek
                   ` (10 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  19 +++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |  11 ++
 OvmfPkg/VirtioFsDxe/FuseInit.c              |  12 +-
 OvmfPkg/VirtioFsDxe/FuseWrite.c             | 155 ++++++++++++++++++++
 5 files changed, 196 insertions(+), 2 deletions(-)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index fa8c40019f4e..0b7b3ff80edd 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -148,16 +148,17 @@ typedef enum {
   VirtioFsFuseOpLookup      =  1,
   VirtioFsFuseOpForget      =  2,
   VirtioFsFuseOpGetAttr     =  3,
   VirtioFsFuseOpMkDir       =  9,
   VirtioFsFuseOpUnlink      = 10,
   VirtioFsFuseOpRmDir       = 11,
   VirtioFsFuseOpOpen        = 14,
   VirtioFsFuseOpRead        = 15,
+  VirtioFsFuseOpWrite       = 16,
   VirtioFsFuseOpStatFs      = 17,
   VirtioFsFuseOpRelease     = 18,
   VirtioFsFuseOpFsync       = 20,
   VirtioFsFuseOpFlush       = 25,
   VirtioFsFuseOpInit        = 26,
   VirtioFsFuseOpOpenDir     = 27,
   VirtioFsFuseOpReleaseDir  = 29,
   VirtioFsFuseOpFsyncDir    = 30,
@@ -277,16 +278,34 @@ typedef struct {
   UINT64 Offset;
   UINT32 Size;
   UINT32 ReadFlags;
   UINT64 LockOwner;
   UINT32 Flags;
   UINT32 Padding;
 } VIRTIO_FS_FUSE_READ_REQUEST;
 
+//
+// Headers for VirtioFsFuseOpWrite.
+//
+typedef struct {
+  UINT64 FileHandle;
+  UINT64 Offset;
+  UINT32 Size;
+  UINT32 WriteFlags;
+  UINT64 LockOwner;
+  UINT32 Flags;
+  UINT32 Padding;
+} VIRTIO_FS_FUSE_WRITE_REQUEST;
+
+typedef struct {
+  UINT32 Size;
+  UINT32 Padding;
+} VIRTIO_FS_FUSE_WRITE_RESPONSE;
+
 //
 // Header for VirtioFsFuseOpStatFs.
 //
 typedef struct {
   UINT64 Blocks;
   UINT64 Bfree;
   UINT64 Bavail;
   UINT64 Files;
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index 39c77c7c4ee9..2c145da5f5ae 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -92,16 +92,17 @@ [Sources]
   FuseMkDir.c
   FuseOpen.c
   FuseOpenDir.c
   FuseOpenOrCreate.c
   FuseRead.c
   FuseRelease.c
   FuseStatFs.c
   FuseUnlink.c
+  FuseWrite.c
   Helpers.c
   SimpleFsClose.c
   SimpleFsDelete.c
   SimpleFsFlush.c
   SimpleFsGetInfo.c
   SimpleFsGetPosition.c
   SimpleFsOpen.c
   SimpleFsOpenVolume.c
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index a704acdd520e..132a63400527 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -73,16 +73,17 @@ typedef struct {
   //                              -----------   ------------------  ----------
   UINT64                          Signature; // DriverBindingStart  0
   VIRTIO_DEVICE_PROTOCOL          *Virtio;   // DriverBindingStart  0
   VIRTIO_FS_LABEL                 Label;     // VirtioFsInit        1
   UINT16                          QueueSize; // VirtioFsInit        1
   VRING                           Ring;      // VirtioRingInit      2
   VOID                            *RingMap;  // VirtioRingMap       2
   UINT64                          RequestId; // FuseInitSession     1
+  UINT32                          MaxWrite;  // FuseInitSession     1
   EFI_EVENT                       ExitBoot;  // DriverBindingStart  0
   LIST_ENTRY                      OpenFiles; // DriverBindingStart  0
   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL SimpleFs;  // DriverBindingStart  0
 } VIRTIO_FS;
 
 #define VIRTIO_FS_FROM_SIMPLE_FS(SimpleFsReference) \
   CR (SimpleFsReference, VIRTIO_FS, SimpleFs, VIRTIO_FS_SIG);
 
@@ -329,16 +330,26 @@ VirtioFsFuseReadFileOrDir (
   IN     UINT64    NodeId,
   IN     UINT64    FuseHandle,
   IN     BOOLEAN   IsDir,
   IN     UINT64    Offset,
   IN OUT UINT32    *Size,
      OUT VOID      *Data
   );
 
+EFI_STATUS
+VirtioFsFuseWrite (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId,
+  IN     UINT64    FuseHandle,
+  IN     UINT64    Offset,
+  IN OUT UINT32    *Size,
+  IN     VOID      *Data
+  );
+
 EFI_STATUS
 VirtioFsFuseStatFs (
   IN OUT VIRTIO_FS                      *VirtioFs,
   IN     UINT64                         NodeId,
      OUT VIRTIO_FS_FUSE_STATFS_RESPONSE *FilesysAttr
      );
 
 EFI_STATUS
diff --git a/OvmfPkg/VirtioFsDxe/FuseInit.c b/OvmfPkg/VirtioFsDxe/FuseInit.c
index 7aa6ee75caf6..431842804912 100644
--- a/OvmfPkg/VirtioFsDxe/FuseInit.c
+++ b/OvmfPkg/VirtioFsDxe/FuseInit.c
@@ -17,17 +17,20 @@
   session is started by sending a FUSE_INIT request as defined by the FUSE
   protocol on one request virtqueue."
 
   The function may only be called after VirtioFsInit() returns successfully and
   before VirtioFsUninit() is called.
 
   @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_INIT
                            request to. The FUSE request counter
-                           "VirtioFs->RequestId" is set to 1 on output.
+                           "VirtioFs->RequestId" is set to 1 on output. The
+                           maximum write buffer size exposed in the FUSE_INIT
+                           response is saved in "VirtioFs->MaxWrite", on
+                           output.
 
   @retval EFI_SUCCESS      The FUSE session has been started.
 
   @retval EFI_UNSUPPORTED  FUSE interface version or feature negotiation
                            failed.
 
   @return                  The "errno" value mapped to an EFI_STATUS code, if
                            the Virtio Filesystem device explicitly reported an
@@ -121,14 +124,19 @@ VirtioFsFuseInitSession (
     return Status;
   }
 
   //
   // Check FUSE interface version / feature compatibility.
   //
   if (InitResp.Major < InitReq.Major ||
       (InitResp.Major == InitReq.Major && InitResp.Minor < InitReq.Minor) ||
-      (InitResp.Flags & VIRTIO_FS_FUSE_INIT_REQ_F_DO_READDIRPLUS) == 0) {
+      (InitResp.Flags & VIRTIO_FS_FUSE_INIT_REQ_F_DO_READDIRPLUS) == 0 ||
+      InitResp.MaxWrite < SIZE_4KB) {
     return EFI_UNSUPPORTED;
   }
 
+  //
+  // Save the maximum write buffer size for FUSE_WRITE requests.
+  //
+  VirtioFs->MaxWrite = InitResp.MaxWrite;
   return EFI_SUCCESS;
 }
diff --git a/OvmfPkg/VirtioFsDxe/FuseWrite.c b/OvmfPkg/VirtioFsDxe/FuseWrite.c
new file mode 100644
index 000000000000..cc552bd667ba
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/FuseWrite.c
@@ -0,0 +1,155 @@
+/** @file
+  FUSE_WRITE wrapper for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+/**
+  Write a chunk to a regular file, by sending the FUSE_WRITE request to the
+  Virtio Filesystem device.
+
+  The function may only be called after VirtioFsFuseInitSession() returns
+  successfully and before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_WRITE
+                           request to. On output, the FUSE request counter
+                           "VirtioFs->RequestId" will have been incremented.
+
+  @param[in] NodeId        The inode number of the regular file to write to.
+
+  @param[in] FuseHandle    The open handle to the regular file to write to.
+
+  @param[in] Offset        The absolute file position at which to start
+                           writing.
+
+  @param[in,out] Size      On input, the number of bytes to write. On
+                           successful return, the number of bytes actually
+                           written, which may be smaller than the value on
+                           input.
+
+  @param[in] Data          The buffer to write to the regular file.
+
+  @retval EFI_SUCCESS          Write successful. The caller is responsible for
+                               checking Size to learn the actual byte count
+                               transferred.
+
+  @retval EFI_BAD_BUFFER_SIZE  On input, Size is larger than
+                               "VirtioFs->MaxWrite".
+
+  @return                      The "errno" value mapped to an EFI_STATUS code,
+                               if the Virtio Filesystem device explicitly
+                               reported an error.
+
+  @return                      Error codes propagated from
+                               VirtioFsSgListsValidate(),
+                               VirtioFsFuseNewRequest(),
+                               VirtioFsSgListsSubmit(),
+                               VirtioFsFuseCheckResponse().
+**/
+EFI_STATUS
+VirtioFsFuseWrite (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId,
+  IN     UINT64    FuseHandle,
+  IN     UINT64    Offset,
+  IN OUT UINT32    *Size,
+  IN     VOID      *Data
+  )
+{
+  VIRTIO_FS_FUSE_REQUEST        CommonReq;
+  VIRTIO_FS_FUSE_WRITE_REQUEST  WriteReq;
+  VIRTIO_FS_IO_VECTOR           ReqIoVec[3];
+  VIRTIO_FS_SCATTER_GATHER_LIST ReqSgList;
+  VIRTIO_FS_FUSE_RESPONSE       CommonResp;
+  VIRTIO_FS_FUSE_WRITE_RESPONSE WriteResp;
+  VIRTIO_FS_IO_VECTOR           RespIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST RespSgList;
+  EFI_STATUS                    Status;
+
+  //
+  // Honor the write buffer size limit of the Virtio Filesystem device.
+  //
+  if (*Size > VirtioFs->MaxWrite) {
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  //
+  // Set up the scatter-gather lists.
+  //
+  ReqIoVec[0].Buffer = &CommonReq;
+  ReqIoVec[0].Size   = sizeof CommonReq;
+  ReqIoVec[1].Buffer = &WriteReq;
+  ReqIoVec[1].Size   = sizeof WriteReq;
+  ReqIoVec[2].Buffer = Data;
+  ReqIoVec[2].Size   = *Size;
+  ReqSgList.IoVec    = ReqIoVec;
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);
+
+  RespIoVec[0].Buffer = &CommonResp;
+  RespIoVec[0].Size   = sizeof CommonResp;
+  RespIoVec[1].Buffer = &WriteResp;
+  RespIoVec[1].Size   = sizeof WriteResp;
+  RespSgList.IoVec    = RespIoVec;
+  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);
+
+  //
+  // Validate the scatter-gather lists; calculate the total transfer sizes.
+  //
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the common request header.
+  //
+  Status = VirtioFsFuseNewRequest (VirtioFs, &CommonReq, ReqSgList.TotalSize,
+             VirtioFsFuseOpWrite, NodeId);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the FUSE_WRITE-specific fields.
+  //
+  WriteReq.FileHandle = FuseHandle;
+  WriteReq.Offset     = Offset;
+  WriteReq.Size       = *Size;
+  WriteReq.WriteFlags = 0;
+  WriteReq.LockOwner  = 0;
+  WriteReq.Flags      = 0;
+  WriteReq.Padding    = 0;
+
+  //
+  // Submit the request.
+  //
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Verify the response (all response buffers are fixed size).
+  //
+  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);
+  if (EFI_ERROR (Status)) {
+    if (Status == EFI_DEVICE_ERROR) {
+      DEBUG ((DEBUG_ERROR, "%a: Label=\"%s\" NodeId=%Lu FuseHandle=%Lu "
+        "Offset=0x%Lx Size=0x%x Data@%p Errno=%d\n", __FUNCTION__,
+        VirtioFs->Label, NodeId, FuseHandle, Offset, *Size, Data,
+        CommonResp.Error));
+      Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
+    }
+    return Status;
+  }
+
+  //
+  // Report the actual transfer size.
+  //
+  *Size = WriteResp.Size;
+  return EFI_SUCCESS;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 39/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Write()
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (37 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 38/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_WRITE Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 40/48] OvmfPkg/VirtioFsDxe: handle the volume label in EFI_FILE_PROTOCOL.SetInfo Laszlo Ersek
                   ` (9 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Using the functions introduced previously, we can now implement
VirtioFsSimpleFileWrite().

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>
---
 OvmfPkg/VirtioFsDxe/SimpleFsWrite.c | 63 +++++++++++++++++++-
 1 file changed, 62 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsWrite.c b/OvmfPkg/VirtioFsDxe/SimpleFsWrite.c
index 90d82bd722b1..8ae317c88e43 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsWrite.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsWrite.c
@@ -11,10 +11,71 @@
 EFI_STATUS
 EFIAPI
 VirtioFsSimpleFileWrite (
   IN     EFI_FILE_PROTOCOL *This,
   IN OUT UINTN             *BufferSize,
   IN     VOID              *Buffer
   )
 {
-  return EFI_NO_MEDIA;
+  VIRTIO_FS_FILE *VirtioFsFile;
+  VIRTIO_FS      *VirtioFs;
+  EFI_STATUS     Status;
+  UINTN          Transferred;
+  UINTN          Left;
+
+  VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
+  VirtioFs     = VirtioFsFile->OwnerFs;
+
+  if (VirtioFsFile->IsDirectory) {
+    return EFI_UNSUPPORTED;
+  }
+  if (!VirtioFsFile->IsOpenForWriting) {
+    return EFI_ACCESS_DENIED;
+  }
+
+  Status      = EFI_SUCCESS;
+  Transferred = 0;
+  Left        = *BufferSize;
+  while (Left > 0) {
+    UINT32 WriteSize;
+
+    //
+    // Honor the write buffer size limit.
+    //
+    WriteSize = (UINT32)MIN ((UINTN)VirtioFs->MaxWrite, Left);
+    Status = VirtioFsFuseWrite (
+               VirtioFs,
+               VirtioFsFile->NodeId,
+               VirtioFsFile->FuseHandle,
+               VirtioFsFile->FilePosition + Transferred,
+               &WriteSize,
+               (UINT8 *)Buffer + Transferred
+               );
+    if (!EFI_ERROR (Status) && WriteSize == 0) {
+      //
+      // Progress should have been made.
+      //
+      Status = EFI_DEVICE_ERROR;
+    }
+    if (EFI_ERROR (Status)) {
+      break;
+    }
+    Transferred += WriteSize;
+    Left        -= WriteSize;
+  }
+
+  *BufferSize = Transferred;
+  VirtioFsFile->FilePosition += Transferred;
+  //
+  // According to the UEFI spec,
+  //
+  // - 'Partial writes only occur when there has been a data error during the
+  //    write attempt (such as "file space full")', and
+  //
+  // - (as an example) EFI_VOLUME_FULL is returned when 'The volume is full'.
+  //
+  // These together imply that after a partial write, we have to return an
+  // error. In other words, (Transferred > 0) is inconsequential for the return
+  // value.
+  //
+  return Status;
 }
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 40/48] OvmfPkg/VirtioFsDxe: handle the volume label in EFI_FILE_PROTOCOL.SetInfo
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (38 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 39/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Write() Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 41/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_RENAME2 Laszlo Ersek
                   ` (8 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

The least complicated third of EFI_FILE_PROTOCOL.SetInfo() is to handle
the EFI_FILE_SYSTEM_INFO and EFI_FILE_SYSTEM_VOLUME_LABEL setting
requests. Both of those can only change the volume label -- which the
Virtio Filesystem device does not support.

Verify the input for well-formedness, and report success only if the
volume label is being set to its current value.

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>
---
 OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c | 226 +++++++++++++++++++-
 1 file changed, 225 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c b/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c
index 200b7a1bcd20..895b5c029a9e 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c
@@ -1,21 +1,245 @@
 /** @file
   EFI_FILE_PROTOCOL.SetInfo() member function for the Virtio Filesystem driver.
 
   Copyright (C) 2020, Red Hat, Inc.
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
+#include <Guid/FileSystemInfo.h>            // gEfiFileSystemInfoGuid
+#include <Guid/FileSystemVolumeLabelInfo.h> // gEfiFileSystemVolumeLabelInfo...
+#include <Library/BaseLib.h>                // StrCmp()
+#include <Library/BaseMemoryLib.h>          // CompareGuid()
+
 #include "VirtioFsDxe.h"
 
+/**
+  Validate a buffer that the EFI_FILE_PROTOCOL.SetInfo() caller passes in for a
+  particular InformationType GUID.
+
+  The structure to be validated is supposed to end with a variable-length,
+  NUL-terminated CHAR16 Name string.
+
+  @param[in] SizeByProtocolCaller  The BufferSize parameter as provided by the
+                                   EFI_FILE_PROTOCOL.SetInfo() caller.
+
+  @param[in] MinimumStructSize     The minimum structure size that is required
+                                   for the given InformationType GUID,
+                                   including a single CHAR16 element from the
+                                   trailing Name field.
+
+  @param[in] IsSizeByInfoPresent   TRUE if and only if the expected structure
+                                   starts with a UINT64 Size field that reports
+                                   the actual structure size.
+
+  @param[in] Buffer                The Buffer parameter as provided by the
+                                   EFI_FILE_PROTOCOL.SetInfo() caller.
+
+  @retval EFI_SUCCESS            Validation successful, Buffer is well-formed.
+
+  @retval EFI_BAD_BUFFER_SIZE    The EFI_FILE_PROTOCOL.SetInfo()
+                                 caller provided a BufferSize that is smaller
+                                 than the minimum structure size required for
+                                 the given InformationType GUID.
+
+  @retval EFI_INVALID_PARAMETER  IsSizeByInfoPresent is TRUE, and the leading
+                                 UINT64 Size field does not match the
+                                 EFI_FILE_PROTOCOL.SetInfo() caller-provided
+                                 BufferSize.
+
+  @retval EFI_INVALID_PARAMETER  The trailing Name field does not consist of a
+                                 whole multiple of CHAR16 elements.
+
+  @retval EFI_INVALID_PARAMETER  The trailing Name field is not NUL-terminated.
+**/
+STATIC
+EFI_STATUS
+ValidateInfoStructure (
+  IN UINTN   SizeByProtocolCaller,
+  IN UINTN   MinimumStructSize,
+  IN BOOLEAN IsSizeByInfoPresent,
+  IN VOID    *Buffer
+  )
+{
+  UINTN  NameFieldByteOffset;
+  UINTN  NameFieldBytes;
+  UINTN  NameFieldChar16s;
+  CHAR16 *NameField;
+
+  //
+  // Make sure the internal function asking for validation passes in sane
+  // values.
+  //
+  ASSERT (MinimumStructSize >= sizeof (CHAR16));
+  NameFieldByteOffset = MinimumStructSize - sizeof (CHAR16);
+
+  if (IsSizeByInfoPresent) {
+    ASSERT (MinimumStructSize >= sizeof (UINT64) + sizeof (CHAR16));
+    ASSERT (NameFieldByteOffset >= sizeof (UINT64));
+  }
+
+  //
+  // Check whether the protocol caller provided enough bytes for the minimum
+  // size of this info structure.
+  //
+  if (SizeByProtocolCaller < MinimumStructSize) {
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  //
+  // If the info structure starts with a UINT64 Size field, check if that
+  // agrees with the protocol caller-provided size.
+  //
+  if (IsSizeByInfoPresent) {
+    UINT64 *SizeByInfo;
+
+    SizeByInfo = Buffer;
+    if (*SizeByInfo != SizeByProtocolCaller) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  //
+  // The CHAR16 Name field at the end of the structure must have an even number
+  // of bytes.
+  //
+  // The subtraction below cannot underflow, and yields at least
+  // sizeof(CHAR16).
+  //
+  ASSERT (SizeByProtocolCaller >= NameFieldByteOffset);
+  NameFieldBytes = SizeByProtocolCaller - NameFieldByteOffset;
+  ASSERT (NameFieldBytes >= sizeof (CHAR16));
+  if (NameFieldBytes % sizeof (CHAR16) != 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // The CHAR16 Name field at the end of the structure must be NUL-terminated.
+  //
+  NameFieldChar16s = NameFieldBytes / sizeof (CHAR16);
+  ASSERT (NameFieldChar16s >= 1);
+
+  NameField = (CHAR16 *)((UINT8 *)Buffer + NameFieldByteOffset);
+  if (NameField[NameFieldChar16s - 1] != L'\0') {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Process an EFI_FILE_SYSTEM_INFO setting request.
+**/
+STATIC
+EFI_STATUS
+SetFileSystemInfo (
+  IN EFI_FILE_PROTOCOL *This,
+  IN UINTN             BufferSize,
+  IN VOID              *Buffer
+  )
+{
+  VIRTIO_FS_FILE       *VirtioFsFile;
+  VIRTIO_FS            *VirtioFs;
+  EFI_STATUS           Status;
+  EFI_FILE_SYSTEM_INFO *FileSystemInfo;
+
+  VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
+  VirtioFs     = VirtioFsFile->OwnerFs;
+
+  //
+  // Validate if Buffer passes as EFI_FILE_SYSTEM_INFO.
+  //
+  Status = ValidateInfoStructure (
+             BufferSize,                       // SizeByProtocolCaller
+             OFFSET_OF (EFI_FILE_SYSTEM_INFO,
+               VolumeLabel) + sizeof (CHAR16), // MinimumStructSize
+             TRUE,                             // IsSizeByInfoPresent
+             Buffer
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  FileSystemInfo = Buffer;
+
+  //
+  // EFI_FILE_SYSTEM_INFO fields other than VolumeLabel cannot be changed, per
+  // spec.
+  //
+  // If the label is being changed to its current value, report success;
+  // otherwise, reject the request, as the Virtio Filesystem device does not
+  // support changing the label.
+  //
+  if (StrCmp (FileSystemInfo->VolumeLabel, VirtioFs->Label) == 0) {
+    return EFI_SUCCESS;
+  }
+  return EFI_WRITE_PROTECTED;
+}
+
+/**
+  Process an EFI_FILE_SYSTEM_VOLUME_LABEL setting request.
+**/
+STATIC
+EFI_STATUS
+SetFileSystemVolumeLabelInfo (
+  IN EFI_FILE_PROTOCOL *This,
+  IN UINTN             BufferSize,
+  IN VOID              *Buffer
+  )
+{
+  VIRTIO_FS_FILE               *VirtioFsFile;
+  VIRTIO_FS                    *VirtioFs;
+  EFI_STATUS                   Status;
+  EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel;
+
+  VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
+  VirtioFs     = VirtioFsFile->OwnerFs;
+
+  //
+  // Validate if Buffer passes as EFI_FILE_SYSTEM_VOLUME_LABEL.
+  //
+  Status = ValidateInfoStructure (
+             BufferSize,                              // SizeByProtocolCaller
+             OFFSET_OF (EFI_FILE_SYSTEM_VOLUME_LABEL,
+               VolumeLabel) + sizeof (CHAR16),        // MinimumStructSize
+             FALSE,                                   // IsSizeByInfoPresent
+             Buffer
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  FileSystemVolumeLabel = Buffer;
+
+  //
+  // If the label is being changed to its current value, report success;
+  // otherwise, reject the request, as the Virtio Filesystem device does not
+  // support changing the label.
+  //
+  if (StrCmp (FileSystemVolumeLabel->VolumeLabel, VirtioFs->Label) == 0) {
+    return EFI_SUCCESS;
+  }
+  return EFI_WRITE_PROTECTED;
+}
+
 EFI_STATUS
 EFIAPI
 VirtioFsSimpleFileSetInfo (
   IN EFI_FILE_PROTOCOL *This,
   IN EFI_GUID          *InformationType,
   IN UINTN             BufferSize,
   IN VOID              *Buffer
   )
 {
-  return EFI_NO_MEDIA;
+  if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
+    return EFI_UNSUPPORTED;
+  }
+
+  if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
+    return SetFileSystemInfo (This, BufferSize, Buffer);
+  }
+
+  if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
+    return SetFileSystemVolumeLabelInfo (This, BufferSize, Buffer);
+  }
+
+  return EFI_UNSUPPORTED;
 }
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 41/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_RENAME2
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (39 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 40/48] OvmfPkg/VirtioFsDxe: handle the volume label in EFI_FILE_PROTOCOL.SetInfo Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 42/48] OvmfPkg/VirtioFsDxe: add helper for composing rename/move destination path Laszlo Ersek
                   ` (7 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Add the VirtioFsFuseRename() function, for sending the FUSE_RENAME2
command to the Virtio Filesystem device.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  15 +++
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |   9 ++
 OvmfPkg/VirtioFsDxe/FuseRename.c            | 131 ++++++++++++++++++++
 4 files changed, 156 insertions(+)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index 0b7b3ff80edd..0b2ed7010046 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -136,16 +136,21 @@ typedef struct {
   ((Namelen) == 0 || (Namelen) > SIZE_4KB ?                          \
    (UINTN)0 :                                                        \
    ALIGN_VALUE (                                                     \
      sizeof (VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE) + (UINTN)(Namelen), \
      sizeof (UINT64)                                                 \
      )                                                               \
    )
 
+//
+// Flags for VirtioFsFuseOpRename2.
+//
+#define VIRTIO_FS_FUSE_RENAME2_REQ_F_NOREPLACE BIT0
+
 //
 // FUSE operation codes.
 //
 typedef enum {
   VirtioFsFuseOpLookup      =  1,
   VirtioFsFuseOpForget      =  2,
   VirtioFsFuseOpGetAttr     =  3,
   VirtioFsFuseOpMkDir       =  9,
@@ -159,16 +164,17 @@ typedef enum {
   VirtioFsFuseOpFsync       = 20,
   VirtioFsFuseOpFlush       = 25,
   VirtioFsFuseOpInit        = 26,
   VirtioFsFuseOpOpenDir     = 27,
   VirtioFsFuseOpReleaseDir  = 29,
   VirtioFsFuseOpFsyncDir    = 30,
   VirtioFsFuseOpCreate      = 35,
   VirtioFsFuseOpReadDirPlus = 44,
+  VirtioFsFuseOpRename2     = 45,
 } VIRTIO_FS_FUSE_OPCODE;
 
 #pragma pack (1)
 //
 // Request-response headers common to all request types.
 //
 typedef struct {
   UINT32 Len;
@@ -393,11 +399,20 @@ typedef struct {
 typedef struct {
   VIRTIO_FS_FUSE_NODE_RESPONSE       NodeResp;
   VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE AttrResp;
   UINT64                             NodeId;
   UINT64                             CookieForNextEntry;
   UINT32                             Namelen;
   UINT32                             Type;
 } VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE;
+
+//
+// Header for VirtioFsFuseOpRename2.
+//
+typedef struct {
+  UINT64 NewDir;
+  UINT32 Flags;
+  UINT32 Padding;
+} VIRTIO_FS_FUSE_RENAME2_REQUEST;
 #pragma pack ()
 
 #endif // VIRTIO_FS_H_
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index 2c145da5f5ae..9dccd7d6a9ef 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -90,16 +90,17 @@ [Sources]
   FuseInit.c
   FuseLookup.c
   FuseMkDir.c
   FuseOpen.c
   FuseOpenDir.c
   FuseOpenOrCreate.c
   FuseRead.c
   FuseRelease.c
+  FuseRename.c
   FuseStatFs.c
   FuseUnlink.c
   FuseWrite.c
   Helpers.c
   SimpleFsClose.c
   SimpleFsDelete.c
   SimpleFsFlush.c
   SimpleFsGetInfo.c
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 132a63400527..9334e5434c51 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -391,16 +391,25 @@ EFI_STATUS
 VirtioFsFuseOpenOrCreate (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    ParentNodeId,
   IN     CHAR8     *Name,
      OUT UINT64    *NodeId,
      OUT UINT64    *FuseHandle
   );
 
+EFI_STATUS
+VirtioFsFuseRename (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    OldParentNodeId,
+  IN     CHAR8     *OldName,
+  IN     UINT64    NewParentNodeId,
+  IN     CHAR8     *NewName
+  );
+
 //
 // EFI_SIMPLE_FILE_SYSTEM_PROTOCOL member functions for the Virtio Filesystem
 // driver.
 //
 
 EFI_STATUS
 EFIAPI
 VirtioFsOpenVolume (
diff --git a/OvmfPkg/VirtioFsDxe/FuseRename.c b/OvmfPkg/VirtioFsDxe/FuseRename.c
new file mode 100644
index 000000000000..fc9b27ccf6d4
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/FuseRename.c
@@ -0,0 +1,131 @@
+/** @file
+  FUSE_RENAME2 wrapper for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h> // AsciiStrSize()
+
+#include "VirtioFsDxe.h"
+
+/**
+  Rename a regular file or a directory, by sending the FUSE_RENAME2 request to
+  the Virtio Filesystem device. If the new filename exists, the request will
+  fail.
+
+  The function may only be called after VirtioFsFuseInitSession() returns
+  successfully and before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs     The Virtio Filesystem device to send the
+                              FUSE_RENAME2 request to. On output, the FUSE
+                              request counter "VirtioFs->RequestId" will have
+                              been incremented.
+
+  @param[in] OldParentNodeId  The inode number of the directory in which
+                              OldName should be removed.
+
+  @param[in] OldName          The single-component filename to remove in the
+                              directory identified by OldParentNodeId.
+
+  @param[in] NewParentNodeId  The inode number of the directory in which
+                              NewName should be created, such that on
+                              successful return, (NewParentNodeId, NewName)
+                              refer to the same inode as (OldParentNodeId,
+                              OldName) did on entry.
+
+  @param[in] NewName          The single-component filename to create in the
+                              directory identified by NewParentNodeId.
+
+  @retval EFI_SUCCESS  The file or directory has been renamed.
+
+  @return              The "errno" value mapped to an EFI_STATUS code, if the
+                       Virtio Filesystem device explicitly reported an error.
+
+  @return              Error codes propagated from VirtioFsSgListsValidate(),
+                       VirtioFsFuseNewRequest(), VirtioFsSgListsSubmit(),
+                       VirtioFsFuseCheckResponse().
+**/
+EFI_STATUS
+VirtioFsFuseRename (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    OldParentNodeId,
+  IN     CHAR8     *OldName,
+  IN     UINT64    NewParentNodeId,
+  IN     CHAR8     *NewName
+  )
+{
+  VIRTIO_FS_FUSE_REQUEST         CommonReq;
+  VIRTIO_FS_FUSE_RENAME2_REQUEST Rename2Req;
+  VIRTIO_FS_IO_VECTOR            ReqIoVec[4];
+  VIRTIO_FS_SCATTER_GATHER_LIST  ReqSgList;
+  VIRTIO_FS_FUSE_RESPONSE        CommonResp;
+  VIRTIO_FS_IO_VECTOR            RespIoVec[1];
+  VIRTIO_FS_SCATTER_GATHER_LIST  RespSgList;
+  EFI_STATUS                     Status;
+
+  //
+  // Set up the scatter-gather lists.
+  //
+  ReqIoVec[0].Buffer = &CommonReq;
+  ReqIoVec[0].Size   = sizeof CommonReq;
+  ReqIoVec[1].Buffer = &Rename2Req;
+  ReqIoVec[1].Size   = sizeof Rename2Req;
+  ReqIoVec[2].Buffer = OldName;
+  ReqIoVec[2].Size   = AsciiStrSize (OldName);
+  ReqIoVec[3].Buffer = NewName;
+  ReqIoVec[3].Size   = AsciiStrSize (NewName);
+  ReqSgList.IoVec    = ReqIoVec;
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);
+
+  RespIoVec[0].Buffer = &CommonResp;
+  RespIoVec[0].Size   = sizeof CommonResp;
+  RespSgList.IoVec    = RespIoVec;
+  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);
+
+  //
+  // Validate the scatter-gather lists; calculate the total transfer sizes.
+  //
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the common request header.
+  //
+  Status = VirtioFsFuseNewRequest (VirtioFs, &CommonReq, ReqSgList.TotalSize,
+             VirtioFsFuseOpRename2, OldParentNodeId);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the FUSE_RENAME2-specific fields.
+  //
+  Rename2Req.NewDir  = NewParentNodeId;
+  Rename2Req.Flags   = VIRTIO_FS_FUSE_RENAME2_REQ_F_NOREPLACE;
+  Rename2Req.Padding = 0;
+
+  //
+  // Submit the request.
+  //
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Verify the response (all response buffers are fixed size).
+  //
+  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);
+  if (Status == EFI_DEVICE_ERROR) {
+    DEBUG ((DEBUG_ERROR, "%a: Label=\"%s\" OldParentNodeId=%Lu OldName=\"%a\" "
+      "NewParentNodeId=%Lu NewName=\"%a\" Errno=%d\n", __FUNCTION__,
+      VirtioFs->Label, OldParentNodeId, OldName, NewParentNodeId, NewName,
+      CommonResp.Error));
+    Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
+  }
+  return Status;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 42/48] OvmfPkg/VirtioFsDxe: add helper for composing rename/move destination path
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (40 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 41/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_RENAME2 Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-18 17:39   ` Ard Biesheuvel
  2020-12-16 21:11 ` [edk2 PATCH 43/48] OvmfPkg/VirtioFsDxe: handle file rename/move in EFI_FILE_PROTOCOL.SetInfo Laszlo Ersek
                   ` (6 subsequent siblings)
  48 siblings, 1 reply; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

The EFI_FILE_PROTOCOL.SetInfo() member is somewhat under-specified; one of
its modes of operation is renaming/moving the file.

In order to create the destination pathname in canonical format, 2*2=4
cases have to be considered. For the sake of discussion, assume the
current canonical pathname of a VIRTIO_FS_FILE is "/home/user/f1.txt".
Then, consider the following rename/move requests from
EFI_FILE_PROTOCOL.SetInfo():

  Destination requested  Destination  Move into   Destination in
  by SetInfo()           relative?    directory?  canonical format
  ---------------------  -----------  ----------  -----------------------
  L"\\dir\\f2.txt"       no           no          "/dir/f2.txt"
  L"\\dir\\"             no           yes         "/dir/f1.txt"
  L"dir\\f2.txt"         yes          no          "/home/user/dir/f2.txt"
  L"dir\\"               yes          yes         "/home/user/dir/f1.txt"

Add the VirtioFsComposeRenameDestination() function, for composing the
last column from the current canonical pathname and the SetInfo() input.

The function works on the following principles:

- The prefix of the destination path is "/", if the SetInfo() rename
  request is absolute.

  Otherwise, the dest prefix is the "current directory" (the most specific
  parent directory) of the original pathname (in the above example,
  "/home/user").

- The suffix of the destination path is precisely the SetInfo() request
  string, if the "move into directory" convenience format -- the trailing
  backslash -- is not used. (In the above example, L"\\dir\\f2.txt" and
  L"dir\\f2.txt".)

  Otherwise, the suffix is the SetInfo() request, plus the original
  basename (in the above example, L"\\dir\\f1.txt" and L"dir\\f1.txt").

- The complete destination is created by fusing the dest prefix and the
  dest suffix, using the VirtioFsAppendPath() function.

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>
---
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h |   8 +
 OvmfPkg/VirtioFsDxe/Helpers.c     | 194 ++++++++++++++++++++
 2 files changed, 202 insertions(+)

diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 9334e5434c51..a6dfac71f4a7 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -257,16 +257,24 @@ VirtioFsLookupMostSpecificParentDir (
 
 EFI_STATUS
 VirtioFsGetBasename (
   IN     CHAR8  *Path,
      OUT CHAR16 *Basename     OPTIONAL,
   IN OUT UINTN  *BasenameSize
   );
 
+EFI_STATUS
+VirtioFsComposeRenameDestination (
+  IN     CHAR8   *LhsPath8,
+  IN     CHAR16  *RhsPath16,
+     OUT CHAR8   **ResultPath8,
+     OUT BOOLEAN *RootEscape
+  );
+
 EFI_STATUS
 VirtioFsFuseAttrToEfiFileInfo (
   IN     VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr,
      OUT EFI_FILE_INFO                      *FileInfo
   );
 
 EFI_STATUS
 VirtioFsFuseDirentPlusToEfiFileInfo (
diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
index cdaa8557a17b..b741cf753495 100644
--- a/OvmfPkg/VirtioFsDxe/Helpers.c
+++ b/OvmfPkg/VirtioFsDxe/Helpers.c
@@ -1778,16 +1778,210 @@ VirtioFsGetBasename (
   }
 
   for (Idx = LastComponent; Idx < PathSize; Idx++) {
     Basename[Idx - LastComponent] = Path[Idx];
   }
   return EFI_SUCCESS;
 }
 
+/**
+  Format the destination of a rename/move operation as a dynamically allocated
+  canonical pathname.
+
+  Any dot-dot in RhsPath16 that would remove the root directory is dropped, and
+  reported through RootEscape, without failing the function call.
+
+  @param[in] LhsPath8     The source pathname operand of the rename/move
+                          operation, expressed as a canonical pathname (as
+                          defined in the description of VirtioFsAppendPath()).
+                          The root directory "/" cannot be renamed/moved, and
+                          will be rejected.
+
+  @param[in] RhsPath16    The destination pathname operand expressed as a
+                          UEFI-style CHAR16 pathname.
+
+                          If RhsPath16 starts with a backslash, then RhsPath16
+                          is considered absolute. Otherwise, RhsPath16 is
+                          interpreted relative to the most specific parent
+                          directory found in LhsPath8.
+
+                          Independently, if RhsPath16 ends with a backslash
+                          (i.e., RhsPath16 is given in the "move into
+                          directory" convenience form), then RhsPath16 is
+                          interpreted with the basename of LhsPath8 appended.
+                          Otherwise, the last pathname component of RhsPath16
+                          is taken as the last pathname component of the
+                          rename/move destination.
+
+                          An empty RhsPath16 is rejected.
+
+  @param[out] ResultPath8  The POSIX-style, canonical format pathname that
+                           leads to the renamed/moved file. After use, the
+                           caller is responsible for freeing ResultPath8.
+
+  @param[out] RootEscape   Set to TRUE if at least one dot-dot component in
+                           RhsPath16 attempted to escape the root directory;
+                           set to FALSE otherwise.
+
+  @retval EFI_SUCCESS            ResultPath8 has been produced. RootEscape has
+                                 been output.
+
+  @retval EFI_INVALID_PARAMETER  LhsPath8 is "/".
+
+  @retval EFI_INVALID_PARAMETER  RhsPath16 is zero-length.
+
+  @retval EFI_INVALID_PARAMETER  RhsPath16 failed the
+                                 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
+
+  @retval EFI_OUT_OF_RESOURCES   Memory allocation failed.
+
+  @retval EFI_OUT_OF_RESOURCES   ResultPath8 would have failed the
+                                 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
+
+  @retval EFI_UNSUPPORTED        RhsPath16 contains a character that either
+                                 falls outside of the printable ASCII set, or
+                                 is a forward slash.
+**/
+EFI_STATUS
+VirtioFsComposeRenameDestination (
+  IN     CHAR8   *LhsPath8,
+  IN     CHAR16  *RhsPath16,
+     OUT CHAR8   **ResultPath8,
+     OUT BOOLEAN *RootEscape
+  )
+{
+  //
+  // Lengths are expressed as numbers of characters (CHAR8 or CHAR16),
+  // excluding terminating NULs. Sizes are expressed as byte counts, including
+  // the bytes taken up by terminating NULs.
+  //
+  UINTN      RhsLen;
+  UINTN      LhsBasename16Size;
+  EFI_STATUS Status;
+  UINTN      LhsBasenameLen;
+  UINTN      DestSuffix16Size;
+  CHAR16     *DestSuffix16;
+  CHAR8      *DestPrefix8;
+
+  //
+  // An empty destination operand for the rename/move operation is not allowed.
+  //
+  RhsLen = StrLen (RhsPath16);
+  if (RhsLen == 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+  //
+  // Enforce length restriction on RhsPath16.
+  //
+  if (RhsLen > VIRTIO_FS_MAX_PATHNAME_LENGTH) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Determine the length of the basename of LhsPath8.
+  //
+  LhsBasename16Size = 0;
+  Status = VirtioFsGetBasename (LhsPath8, NULL, &LhsBasename16Size);
+  ASSERT (Status == EFI_BUFFER_TOO_SMALL);
+  ASSERT (LhsBasename16Size >= sizeof (CHAR16));
+  ASSERT (LhsBasename16Size % sizeof (CHAR16) == 0);
+  LhsBasenameLen = LhsBasename16Size / sizeof (CHAR16) - 1;
+  if (LhsBasenameLen == 0) {
+    //
+    // The root directory cannot be renamed/moved.
+    //
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Resolve the "move into directory" convenience form in RhsPath16.
+  //
+  if (RhsPath16[RhsLen - 1] == L'\\') {
+    //
+    // Append the basename of LhsPath8 as a CHAR16 string to RhsPath16.
+    //
+    DestSuffix16Size = RhsLen * sizeof (CHAR16) + LhsBasename16Size;
+    DestSuffix16 = AllocatePool (DestSuffix16Size);
+    if (DestSuffix16 == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    CopyMem (DestSuffix16, RhsPath16, RhsLen * sizeof (CHAR16));
+    Status = VirtioFsGetBasename (LhsPath8, DestSuffix16 + RhsLen,
+               &LhsBasename16Size);
+    ASSERT_EFI_ERROR (Status);
+  } else {
+    //
+    // Just create a copy of RhsPath16.
+    //
+    DestSuffix16Size = (RhsLen + 1) * sizeof (CHAR16);
+    DestSuffix16 = AllocateCopyPool (DestSuffix16Size, RhsPath16);
+    if (DestSuffix16 == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+  }
+
+  //
+  // If the destination operand is absolute, it will be interpreted relative to
+  // the root directory.
+  //
+  // Otherwise (i.e., if the destination operand is relative), then create the
+  // canonical pathname that the destination operand is interpreted relatively
+  // to; that is, the canonical pathname of the most specific parent directory
+  // found in LhsPath8.
+  //
+  if (DestSuffix16[0] == L'\\') {
+    DestPrefix8 = AllocateCopyPool (sizeof "/", "/");
+    if (DestPrefix8 == NULL) {
+      Status = EFI_OUT_OF_RESOURCES;
+      goto FreeDestSuffix16;
+    }
+  } else {
+    UINTN LhsLen;
+    UINTN DestPrefixLen;
+
+    //
+    // Strip the basename of LhsPath8.
+    //
+    LhsLen = AsciiStrLen (LhsPath8);
+    ASSERT (LhsBasenameLen < LhsLen);
+    DestPrefixLen = LhsLen - LhsBasenameLen;
+    ASSERT (LhsPath8[DestPrefixLen - 1] == '/');
+    //
+    // If we're not at the root directory, strip the slash too.
+    //
+    if (DestPrefixLen > 1) {
+      DestPrefixLen--;
+    }
+    DestPrefix8 = AllocatePool (DestPrefixLen + 1);
+    if (DestPrefix8 == NULL) {
+      Status = EFI_OUT_OF_RESOURCES;
+      goto FreeDestSuffix16;
+    }
+    CopyMem (DestPrefix8, LhsPath8, DestPrefixLen);
+    DestPrefix8[DestPrefixLen] = '\0';
+  }
+
+  //
+  // Now combine DestPrefix8 and DestSuffix16 into the final canonical
+  // pathname.
+  //
+  Status = VirtioFsAppendPath (DestPrefix8, DestSuffix16, ResultPath8,
+             RootEscape);
+
+  FreePool (DestPrefix8);
+  //
+  // Fall through.
+  //
+FreeDestSuffix16:
+  FreePool (DestSuffix16);
+
+  return Status;
+}
+
 /**
   Convert select fields of a VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
   corresponding fields in EFI_FILE_INFO.
 
   @param[in] FuseAttr   The VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
                         convert the relevant fields from.
 
   @param[out] FileInfo  The EFI_FILE_INFO structure to modify. Importantly, the
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 43/48] OvmfPkg/VirtioFsDxe: handle file rename/move in EFI_FILE_PROTOCOL.SetInfo
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (41 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 42/48] OvmfPkg/VirtioFsDxe: add helper for composing rename/move destination path Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 44/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_SETATTR Laszlo Ersek
                   ` (5 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Using the functions introduced previously, we can now implement the rename
operation in VirtioFsSimpleFileSetInfo().

Attribute updates come later.

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>
---
 OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c | 234 +++++++++++++++++++-
 1 file changed, 233 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c b/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c
index 895b5c029a9e..55169dde78b7 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c
@@ -5,16 +5,17 @@
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
 #include <Guid/FileSystemInfo.h>            // gEfiFileSystemInfoGuid
 #include <Guid/FileSystemVolumeLabelInfo.h> // gEfiFileSystemVolumeLabelInfo...
 #include <Library/BaseLib.h>                // StrCmp()
 #include <Library/BaseMemoryLib.h>          // CompareGuid()
+#include <Library/MemoryAllocationLib.h>    // FreePool()
 
 #include "VirtioFsDxe.h"
 
 /**
   Validate a buffer that the EFI_FILE_PROTOCOL.SetInfo() caller passes in for a
   particular InformationType GUID.
 
   The structure to be validated is supposed to end with a variable-length,
@@ -122,16 +123,247 @@ ValidateInfoStructure (
   NameField = (CHAR16 *)((UINT8 *)Buffer + NameFieldByteOffset);
   if (NameField[NameFieldChar16s - 1] != L'\0') {
     return EFI_INVALID_PARAMETER;
   }
 
   return EFI_SUCCESS;
 }
 
+/**
+  Rename a VIRTIO_FS_FILE as requested in EFI_FILE_INFO.FileName.
+
+  @param[in,out] VirtioFsFile  The VIRTIO_FS_FILE to rename.
+
+  @param[in] NewFileName       The new file name requested by
+                               EFI_FILE_PROTOCOL.SetInfo().
+
+  @retval EFI_SUCCESS        The canonical format destination path that is
+                             determined from the input value of
+                             VirtioFsFile->CanonicalPathname and from
+                             NewFileName is identical to the input value of
+                             VirtioFsFile->CanonicalPathname. This means that
+                             EFI_FILE_INFO does not constitute a rename
+                             request. VirtioFsFile has not been changed.
+
+  @retval EFI_SUCCESS        VirtioFsFile has been renamed.
+                             VirtioFsFile->CanonicalPathname has assumed the
+                             destination pathname in canonical format.
+
+  @retval EFI_ACCESS_DENIED  VirtioFsFile refers to the root directory, and
+                             NewFileName expresses an actual rename/move
+                             request.
+
+  @retval EFI_ACCESS_DENIED  VirtioFsFile is the (possibly indirect) parent
+                             directory of at least one other VIRTIO_FS_FILE
+                             that is open for the same Virtio Filesystem
+                             (identified by VirtioFsFile->OwnerFs). Renaming
+                             VirtioFsFile would invalidate the canonical
+                             pathnames of those VIRTIO_FS_FILE instances;
+                             therefore the request has been rejected.
+
+  @retval EFI_ACCESS_DENIED  VirtioFsFile is not open for writing, but
+                             NewFileName expresses an actual rename/move
+                             request.
+
+  @retval EFI_NOT_FOUND      At least one dot-dot component in NewFileName
+                             attempted to escape the root directory.
+
+  @return                    Error codes propagated from underlying functions.
+**/
+STATIC
+EFI_STATUS
+Rename (
+  IN OUT VIRTIO_FS_FILE *VirtioFsFile,
+  IN     CHAR16         *NewFileName
+  )
+{
+
+  VIRTIO_FS  *VirtioFs;
+  EFI_STATUS Status;
+  CHAR8      *Destination;
+  BOOLEAN    RootEscape;
+  UINT64     OldParentDirNodeId;
+  CHAR8      *OldLastComponent;
+  UINT64     NewParentDirNodeId;
+  CHAR8      *NewLastComponent;
+
+  VirtioFs = VirtioFsFile->OwnerFs;
+
+  //
+  // The root directory cannot be renamed.
+  //
+  if (AsciiStrCmp (VirtioFsFile->CanonicalPathname, "/") == 0) {
+    if (StrCmp (NewFileName, L"") == 0) {
+      //
+      // Not a rename request anyway.
+      //
+      return EFI_SUCCESS;
+    }
+    return EFI_ACCESS_DENIED;
+  }
+
+  //
+  // Compose the canonical pathname for the destination.
+  //
+  Status = VirtioFsComposeRenameDestination (VirtioFsFile->CanonicalPathname,
+             NewFileName, &Destination, &RootEscape);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  if (RootEscape) {
+    Status = EFI_NOT_FOUND;
+    goto FreeDestination;
+  }
+  //
+  // If the rename would leave VirtioFsFile->CanonicalPathname unchanged, then
+  // EFI_FILE_PROTOCOL.SetInfo() isn't asking for a rename actually.
+  //
+  if (AsciiStrCmp (VirtioFsFile->CanonicalPathname, Destination) == 0) {
+    Status = EFI_SUCCESS;
+    goto FreeDestination;
+  }
+  //
+  // Check if the rename would break the canonical pathnames of other
+  // VIRTIO_FS_FILE instances of the same VIRTIO_FS.
+  //
+  if (VirtioFsFile->IsDirectory) {
+    UINTN      PathLen;
+    LIST_ENTRY *OpenFilesEntry;
+
+    PathLen = AsciiStrLen (VirtioFsFile->CanonicalPathname);
+    BASE_LIST_FOR_EACH (OpenFilesEntry, &VirtioFs->OpenFiles) {
+      VIRTIO_FS_FILE *OtherFile;
+
+      OtherFile = VIRTIO_FS_FILE_FROM_OPEN_FILES_ENTRY (OpenFilesEntry);
+      if (OtherFile != VirtioFsFile &&
+          AsciiStrnCmp (VirtioFsFile->CanonicalPathname,
+            OtherFile->CanonicalPathname, PathLen) == 0 &&
+          (OtherFile->CanonicalPathname[PathLen] == '\0' ||
+           OtherFile->CanonicalPathname[PathLen] == '/')) {
+        //
+        // OtherFile refers to the same directory as VirtioFsFile, or is a
+        // (possibly indirect) child of the directory referred to by
+        // VirtioFsFile.
+        //
+        Status = EFI_ACCESS_DENIED;
+        goto FreeDestination;
+      }
+    }
+  }
+  //
+  // From this point on, the file needs to be open for writing.
+  //
+  if (!VirtioFsFile->IsOpenForWriting) {
+    Status = EFI_ACCESS_DENIED;
+    goto FreeDestination;
+  }
+  //
+  // Split both source and destination canonical pathnames into (most specific
+  // parent directory, last component) pairs.
+  //
+  Status = VirtioFsLookupMostSpecificParentDir (VirtioFs,
+             VirtioFsFile->CanonicalPathname, &OldParentDirNodeId,
+             &OldLastComponent);
+  if (EFI_ERROR (Status)) {
+    goto FreeDestination;
+  }
+  Status = VirtioFsLookupMostSpecificParentDir (VirtioFs, Destination,
+             &NewParentDirNodeId, &NewLastComponent);
+  if (EFI_ERROR (Status)) {
+    goto ForgetOldParentDirNodeId;
+  }
+  //
+  // Perform the rename. If the destination path exists, the rename will fail.
+  //
+  Status = VirtioFsFuseRename (VirtioFs, OldParentDirNodeId, OldLastComponent,
+             NewParentDirNodeId, NewLastComponent);
+  if (EFI_ERROR (Status)) {
+    goto ForgetNewParentDirNodeId;
+  }
+
+  //
+  // Swap in the new canonical pathname.
+  //
+  FreePool (VirtioFsFile->CanonicalPathname);
+  VirtioFsFile->CanonicalPathname = Destination;
+  Destination = NULL;
+  Status = EFI_SUCCESS;
+
+  //
+  // Fall through.
+  //
+ForgetNewParentDirNodeId:
+  if (NewParentDirNodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
+    VirtioFsFuseForget (VirtioFs, NewParentDirNodeId);
+  }
+
+ForgetOldParentDirNodeId:
+  if (OldParentDirNodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
+    VirtioFsFuseForget (VirtioFs, OldParentDirNodeId);
+  }
+
+FreeDestination:
+  if (Destination != NULL) {
+    FreePool (Destination);
+  }
+  return Status;
+}
+
+/**
+  Process an EFI_FILE_INFO setting request.
+**/
+STATIC
+EFI_STATUS
+SetFileInfo (
+  IN EFI_FILE_PROTOCOL *This,
+  IN UINTN             BufferSize,
+  IN VOID              *Buffer
+  )
+{
+  VIRTIO_FS_FILE *VirtioFsFile;
+  EFI_STATUS     Status;
+  EFI_FILE_INFO  *FileInfo;
+
+  VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
+
+  //
+  // Validate if Buffer passes as EFI_FILE_INFO.
+  //
+  Status = ValidateInfoStructure (
+             BufferSize,                    // SizeByProtocolCaller
+             OFFSET_OF (EFI_FILE_INFO,
+               FileName) + sizeof (CHAR16), // MinimumStructSize
+             TRUE,                          // IsSizeByInfoPresent
+             Buffer
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  FileInfo = Buffer;
+
+  //
+  // Perform the rename/move request, if any.
+  //
+  Status = Rename (VirtioFsFile, FileInfo->FileName);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  //
+  // Update any attributes requested.
+  //
+  Status = EFI_UNSUPPORTED;
+  //
+  // The UEFI spec does not speak about partial failure in
+  // EFI_FILE_PROTOCOL.SetInfo(); we won't try to roll back the rename (if
+  // there was one) in case the attribute updates fail.
+  //
+  return Status;
+}
+
 /**
   Process an EFI_FILE_SYSTEM_INFO setting request.
 **/
 STATIC
 EFI_STATUS
 SetFileSystemInfo (
   IN EFI_FILE_PROTOCOL *This,
   IN UINTN             BufferSize,
@@ -225,17 +457,17 @@ EFIAPI
 VirtioFsSimpleFileSetInfo (
   IN EFI_FILE_PROTOCOL *This,
   IN EFI_GUID          *InformationType,
   IN UINTN             BufferSize,
   IN VOID              *Buffer
   )
 {
   if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
-    return EFI_UNSUPPORTED;
+    return SetFileInfo (This, BufferSize, Buffer);
   }
 
   if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
     return SetFileSystemInfo (This, BufferSize, Buffer);
   }
 
   if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
     return SetFileSystemVolumeLabelInfo (This, BufferSize, Buffer);
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 44/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_SETATTR
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (42 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 43/48] OvmfPkg/VirtioFsDxe: handle file rename/move in EFI_FILE_PROTOCOL.SetInfo Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 45/48] OvmfPkg/VirtioFsDxe: add helper for determining file size update Laszlo Ersek
                   ` (4 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Add the VirtioFsFuseSetAttr() function, for sending the FUSE_SETATTR
command to the Virtio Filesystem device.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  35 +++-
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |   1 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |  10 ++
 OvmfPkg/VirtioFsDxe/FuseSetAttr.c           | 174 ++++++++++++++++++++
 4 files changed, 219 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index 0b2ed7010046..15fb28f95a28 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -97,16 +97,25 @@ typedef struct {
 #define VIRTIO_FS_FUSE_MODE_PERM_WUSR 0000200u
 #define VIRTIO_FS_FUSE_MODE_PERM_RWXG 0000070u
 #define VIRTIO_FS_FUSE_MODE_PERM_RGRP 0000040u
 #define VIRTIO_FS_FUSE_MODE_PERM_WGRP 0000020u
 #define VIRTIO_FS_FUSE_MODE_PERM_RWXO 0000007u
 #define VIRTIO_FS_FUSE_MODE_PERM_ROTH 0000004u
 #define VIRTIO_FS_FUSE_MODE_PERM_WOTH 0000002u
 
+//
+// Flags for VirtioFsFuseOpSetAttr, in the VIRTIO_FS_FUSE_SETATTR_REQUEST.Valid
+// field.
+//
+#define VIRTIO_FS_FUSE_SETATTR_REQ_F_MODE  BIT0
+#define VIRTIO_FS_FUSE_SETATTR_REQ_F_SIZE  BIT3
+#define VIRTIO_FS_FUSE_SETATTR_REQ_F_ATIME BIT4
+#define VIRTIO_FS_FUSE_SETATTR_REQ_F_MTIME BIT5
+
 //
 // Flags for VirtioFsFuseOpOpen.
 //
 #define VIRTIO_FS_FUSE_OPEN_REQ_F_RDONLY 0
 #define VIRTIO_FS_FUSE_OPEN_REQ_F_RDWR   2
 
 //
 // Flags for VirtioFsFuseOpInit.
@@ -148,16 +157,17 @@ typedef struct {
 
 //
 // FUSE operation codes.
 //
 typedef enum {
   VirtioFsFuseOpLookup      =  1,
   VirtioFsFuseOpForget      =  2,
   VirtioFsFuseOpGetAttr     =  3,
+  VirtioFsFuseOpSetAttr     =  4,
   VirtioFsFuseOpMkDir       =  9,
   VirtioFsFuseOpUnlink      = 10,
   VirtioFsFuseOpRmDir       = 11,
   VirtioFsFuseOpOpen        = 14,
   VirtioFsFuseOpRead        = 15,
   VirtioFsFuseOpWrite       = 16,
   VirtioFsFuseOpStatFs      = 17,
   VirtioFsFuseOpRelease     = 18,
@@ -235,30 +245,53 @@ typedef struct {
 //
 // Header for VirtioFsFuseOpForget.
 //
 typedef struct {
   UINT64 NumberOfLookups;
 } VIRTIO_FS_FUSE_FORGET_REQUEST;
 
 //
-// Headers for VirtioFsFuseOpGetAttr.
+// Headers for VirtioFsFuseOpGetAttr (VIRTIO_FS_FUSE_GETATTR_RESPONSE is also
+// for VirtioFsFuseOpSetAttr).
 //
 typedef struct {
   UINT32 GetAttrFlags;
   UINT32 Dummy;
   UINT64 FileHandle;
 } VIRTIO_FS_FUSE_GETATTR_REQUEST;
 
 typedef struct {
   UINT64 AttrValid;
   UINT32 AttrValidNsec;
   UINT32 Dummy;
 } VIRTIO_FS_FUSE_GETATTR_RESPONSE;
 
+//
+// Header for VirtioFsFuseOpSetAttr.
+//
+typedef struct {
+  UINT32 Valid;
+  UINT32 Padding;
+  UINT64 FileHandle;
+  UINT64 Size;
+  UINT64 LockOwner;
+  UINT64 Atime;
+  UINT64 Mtime;
+  UINT64 Ctime;
+  UINT32 AtimeNsec;
+  UINT32 MtimeNsec;
+  UINT32 CtimeNsec;
+  UINT32 Mode;
+  UINT32 Unused4;
+  UINT32 Uid;
+  UINT32 Gid;
+  UINT32 Unused5;
+} VIRTIO_FS_FUSE_SETATTR_REQUEST;
+
 //
 // Header for VirtioFsFuseOpMkDir.
 //
 typedef struct {
   UINT32 Mode;
   UINT32 Umask;
 } VIRTIO_FS_FUSE_MKDIR_REQUEST;
 
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
index 9dccd7d6a9ef..8d962bcdc18c 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
@@ -91,16 +91,17 @@ [Sources]
   FuseLookup.c
   FuseMkDir.c
   FuseOpen.c
   FuseOpenDir.c
   FuseOpenOrCreate.c
   FuseRead.c
   FuseRelease.c
   FuseRename.c
+  FuseSetAttr.c
   FuseStatFs.c
   FuseUnlink.c
   FuseWrite.c
   Helpers.c
   SimpleFsClose.c
   SimpleFsDelete.c
   SimpleFsFlush.c
   SimpleFsGetInfo.c
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index a6dfac71f4a7..9e6348f9386e 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -303,16 +303,26 @@ VirtioFsFuseForget (
 
 EFI_STATUS
 VirtioFsFuseGetAttr (
   IN OUT VIRTIO_FS                          *VirtioFs,
   IN     UINT64                             NodeId,
      OUT VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr
   );
 
+EFI_STATUS
+VirtioFsFuseSetAttr (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId,
+  IN     UINT64    *Size      OPTIONAL,
+  IN     UINT64    *Atime     OPTIONAL,
+  IN     UINT64    *Mtime     OPTIONAL,
+  IN     UINT32    *Mode      OPTIONAL
+  );
+
 EFI_STATUS
 VirtioFsFuseMkDir (
   IN OUT VIRTIO_FS *VirtioFs,
   IN     UINT64    ParentNodeId,
   IN     CHAR8     *Name,
      OUT UINT64    *NodeId
   );
 
diff --git a/OvmfPkg/VirtioFsDxe/FuseSetAttr.c b/OvmfPkg/VirtioFsDxe/FuseSetAttr.c
new file mode 100644
index 000000000000..dcd014172022
--- /dev/null
+++ b/OvmfPkg/VirtioFsDxe/FuseSetAttr.c
@@ -0,0 +1,174 @@
+/** @file
+  FUSE_SETATTR wrapper for the Virtio Filesystem device.
+
+  Copyright (C) 2020, Red Hat, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "VirtioFsDxe.h"
+
+/**
+  Send the FUSE_SETATTR request to the Virtio Filesystem device, for changing
+  the attributes of an inode.
+
+  The function may only be called after VirtioFsFuseInitSession() returns
+  successfully and before VirtioFsUninit() is called.
+
+  @param[in,out] VirtioFs  The Virtio Filesystem device to send the
+                           FUSE_SETATTR request to. On output, the FUSE request
+                           counter "VirtioFs->RequestId" will have been
+                           incremented.
+
+  @param[in] NodeId        The inode number representing the regular file or
+                           directory whose attributes should be changed.
+
+  @param[in] Size          The new size to set for the regular file. If NULL,
+                           then the file size will not be changed. If NodeId
+                           refers to a directory, then the caller is
+                           responsible for passing NULL as Size.
+
+  @param[in] Atime         The new last access time to set for the regular file
+                           or directory (seconds since the Epoch). If NULL,
+                           then the last access time is not changed.
+
+  @param[in] Mtime         The new last modification time to set for the
+                           regular file or directory (seconds since the Epoch).
+                           If NULL, then the last modification time is not
+                           changed.
+
+  @param[in] Mode          The new file mode bits to set for the regular file
+                           or directory. If NULL, then the file mode bits are
+                           not changed.
+
+  @retval EFI_SUCCESS  The attributes have been updated.
+
+  @return              The "errno" value mapped to an EFI_STATUS code, if the
+                       Virtio Filesystem device explicitly reported an error.
+
+  @return              Error codes propagated from VirtioFsSgListsValidate(),
+                       VirtioFsFuseNewRequest(), VirtioFsSgListsSubmit(),
+                       VirtioFsFuseCheckResponse().
+**/
+EFI_STATUS
+VirtioFsFuseSetAttr (
+  IN OUT VIRTIO_FS *VirtioFs,
+  IN     UINT64    NodeId,
+  IN     UINT64    *Size      OPTIONAL,
+  IN     UINT64    *Atime     OPTIONAL,
+  IN     UINT64    *Mtime     OPTIONAL,
+  IN     UINT32    *Mode      OPTIONAL
+  )
+{
+  VIRTIO_FS_FUSE_REQUEST             CommonReq;
+  VIRTIO_FS_FUSE_SETATTR_REQUEST     AttrReq;
+  VIRTIO_FS_IO_VECTOR                ReqIoVec[2];
+  VIRTIO_FS_SCATTER_GATHER_LIST      ReqSgList;
+  VIRTIO_FS_FUSE_RESPONSE            CommonResp;
+  VIRTIO_FS_FUSE_GETATTR_RESPONSE    GetAttrResp;
+  VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE AttrResp;
+  VIRTIO_FS_IO_VECTOR                RespIoVec[3];
+  VIRTIO_FS_SCATTER_GATHER_LIST      RespSgList;
+  EFI_STATUS                         Status;
+
+  //
+  // Set up the scatter-gather lists.
+  //
+  ReqIoVec[0].Buffer = &CommonReq;
+  ReqIoVec[0].Size   = sizeof CommonReq;
+  ReqIoVec[1].Buffer = &AttrReq;
+  ReqIoVec[1].Size   = sizeof AttrReq;
+  ReqSgList.IoVec    = ReqIoVec;
+  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);
+
+  RespIoVec[0].Buffer = &CommonResp;
+  RespIoVec[0].Size   = sizeof CommonResp;
+  RespIoVec[1].Buffer = &GetAttrResp;
+  RespIoVec[1].Size   = sizeof GetAttrResp;
+  RespIoVec[2].Buffer = &AttrResp;
+  RespIoVec[2].Size   = sizeof AttrResp;
+  RespSgList.IoVec    = RespIoVec;
+  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);
+
+  //
+  // Validate the scatter-gather lists; calculate the total transfer sizes.
+  //
+  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the common request header.
+  //
+  Status = VirtioFsFuseNewRequest (VirtioFs, &CommonReq, ReqSgList.TotalSize,
+             VirtioFsFuseOpSetAttr, NodeId);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Populate the FUSE_SETATTR-specific fields.
+  //
+  AttrReq.Valid      = 0;
+  AttrReq.Padding    = 0;
+  AttrReq.FileHandle = 0;
+  AttrReq.Size       = (Size == NULL) ? 0 : *Size;
+  AttrReq.LockOwner  = 0;
+  AttrReq.Atime      = (Atime == NULL) ? 0 : *Atime;
+  AttrReq.Mtime      = (Mtime == NULL) ? 0 : *Mtime;
+  AttrReq.Ctime      = 0;
+  AttrReq.AtimeNsec  = 0;
+  AttrReq.MtimeNsec  = 0;
+  AttrReq.CtimeNsec  = 0;
+  AttrReq.Mode       = (Mode == NULL) ? 0 : *Mode;
+  AttrReq.Unused4    = 0;
+  AttrReq.Uid        = 0;
+  AttrReq.Gid        = 0;
+  AttrReq.Unused5    = 0;
+
+  if (Size != NULL) {
+    AttrReq.Valid |= VIRTIO_FS_FUSE_SETATTR_REQ_F_SIZE;
+  }
+  if (Atime != NULL) {
+    AttrReq.Valid |= VIRTIO_FS_FUSE_SETATTR_REQ_F_ATIME;
+  }
+  if (Mtime != NULL) {
+    AttrReq.Valid |= VIRTIO_FS_FUSE_SETATTR_REQ_F_MTIME;
+  }
+  if (Mode != NULL) {
+    AttrReq.Valid |= VIRTIO_FS_FUSE_SETATTR_REQ_F_MODE;
+  }
+
+  //
+  // Submit the request.
+  //
+  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Verify the response (all response buffers are fixed size).
+  //
+  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);
+  if (Status == EFI_DEVICE_ERROR) {
+    DEBUG ((DEBUG_ERROR, "%a: Label=\"%s\" NodeId=%Lu", __FUNCTION__,
+      VirtioFs->Label, NodeId));
+    if (Size != NULL) {
+      DEBUG ((DEBUG_ERROR, " Size=0x%Lx", *Size));
+    }
+    if (Atime != NULL) {
+      DEBUG ((DEBUG_ERROR, " Atime=%Lu", *Atime));
+    }
+    if (Mtime != NULL) {
+      DEBUG ((DEBUG_ERROR, " Mtime=%Lu", *Mtime));
+    }
+    if (Mode != NULL) {
+      DEBUG ((DEBUG_ERROR, " Mode=0x%x", *Mode)); // no support for octal :/
+    }
+    DEBUG ((DEBUG_ERROR, " Errno=%d\n",  CommonResp.Error));
+    Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
+  }
+  return Status;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 45/48] OvmfPkg/VirtioFsDxe: add helper for determining file size update
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (43 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 44/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_SETATTR Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 46/48] OvmfPkg/VirtioFsDxe: add helper for determining access time updates Laszlo Ersek
                   ` (3 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Add the VirtioFsGetFuseSizeUpdate() function, for determining whether an
EFI_FILE_PROTOCOL.SetInfo() invocation requests a file size update.

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>
---
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h |  8 ++++
 OvmfPkg/VirtioFsDxe/Helpers.c     | 40 ++++++++++++++++++++
 2 files changed, 48 insertions(+)

diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 9e6348f9386e..096756302942 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -277,16 +277,24 @@ VirtioFsFuseAttrToEfiFileInfo (
   );
 
 EFI_STATUS
 VirtioFsFuseDirentPlusToEfiFileInfo (
   IN     VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE *FuseDirent,
   IN OUT EFI_FILE_INFO                      *FileInfo
   );
 
+VOID
+VirtioFsGetFuseSizeUpdate (
+  IN     EFI_FILE_INFO *Info,
+  IN     EFI_FILE_INFO *NewInfo,
+     OUT BOOLEAN       *Update,
+     OUT UINT64        *Size
+  );
+
 //
 // Wrapper functions for FUSE commands (primitives).
 //
 
 EFI_STATUS
 VirtioFsFuseLookup (
   IN OUT VIRTIO_FS                          *VirtioFs,
   IN     UINT64                             DirNodeId,
diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
index b741cf753495..838971bd2c85 100644
--- a/OvmfPkg/VirtioFsDxe/Helpers.c
+++ b/OvmfPkg/VirtioFsDxe/Helpers.c
@@ -2168,8 +2168,48 @@ VirtioFsFuseDirentPlusToEfiFileInfo (
   FileInfo->FileName[Idx++] = L'\0';
   //
   // Set the (possibly reduced) size.
   //
   FileInfo->Size = FileInfoSize;
 
   return EFI_SUCCESS;
 }
+
+/**
+  Given an EFI_FILE_INFO object received in an EFI_FILE_PROTOCOL.SetInfo()
+  call, determine whether updating the size of the file is necessary, relative
+  to an EFI_FILE_INFO object describing the current state of the file.
+
+  @param[in] Info     The EFI_FILE_INFO describing the current state of the
+                      file. The caller is responsible for populating Info on
+                      input with VirtioFsFuseAttrToEfiFileInfo(), from the
+                      current FUSE attributes of the file. The Info->Size and
+                      Info->FileName members are ignored.
+
+  @param[in] NewInfo  The EFI_FILE_INFO object received in the
+                      EFI_FILE_PROTOCOL.SetInfo() call.
+
+  @param[out] Update  Set to TRUE on output if the file size needs to be
+                      updated. Set to FALSE otherwise.
+
+  @param[out] Size    If Update is set to TRUE, then Size provides the new file
+                      size to set. Otherwise, Size is not written to.
+**/
+VOID
+VirtioFsGetFuseSizeUpdate (
+  IN     EFI_FILE_INFO *Info,
+  IN     EFI_FILE_INFO *NewInfo,
+     OUT BOOLEAN       *Update,
+     OUT UINT64        *Size
+  )
+{
+  BOOLEAN IsDirectory;
+
+  IsDirectory = (BOOLEAN)((Info->Attribute & EFI_FILE_DIRECTORY) != 0);
+
+  if (IsDirectory || Info->FileSize == NewInfo->FileSize) {
+    *Update = FALSE;
+    return;
+  }
+  *Update = TRUE;
+  *Size = NewInfo->FileSize;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 46/48] OvmfPkg/VirtioFsDxe: add helper for determining access time updates
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (44 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 45/48] OvmfPkg/VirtioFsDxe: add helper for determining file size update Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 47/48] OvmfPkg/VirtioFsDxe: add helper for determining file mode bits update Laszlo Ersek
                   ` (2 subsequent siblings)
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Add the VirtioFsGetFuseTimeUpdates() function, for determining whether an
EFI_FILE_PROTOCOL.SetInfo() invocation requests access time updates.

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>
---
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h |  10 ++
 OvmfPkg/VirtioFsDxe/Helpers.c     | 106 ++++++++++++++++++++
 2 files changed, 116 insertions(+)

diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 096756302942..4331cabbd40e 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -285,16 +285,26 @@ VirtioFsFuseDirentPlusToEfiFileInfo (
 VOID
 VirtioFsGetFuseSizeUpdate (
   IN     EFI_FILE_INFO *Info,
   IN     EFI_FILE_INFO *NewInfo,
      OUT BOOLEAN       *Update,
      OUT UINT64        *Size
   );
 
+EFI_STATUS
+VirtioFsGetFuseTimeUpdates (
+  IN     EFI_FILE_INFO *Info,
+  IN     EFI_FILE_INFO *NewInfo,
+     OUT BOOLEAN       *UpdateAtime,
+     OUT BOOLEAN       *UpdateMtime,
+     OUT UINT64        *Atime,
+     OUT UINT64        *Mtime
+  );
+
 //
 // Wrapper functions for FUSE commands (primitives).
 //
 
 EFI_STATUS
 VirtioFsFuseLookup (
   IN OUT VIRTIO_FS                          *VirtioFs,
   IN     UINT64                             DirNodeId,
diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
index 838971bd2c85..c85c7400f2be 100644
--- a/OvmfPkg/VirtioFsDxe/Helpers.c
+++ b/OvmfPkg/VirtioFsDxe/Helpers.c
@@ -2208,8 +2208,114 @@ VirtioFsGetFuseSizeUpdate (
 
   if (IsDirectory || Info->FileSize == NewInfo->FileSize) {
     *Update = FALSE;
     return;
   }
   *Update = TRUE;
   *Size = NewInfo->FileSize;
 }
+
+/**
+  Given an EFI_FILE_INFO object received in an EFI_FILE_PROTOCOL.SetInfo()
+  call, determine whether updating the last access time and/or the last
+  modification time of the file is necessary, relative to an EFI_FILE_INFO
+  object describing the current state of the file.
+
+  @param[in] Info          The EFI_FILE_INFO describing the current state of
+                           the file. The caller is responsible for populating
+                           Info on input with VirtioFsFuseAttrToEfiFileInfo(),
+                           from the current FUSE attributes of the file. The
+                           Info->Size and Info->FileName members are ignored.
+
+  @param[in] NewInfo       The EFI_FILE_INFO object received in the
+                           EFI_FILE_PROTOCOL.SetInfo() call.
+
+  @param[out] UpdateAtime  Set to TRUE on output if the last access time needs
+                           to be updated. Set to FALSE otherwise.
+
+  @param[out] UpdateMtime  Set to TRUE on output if the last modification time
+                           needs to be updated. Set to FALSE otherwise.
+
+  @param[out] Atime        If UpdateAtime is set to TRUE, then Atime provides
+                           the last access timestamp to set (as seconds since
+                           the Epoch). Otherwise, Atime is not written to.
+
+  @param[out] Mtime        If UpdateMtime is set to TRUE, then Mtime provides
+                           the last modification timestamp to set (as seconds
+                           since the Epoch). Otherwise, Mtime is not written
+                           to.
+
+  @retval EFI_SUCCESS        Output parameters have been set successfully.
+
+  @retval EFI_ACCESS_DENIED  NewInfo requests changing both CreateTime and
+                             ModificationTime, but to values that differ from
+                             each other. The Virtio Filesystem device does not
+                             support this.
+**/
+EFI_STATUS
+VirtioFsGetFuseTimeUpdates (
+  IN     EFI_FILE_INFO *Info,
+  IN     EFI_FILE_INFO *NewInfo,
+     OUT BOOLEAN       *UpdateAtime,
+     OUT BOOLEAN       *UpdateMtime,
+     OUT UINT64        *Atime,
+     OUT UINT64        *Mtime
+  )
+{
+  EFI_TIME              *Time[3];
+  EFI_TIME              *NewTime[ARRAY_SIZE (Time)];
+  UINTN                 Idx;
+  STATIC CONST EFI_TIME ZeroTime;
+  BOOLEAN               Change[ARRAY_SIZE (Time)];
+  UINT64                Seconds[ARRAY_SIZE (Time)];
+
+  Time[0]    = &Info->CreateTime;
+  Time[1]    = &Info->LastAccessTime;
+  Time[2]    = &Info->ModificationTime;
+  NewTime[0] = &NewInfo->CreateTime;
+  NewTime[1] = &NewInfo->LastAccessTime;
+  NewTime[2] = &NewInfo->ModificationTime;
+
+  //
+  // Determine which timestamps differ from the current state. (A zero time
+  // means "don't update", per UEFI spec.) For each timestamp that's being
+  // changed, calculate the seconds since the Epoch.
+  //
+  for (Idx = 0; Idx < ARRAY_SIZE (Time); Idx++) {
+    if (CompareMem (NewTime[Idx], &ZeroTime, sizeof (EFI_TIME)) == 0 ||
+        CompareMem (NewTime[Idx], Time[Idx], sizeof (EFI_TIME)) == 0) {
+      Change[Idx] = FALSE;
+    } else {
+      Change[Idx] = TRUE;
+      Seconds[Idx] = EfiTimeToEpoch (NewTime[Idx]);
+    }
+  }
+
+  //
+  // If a change is requested for exactly one of CreateTime and
+  // ModificationTime, we'll change the last modification time. If changes are
+  // requested for both, and to the same timestamp, we'll similarly update the
+  // last modification time. If changes are requested for both, but to
+  // different timestamps, we reject the request.
+  //
+  if (Change[0] && Change[2] && Seconds[0] != Seconds[2]) {
+    return EFI_ACCESS_DENIED;
+  }
+
+  *UpdateAtime = FALSE;
+  *UpdateMtime = FALSE;
+
+  if (Change[0]) {
+    *UpdateMtime = TRUE;
+    *Mtime = Seconds[0];
+  }
+  if (Change[1]) {
+    *UpdateAtime = TRUE;
+    *Atime = Seconds[1];
+  }
+  if (Change[2]) {
+    *UpdateMtime = TRUE;
+    *Mtime = Seconds[2];
+  }
+
+  return EFI_SUCCESS;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 47/48] OvmfPkg/VirtioFsDxe: add helper for determining file mode bits update
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (45 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 46/48] OvmfPkg/VirtioFsDxe: add helper for determining access time updates Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-16 21:11 ` [edk2 PATCH 48/48] OvmfPkg/VirtioFsDxe: handle attribute updates in EFI_FILE_PROTOCOL.SetInfo Laszlo Ersek
  2020-12-18 17:44 ` [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Ard Biesheuvel
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Add the VirtioFsGetFuseModeUpdate() function, for determining whether an
EFI_FILE_PROTOCOL.SetInfo() invocation requests an update to the file mode
bits.

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>
---
 OvmfPkg/Include/IndustryStandard/VirtioFs.h |  3 +
 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |  8 ++
 OvmfPkg/VirtioFsDxe/Helpers.c               | 95 ++++++++++++++++++++
 3 files changed, 106 insertions(+)

diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
index 15fb28f95a28..dee437ec0d39 100644
--- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
+++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
@@ -90,22 +90,25 @@ typedef struct {
 // File mode bitmasks.
 //
 #define VIRTIO_FS_FUSE_MODE_TYPE_MASK 0170000u
 #define VIRTIO_FS_FUSE_MODE_TYPE_REG  0100000u
 #define VIRTIO_FS_FUSE_MODE_TYPE_DIR  0040000u
 #define VIRTIO_FS_FUSE_MODE_PERM_RWXU 0000700u
 #define VIRTIO_FS_FUSE_MODE_PERM_RUSR 0000400u
 #define VIRTIO_FS_FUSE_MODE_PERM_WUSR 0000200u
+#define VIRTIO_FS_FUSE_MODE_PERM_XUSR 0000100u
 #define VIRTIO_FS_FUSE_MODE_PERM_RWXG 0000070u
 #define VIRTIO_FS_FUSE_MODE_PERM_RGRP 0000040u
 #define VIRTIO_FS_FUSE_MODE_PERM_WGRP 0000020u
+#define VIRTIO_FS_FUSE_MODE_PERM_XGRP 0000010u
 #define VIRTIO_FS_FUSE_MODE_PERM_RWXO 0000007u
 #define VIRTIO_FS_FUSE_MODE_PERM_ROTH 0000004u
 #define VIRTIO_FS_FUSE_MODE_PERM_WOTH 0000002u
+#define VIRTIO_FS_FUSE_MODE_PERM_XOTH 0000001u
 
 //
 // Flags for VirtioFsFuseOpSetAttr, in the VIRTIO_FS_FUSE_SETATTR_REQUEST.Valid
 // field.
 //
 #define VIRTIO_FS_FUSE_SETATTR_REQ_F_MODE  BIT0
 #define VIRTIO_FS_FUSE_SETATTR_REQ_F_SIZE  BIT3
 #define VIRTIO_FS_FUSE_SETATTR_REQ_F_ATIME BIT4
diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
index 4331cabbd40e..3c3eb1ac9338 100644
--- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
+++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
@@ -295,16 +295,24 @@ VirtioFsGetFuseTimeUpdates (
   IN     EFI_FILE_INFO *Info,
   IN     EFI_FILE_INFO *NewInfo,
      OUT BOOLEAN       *UpdateAtime,
      OUT BOOLEAN       *UpdateMtime,
      OUT UINT64        *Atime,
      OUT UINT64        *Mtime
   );
 
+EFI_STATUS
+VirtioFsGetFuseModeUpdate (
+  IN     EFI_FILE_INFO *Info,
+  IN     EFI_FILE_INFO *NewInfo,
+     OUT BOOLEAN       *Update,
+     OUT UINT32        *Mode
+     );
+
 //
 // Wrapper functions for FUSE commands (primitives).
 //
 
 EFI_STATUS
 VirtioFsFuseLookup (
   IN OUT VIRTIO_FS                          *VirtioFs,
   IN     UINT64                             DirNodeId,
diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
index c85c7400f2be..b669842a23bd 100644
--- a/OvmfPkg/VirtioFsDxe/Helpers.c
+++ b/OvmfPkg/VirtioFsDxe/Helpers.c
@@ -2314,8 +2314,103 @@ VirtioFsGetFuseTimeUpdates (
   }
   if (Change[2]) {
     *UpdateMtime = TRUE;
     *Mtime = Seconds[2];
   }
 
   return EFI_SUCCESS;
 }
+
+/**
+  Given an EFI_FILE_INFO object received in an EFI_FILE_PROTOCOL.SetInfo()
+  call, determine whether updating the file mode bits of the file is necessary,
+  relative to an EFI_FILE_INFO object describing the current state of the file.
+
+  @param[in] Info     The EFI_FILE_INFO describing the current state of the
+                      file. The caller is responsible for populating Info on
+                      input with VirtioFsFuseAttrToEfiFileInfo(), from the
+                      current FUSE attributes of the file. The Info->Size and
+                      Info->FileName members are ignored.
+
+  @param[in] NewInfo  The EFI_FILE_INFO object received in the
+                      EFI_FILE_PROTOCOL.SetInfo() call.
+
+  @param[out] Update  Set to TRUE on output if the file mode bits need to be
+                      updated. Set to FALSE otherwise.
+
+  @param[out] Mode    If Update is set to TRUE, then Mode provides the file
+                      mode bits to set. Otherwise, Mode is not written to.
+
+  @retval EFI_SUCCESS        Output parameters have been set successfully.
+
+  @retval EFI_ACCESS_DENIED  NewInfo requests toggling an unknown bit in the
+                             Attribute bitmask.
+
+  @retval EFI_ACCESS_DENIED  NewInfo requests toggling EFI_FILE_DIRECTORY in
+                             the Attribute bitmask.
+**/
+EFI_STATUS
+VirtioFsGetFuseModeUpdate (
+  IN     EFI_FILE_INFO *Info,
+  IN     EFI_FILE_INFO *NewInfo,
+     OUT BOOLEAN       *Update,
+     OUT UINT32        *Mode
+     )
+{
+  UINT64  Toggle;
+  BOOLEAN IsDirectory;
+  BOOLEAN IsWriteable;
+  BOOLEAN WillBeWriteable;
+
+  Toggle = Info->Attribute ^ NewInfo->Attribute;
+  if ((Toggle & ~EFI_FILE_VALID_ATTR) != 0) {
+    //
+    // Unknown attribute requested.
+    //
+    return EFI_ACCESS_DENIED;
+  }
+  if ((Toggle & EFI_FILE_DIRECTORY) != 0) {
+    //
+    // EFI_FILE_DIRECTORY cannot be toggled.
+    //
+    return EFI_ACCESS_DENIED;
+  }
+
+  IsDirectory     = (BOOLEAN)((Info->Attribute    & EFI_FILE_DIRECTORY) != 0);
+  IsWriteable     = (BOOLEAN)((Info->Attribute    & EFI_FILE_READ_ONLY) == 0);
+  WillBeWriteable = (BOOLEAN)((NewInfo->Attribute & EFI_FILE_READ_ONLY) == 0);
+
+  if (IsWriteable == WillBeWriteable) {
+    *Update = FALSE;
+    return EFI_SUCCESS;
+  }
+
+  if (IsDirectory) {
+    if (WillBeWriteable) {
+      *Mode = (VIRTIO_FS_FUSE_MODE_PERM_RWXU |
+               VIRTIO_FS_FUSE_MODE_PERM_RWXG |
+               VIRTIO_FS_FUSE_MODE_PERM_RWXO);
+    } else {
+      *Mode = (VIRTIO_FS_FUSE_MODE_PERM_RUSR |
+               VIRTIO_FS_FUSE_MODE_PERM_XUSR |
+               VIRTIO_FS_FUSE_MODE_PERM_RGRP |
+               VIRTIO_FS_FUSE_MODE_PERM_XGRP |
+               VIRTIO_FS_FUSE_MODE_PERM_ROTH |
+               VIRTIO_FS_FUSE_MODE_PERM_XOTH);
+    }
+  } else {
+    if (WillBeWriteable) {
+      *Mode = (VIRTIO_FS_FUSE_MODE_PERM_RUSR |
+               VIRTIO_FS_FUSE_MODE_PERM_WUSR |
+               VIRTIO_FS_FUSE_MODE_PERM_RGRP |
+               VIRTIO_FS_FUSE_MODE_PERM_WGRP |
+               VIRTIO_FS_FUSE_MODE_PERM_ROTH |
+               VIRTIO_FS_FUSE_MODE_PERM_WOTH);
+    } else {
+      *Mode = (VIRTIO_FS_FUSE_MODE_PERM_RUSR |
+               VIRTIO_FS_FUSE_MODE_PERM_RGRP |
+               VIRTIO_FS_FUSE_MODE_PERM_ROTH);
+    }
+  }
+  *Update = TRUE;
+  return EFI_SUCCESS;
+}
-- 
2.19.1.3.g30247aa5d201



^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [edk2 PATCH 48/48] OvmfPkg/VirtioFsDxe: handle attribute updates in EFI_FILE_PROTOCOL.SetInfo
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (46 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 47/48] OvmfPkg/VirtioFsDxe: add helper for determining file mode bits update Laszlo Ersek
@ 2020-12-16 21:11 ` Laszlo Ersek
  2020-12-18 17:44 ` [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Ard Biesheuvel
  48 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-16 21:11 UTC (permalink / raw)
  To: devel, virtio-fs, lersek
  Cc: Ard Biesheuvel, Jordan Justen, Philippe Mathieu-Daudé

Using the functions introduced previously, we can now update file
attributes in VirtioFsSimpleFileSetInfo().

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>
---
 OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c | 107 +++++++++++++++++++-
 1 file changed, 106 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c b/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c
index 55169dde78b7..e7cc3d5dc399 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c
@@ -303,16 +303,121 @@ Rename (
 
 FreeDestination:
   if (Destination != NULL) {
     FreePool (Destination);
   }
   return Status;
 }
 
+/**
+  Update the attributes of a VIRTIO_FS_FILE as requested in EFI_FILE_INFO.
+
+  @param[in,out] VirtioFsFile  The VIRTIO_FS_FILE to update the attributes of.
+
+  @param[in] NewFileInfo       The new attributes requested by
+                               EFI_FILE_PROTOCOL.SetInfo(). NewFileInfo->Size
+                               and NewFileInfo->FileName are ignored.
+
+  @retval EFI_SUCCESS        No attributes had to be updated.
+
+  @retval EFI_SUCCESS        The required set of attribute updates has been
+                             determined and performed successfully.
+
+  @retval EFI_ACCESS_DENIED  NewFileInfo requests an update to a property
+                             different from the EFI_FILE_READ_ONLY bit in the
+                             Attribute field, but VirtioFsFile is not open for
+                             writing.
+
+  @return                    Error codes propagated from underlying functions.
+**/
+STATIC
+EFI_STATUS
+UpdateAttributes (
+  IN OUT VIRTIO_FS_FILE *VirtioFsFile,
+  IN     EFI_FILE_INFO  *NewFileInfo
+  )
+{
+  VIRTIO_FS                          *VirtioFs;
+  EFI_STATUS                         Status;
+  VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
+  EFI_FILE_INFO                      FileInfo;
+  BOOLEAN                            UpdateFileSize;
+  UINT64                             FileSize;
+  BOOLEAN                            UpdateAtime;
+  BOOLEAN                            UpdateMtime;
+  UINT64                             Atime;
+  UINT64                             Mtime;
+  BOOLEAN                            UpdateMode;
+  UINT32                             Mode;
+
+  VirtioFs = VirtioFsFile->OwnerFs;
+
+  //
+  // Fetch the current attributes first, so we can build the difference between
+  // them and NewFileInfo.
+  //
+  Status = VirtioFsFuseGetAttr (VirtioFs, VirtioFsFile->NodeId, &FuseAttr);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  Status = VirtioFsFuseAttrToEfiFileInfo (&FuseAttr, &FileInfo);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  //
+  // Collect the updates.
+  //
+  if (VirtioFsFile->IsDirectory) {
+    UpdateFileSize = FALSE;
+  } else {
+    VirtioFsGetFuseSizeUpdate (&FileInfo, NewFileInfo, &UpdateFileSize,
+      &FileSize);
+  }
+
+  Status = VirtioFsGetFuseTimeUpdates (&FileInfo, NewFileInfo, &UpdateAtime,
+             &UpdateMtime, &Atime, &Mtime);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = VirtioFsGetFuseModeUpdate (&FileInfo, NewFileInfo, &UpdateMode,
+             &Mode);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // If no attribute updates are necessary, we're done.
+  //
+  if (!UpdateFileSize && !UpdateAtime && !UpdateMtime && !UpdateMode) {
+    return EFI_SUCCESS;
+  }
+  //
+  // If the file is not open for writing, then only Mode may be updated (for
+  // toggling EFI_FILE_READ_ONLY).
+  //
+  if (!VirtioFsFile->IsOpenForWriting &&
+      (UpdateFileSize || UpdateAtime || UpdateMtime)) {
+    return EFI_ACCESS_DENIED;
+  }
+  //
+  // Send the FUSE_SETATTR request now.
+  //
+  Status = VirtioFsFuseSetAttr (
+             VirtioFs,
+             VirtioFsFile->NodeId,
+             UpdateFileSize ? &FileSize : NULL,
+             UpdateAtime    ? &Atime    : NULL,
+             UpdateMtime    ? &Mtime    : NULL,
+             UpdateMode     ? &Mode     : NULL
+             );
+  return Status;
+}
+
 /**
   Process an EFI_FILE_INFO setting request.
 **/
 STATIC
 EFI_STATUS
 SetFileInfo (
   IN EFI_FILE_PROTOCOL *This,
   IN UINTN             BufferSize,
@@ -345,17 +450,17 @@ SetFileInfo (
   //
   Status = Rename (VirtioFsFile, FileInfo->FileName);
   if (EFI_ERROR (Status)) {
     return Status;
   }
   //
   // Update any attributes requested.
   //
-  Status = EFI_UNSUPPORTED;
+  Status = UpdateAttributes (VirtioFsFile, FileInfo);
   //
   // The UEFI spec does not speak about partial failure in
   // EFI_FILE_PROTOCOL.SetInfo(); we won't try to roll back the rename (if
   // there was one) in case the attribute updates fail.
   //
   return Status;
 }
 
-- 
2.19.1.3.g30247aa5d201


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* Re: [Virtio-fs] [edk2 PATCH 06/48] OvmfPkg/VirtioFsDxe: introduce the basic FUSE request/response headers
  2020-12-16 21:10 ` [edk2 PATCH 06/48] OvmfPkg/VirtioFsDxe: introduce the basic FUSE request/response headers Laszlo Ersek
@ 2020-12-17 11:49   ` Dr. David Alan Gilbert
  2020-12-17 13:57     ` Laszlo Ersek
  0 siblings, 1 reply; 65+ messages in thread
From: Dr. David Alan Gilbert @ 2020-12-17 11:49 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: devel, virtio-fs, Jordan Justen, Philippe Mathieu-Daudé,
	Ard Biesheuvel

* Laszlo Ersek (lersek@redhat.com) wrote:
> Introduce the VIRTIO_FS_FUSE_REQUEST and VIRTIO_FS_FUSE_RESPONSE
> structures, which are the common headers for the various FUSE
> request/response structures.
> 
> Introduce the VirtioFsFuseNewRequest() helper function for populating
> VIRTIO_FS_FUSE_REQUEST, from parameters and from a VIRTIO_FS-level request
> counter.
> 
> Introduce the VirtioFsFuseCheckResponse() helper function for verifying
> most FUSE response types that begin with the VIRTIO_FS_FUSE_RESPONSE
> header.
> 
> 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>
> ---
>  OvmfPkg/Include/IndustryStandard/VirtioFs.h |  49 +++++
>  OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |  17 ++
>  OvmfPkg/VirtioFsDxe/DriverBinding.c         |   5 +
>  OvmfPkg/VirtioFsDxe/Helpers.c               | 216 ++++++++++++++++++++
>  4 files changed, 287 insertions(+)
> 
> diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
> index ea7d80d15d0b..521288b03f1c 100644
> --- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
> +++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
> @@ -44,9 +44,58 @@ typedef struct {
>    //
>    // The total number of request virtqueues exposed by the device (i.e.,
>    // excluding the "hiprio" queue).
>    //
>    UINT32 NumReqQueues;
>  } VIRTIO_FS_CONFIG;
>  #pragma pack ()
>  
> +//
> +// FUSE-related definitions follow.
> +//
> +// From virtio-v1.1-cs01-87fa6b5d8155, 5.11 File System Device: "[...] The
> +// driver acts as the FUSE client mounting the file system. The virtio file
> +// system device provides the mechanism for transporting FUSE requests [...]"
> +//
> +// Unfortunately, the documentation of the FUSE wire protocol is lacking. The
> +// Virtio spec (as of this writing) simply defers to
> +// "include/uapi/linux/fuse.h" in the Linux kernel source -- see the reference
> +// in virtio spec file "introduction.tex", at commit 87fa6b5d8155.
> +//
> +// Of course, "include/uapi/linux/fuse.h" is a moving target (the virtio spec
> +// does not specify a particular FUSE interface version). The OvmfPkg code
> +// targets version 7.31, because that's the lowest version that the QEMU
> +// virtio-fs daemon supports at this time -- see QEMU commit 72c42e2d6551
> +// ("virtiofsd: Trim out compatibility code", 2020-01-23).

Fuse has it's own in built protocol versioning and negotiation, and as I
remember it's the version of the header in the Linux kernel that's the
definitive version.  I think it would have also been safe just to take
the latest released kernel version

Dave

> +// Correspondingly, Linux's "include/uapi/linux/fuse.h" is consulted as checked
> +// out at commit (c6ff213fe5b8^) = d78092e4937d ("fuse: fix page dereference
> +// after free", 2020-09-18); that is, right before commit c6ff213fe5b8 ("fuse:
> +// add submount support to <uapi/linux/fuse.h>", 2020-09-18) introduces FUSE
> +// interface version 7.32.
> +//
> +#define VIRTIO_FS_FUSE_MAJOR  7
> +#define VIRTIO_FS_FUSE_MINOR 31
> +
> +#pragma pack (1)
> +//
> +// Request-response headers common to all request types.
> +//
> +typedef struct {
> +  UINT32 Len;
> +  UINT32 Opcode;
> +  UINT64 Unique;
> +  UINT64 NodeId;
> +  UINT32 Uid;
> +  UINT32 Gid;
> +  UINT32 Pid;
> +  UINT32 Padding;
> +} VIRTIO_FS_FUSE_REQUEST;
> +
> +typedef struct {
> +  UINT32 Len;
> +  INT32  Error;
> +  UINT64 Unique;
> +} VIRTIO_FS_FUSE_RESPONSE;
> +#pragma pack ()
> +
>  #endif // VIRTIO_FS_H_
> diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
> index 12acbd6dc359..f7eae9a4b71a 100644
> --- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
> +++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
> @@ -39,16 +39,17 @@ typedef struct {
>    //                              field         init function       init depth
>    //                              -----------   ------------------  ----------
>    UINT64                          Signature; // DriverBindingStart  0
>    VIRTIO_DEVICE_PROTOCOL          *Virtio;   // DriverBindingStart  0
>    VIRTIO_FS_LABEL                 Label;     // VirtioFsInit        1
>    UINT16                          QueueSize; // VirtioFsInit        1
>    VRING                           Ring;      // VirtioRingInit      2
>    VOID                            *RingMap;  // VirtioRingMap       2
> +  UINT64                          RequestId; // DriverBindingStart  0
>    EFI_EVENT                       ExitBoot;  // DriverBindingStart  0
>    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL SimpleFs;  // DriverBindingStart  0
>  } VIRTIO_FS;
>  
>  #define VIRTIO_FS_FROM_SIMPLE_FS(SimpleFsReference) \
>    CR (SimpleFsReference, VIRTIO_FS, SimpleFs, VIRTIO_FS_SIG);
>  
>  //
> @@ -127,16 +128,32 @@ VirtioFsSgListsValidate (
>  
>  EFI_STATUS
>  VirtioFsSgListsSubmit (
>    IN OUT VIRTIO_FS                     *VirtioFs,
>    IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *RequestSgList,
>    IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList OPTIONAL
>    );
>  
> +EFI_STATUS
> +VirtioFsFuseNewRequest (
> +  IN OUT VIRTIO_FS              *VirtioFs,
> +     OUT VIRTIO_FS_FUSE_REQUEST *Request,
> +  IN     UINT32                 RequestSize,
> +  IN     UINT32                 Opcode,
> +  IN     UINT64                 NodeId
> +  );
> +
> +EFI_STATUS
> +VirtioFsFuseCheckResponse (
> +  IN  VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList,
> +  IN  UINT64                        RequestId,
> +  OUT UINTN                         *TailBufferFill
> +  );
> +
>  //
>  // EFI_SIMPLE_FILE_SYSTEM_PROTOCOL member functions for the Virtio Filesystem
>  // driver.
>  //
>  
>  EFI_STATUS
>  EFIAPI
>  VirtioFsOpenVolume (
> diff --git a/OvmfPkg/VirtioFsDxe/DriverBinding.c b/OvmfPkg/VirtioFsDxe/DriverBinding.c
> index b888158a805d..4a2787a50a6e 100644
> --- a/OvmfPkg/VirtioFsDxe/DriverBinding.c
> +++ b/OvmfPkg/VirtioFsDxe/DriverBinding.c
> @@ -79,16 +79,21 @@ VirtioFsBindingStart (
>      goto FreeVirtioFs;
>    }
>  
>    Status = VirtioFsInit (VirtioFs);
>    if (EFI_ERROR (Status)) {
>      goto CloseVirtio;
>    }
>  
> +  //
> +  // Initialize the FUSE request counter.
> +  //
> +  VirtioFs->RequestId = 1;
> +
>    Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
>                    VirtioFsExitBoot, VirtioFs, &VirtioFs->ExitBoot);
>    if (EFI_ERROR (Status)) {
>      goto UninitVirtioFs;
>    }
>  
>    VirtioFs->SimpleFs.Revision   = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
>    VirtioFs->SimpleFs.OpenVolume = VirtioFsOpenVolume;
> diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
> index 88264d4b264c..5bd2dc641f6d 100644
> --- a/OvmfPkg/VirtioFsDxe/Helpers.c
> +++ b/OvmfPkg/VirtioFsDxe/Helpers.c
> @@ -693,8 +693,224 @@ VirtioFsSgListsSubmit (
>        if (!EFI_ERROR (Status) && EFI_ERROR (UnmapStatus)) {
>          Status = UnmapStatus;
>        }
>      }
>    }
>  
>    return Status;
>  }
> +
> +/**
> +  Set up the fields of a new VIRTIO_FS_FUSE_REQUEST object.
> +
> +  The function may only be called after VirtioFsInit() returns successfully and
> +  before VirtioFsUninit() is called.
> +
> +  @param[in,out] VirtioFs The Virtio Filesystem device that the request is
> +                          being prepared for. The "VirtioFs->RequestId" field
> +                          will be copied into "Request->Unique". On output (on
> +                          successful return), "VirtioFs->RequestId" will be
> +                          incremented.
> +
> +  @param[out] Request     The VIRTIO_FS_FUSE_REQUEST object whose fields are to
> +                          be set.
> +
> +  @param[in] RequestSize  The total size of the request, including
> +                          sizeof(VIRTIO_FS_FUSE_REQUEST).
> +
> +  @param[in] Opcode       The VIRTIO_FS_FUSE_OPCODE that identifies the command
> +                          to send.
> +
> +  @param[in] NodeId       The inode number of the file that the request refers
> +                          to.
> +
> +  @retval EFI_INVALID_PARAMETER  RequestSize is smaller than
> +                                 sizeof(VIRTIO_FS_FUSE_REQUEST).
> +
> +  @retval EFI_OUT_OF_RESOURCES   "VirtioFs->RequestId" is MAX_UINT64, and can
> +                                 be incremented no more.
> +
> +  @retval EFI_SUCCESS            Request has been populated,
> +                                 "VirtioFs->RequestId" has been incremented.
> +**/
> +EFI_STATUS
> +VirtioFsFuseNewRequest (
> +  IN OUT VIRTIO_FS              *VirtioFs,
> +     OUT VIRTIO_FS_FUSE_REQUEST *Request,
> +  IN     UINT32                 RequestSize,
> +  IN     UINT32                 Opcode,
> +  IN     UINT64                 NodeId
> +  )
> +{
> +  if (RequestSize < sizeof *Request) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (VirtioFs->RequestId == MAX_UINT64) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  Request->Len     = RequestSize;
> +  Request->Opcode  = Opcode;
> +  Request->Unique  = VirtioFs->RequestId++;
> +  Request->NodeId  = NodeId;
> +  Request->Uid     = 0;
> +  Request->Gid     = 0;
> +  Request->Pid     = 1;
> +  Request->Padding = 0;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Check the common FUSE response format.
> +
> +  The first buffer in the response scatter-gather list is assumed a
> +  VIRTIO_FS_FUSE_RESPONSE structure. Subsequent response buffers, if any, up to
> +  and excluding the last one, are assumed fixed size. The last response buffer
> +  may or may not be fixed size, as specified by the caller.
> +
> +  This function may only be called after VirtioFsSgListsSubmit() returns
> +  successfully.
> +
> +  @param[in] ResponseSgList   The scatter-gather list that describes the
> +                              response part of the exchange -- the buffers that
> +                              the Virtio Filesystem device filled in during the
> +                              virtio transfer.
> +
> +  @param[in] RequestId        The request identifier to which the response is
> +                              expected to belong.
> +
> +  @param[out] TailBufferFill  If NULL, then the last buffer in ResponseSgList
> +                              is considered fixed size. Otherwise, the last
> +                              buffer is considered variable size, and on
> +                              successful return, TailBufferFill reports the
> +                              number of bytes in the last buffer.
> +
> +  @retval EFI_INVALID_PARAMETER  TailBufferFill is not NULL (i.e., the last
> +                                 buffer is considered variable size), and
> +                                 ResponseSgList->NumVec is 1.
> +
> +  @retval EFI_INVALID_PARAMETER  The allocated size of the first buffer does
> +                                 not match sizeof(VIRTIO_FS_FUSE_RESPONSE).
> +
> +  @retval EFI_PROTOCOL_ERROR     The VIRTIO_FS_FUSE_RESPONSE structure in the
> +                                 first buffer has not been fully populated.
> +
> +  @retval EFI_PROTOCOL_ERROR     "VIRTIO_FS_FUSE_RESPONSE.Len" in the first
> +                                 buffer does not equal the sum of the
> +                                 individual buffer sizes (as populated).
> +
> +  @retval EFI_PROTOCOL_ERROR     "VIRTIO_FS_FUSE_RESPONSE.Unique" in the first
> +                                 buffer does not equal RequestId.
> +
> +  @retval EFI_PROTOCOL_ERROR     "VIRTIO_FS_FUSE_RESPONSE.Error" in the first
> +                                 buffer is zero, but a subsequent fixed size
> +                                 buffer has not been fully populated.
> +
> +  @retval EFI_DEVICE_ERROR       "VIRTIO_FS_FUSE_RESPONSE.Error" in the first
> +                                 buffer is nonzero. The caller may investigate
> +                                 "VIRTIO_FS_FUSE_RESPONSE.Error". Note that the
> +                                 completeness of the subsequent fixed size
> +                                 buffers is not verified in this case.
> +
> +  @retval EFI_SUCCESS            Verification successful.
> +**/
> +EFI_STATUS
> +VirtioFsFuseCheckResponse (
> +  IN  VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList,
> +  IN  UINT64                        RequestId,
> +  OUT UINTN                         *TailBufferFill
> +  )
> +{
> +  UINTN                   NumFixedSizeVec;
> +  VIRTIO_FS_FUSE_RESPONSE *CommonResp;
> +  UINT32                  TotalTransferred;
> +  UINTN                   Idx;
> +
> +  //
> +  // Ensured by VirtioFsSgListsValidate().
> +  //
> +  ASSERT (ResponseSgList->NumVec > 0);
> +
> +  if (TailBufferFill == NULL) {
> +    //
> +    // All buffers are considered fixed size.
> +    //
> +    NumFixedSizeVec = ResponseSgList->NumVec;
> +  } else {
> +    //
> +    // If the last buffer is variable size, then we need that buffer to be
> +    // different from the first buffer, which is considered a
> +    // VIRTIO_FS_FUSE_RESPONSE (fixed size) structure.
> +    //
> +    if (ResponseSgList->NumVec == 1) {
> +      return EFI_INVALID_PARAMETER;
> +    }
> +    NumFixedSizeVec = ResponseSgList->NumVec - 1;
> +  }
> +
> +  //
> +  // The first buffer is supposed to carry a (fully populated)
> +  // VIRTIO_FS_FUSE_RESPONSE structure.
> +  //
> +  if (ResponseSgList->IoVec[0].Size != sizeof *CommonResp) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +  if (ResponseSgList->IoVec[0].Transferred != ResponseSgList->IoVec[0].Size) {
> +    return EFI_PROTOCOL_ERROR;
> +  }
> +
> +  //
> +  // FUSE must report the same number of bytes, written by the Virtio
> +  // Filesystem device, as the virtio transport does.
> +  //
> +  CommonResp = ResponseSgList->IoVec[0].Buffer;
> +  TotalTransferred = 0;
> +  for (Idx = 0; Idx < ResponseSgList->NumVec; Idx++) {
> +    //
> +    // Integer overflow and truncation are not possible, based on
> +    // VirtioFsSgListsValidate() and VirtioFsSgListsSubmit().
> +    //
> +    TotalTransferred += (UINT32)ResponseSgList->IoVec[Idx].Transferred;
> +  }
> +  if (CommonResp->Len != TotalTransferred) {
> +    return EFI_PROTOCOL_ERROR;
> +  }
> +
> +  //
> +  // Enforce that FUSE match our request ID in the response.
> +  //
> +  if (CommonResp->Unique != RequestId) {
> +    return EFI_PROTOCOL_ERROR;
> +  }
> +
> +  //
> +  // If there is an explicit error report, skip checking the transfer
> +  // counts for the rest of the fixed size buffers.
> +  //
> +  if (CommonResp->Error != 0) {
> +    return EFI_DEVICE_ERROR;
> +  }
> +
> +  //
> +  // There was no error reported, so we require that the Virtio Filesystem
> +  // device populate all fixed size buffers. We checked this for the very first
> +  // buffer above; let's check the rest (if any).
> +  //
> +  ASSERT (NumFixedSizeVec >= 1);
> +  for (Idx = 1; Idx < NumFixedSizeVec; Idx++) {
> +    if (ResponseSgList->IoVec[Idx].Transferred !=
> +        ResponseSgList->IoVec[Idx].Size) {
> +      return EFI_PROTOCOL_ERROR;
> +    }
> +  }
> +
> +  //
> +  // If the last buffer is considered variable size, report its filled size.
> +  //
> +  if (TailBufferFill != NULL) {
> +    *TailBufferFill = ResponseSgList->IoVec[NumFixedSizeVec].Transferred;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> -- 
> 2.19.1.3.g30247aa5d201
> 
> 
> 
> _______________________________________________
> Virtio-fs mailing list
> Virtio-fs@redhat.com
> https://www.redhat.com/mailman/listinfo/virtio-fs
-- 
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Virtio-fs] [edk2 PATCH 06/48] OvmfPkg/VirtioFsDxe: introduce the basic FUSE request/response headers
  2020-12-17 11:49   ` [Virtio-fs] " Dr. David Alan Gilbert
@ 2020-12-17 13:57     ` Laszlo Ersek
  2020-12-17 14:06       ` Dr. David Alan Gilbert
  2020-12-17 14:32       ` Laszlo Ersek
  0 siblings, 2 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-17 13:57 UTC (permalink / raw)
  To: Dr. David Alan Gilbert
  Cc: devel, virtio-fs, Jordan Justen, Philippe Mathieu-Daudé,
	Ard Biesheuvel

On 12/17/20 12:49, Dr. David Alan Gilbert wrote:
> * Laszlo Ersek (lersek@redhat.com) wrote:
>> Introduce the VIRTIO_FS_FUSE_REQUEST and VIRTIO_FS_FUSE_RESPONSE
>> structures, which are the common headers for the various FUSE
>> request/response structures.
>>
>> Introduce the VirtioFsFuseNewRequest() helper function for populating
>> VIRTIO_FS_FUSE_REQUEST, from parameters and from a VIRTIO_FS-level request
>> counter.
>>
>> Introduce the VirtioFsFuseCheckResponse() helper function for verifying
>> most FUSE response types that begin with the VIRTIO_FS_FUSE_RESPONSE
>> header.
>>
>> 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>
>> ---
>>  OvmfPkg/Include/IndustryStandard/VirtioFs.h |  49 +++++
>>  OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |  17 ++
>>  OvmfPkg/VirtioFsDxe/DriverBinding.c         |   5 +
>>  OvmfPkg/VirtioFsDxe/Helpers.c               | 216 ++++++++++++++++++++
>>  4 files changed, 287 insertions(+)
>>
>> diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
>> index ea7d80d15d0b..521288b03f1c 100644
>> --- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
>> +++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
>> @@ -44,9 +44,58 @@ typedef struct {
>>    //
>>    // The total number of request virtqueues exposed by the device (i.e.,
>>    // excluding the "hiprio" queue).
>>    //
>>    UINT32 NumReqQueues;
>>  } VIRTIO_FS_CONFIG;
>>  #pragma pack ()
>>  
>> +//
>> +// FUSE-related definitions follow.
>> +//
>> +// From virtio-v1.1-cs01-87fa6b5d8155, 5.11 File System Device: "[...] The
>> +// driver acts as the FUSE client mounting the file system. The virtio file
>> +// system device provides the mechanism for transporting FUSE requests [...]"
>> +//
>> +// Unfortunately, the documentation of the FUSE wire protocol is lacking. The
>> +// Virtio spec (as of this writing) simply defers to
>> +// "include/uapi/linux/fuse.h" in the Linux kernel source -- see the reference
>> +// in virtio spec file "introduction.tex", at commit 87fa6b5d8155.
>> +//
>> +// Of course, "include/uapi/linux/fuse.h" is a moving target (the virtio spec
>> +// does not specify a particular FUSE interface version). The OvmfPkg code
>> +// targets version 7.31, because that's the lowest version that the QEMU
>> +// virtio-fs daemon supports at this time -- see QEMU commit 72c42e2d6551
>> +// ("virtiofsd: Trim out compatibility code", 2020-01-23).
> 
> Fuse has it's own in built protocol versioning and negotiation, and as I
> remember it's the version of the header in the Linux kernel that's the
> definitive version.  I think it would have also been safe just to take
> the latest released kernel version

7.32 (kernel commit c6ff213fe5b8) introduces "submount support", which
does not seem relevant. The strategy we follow in
"OvmfPkg/Include/IndustryStandard" is: never include or convert header
files from other projects whole-sale; always evaluate every structure
and macro one by one for necessity, and if a definition is really needed
for the driver being implemented, add that definition manually, and
natively to the edk2 coding style.

This approach has caused zero friction or extra maintenance work over
the years, and it allows for a native look & feel. (It's a different
question what people think of edk2's "native" look and feel :))

A counter-example is the Xen headers in
"OvmfPkg/Include/IndustryStandard/Xen", which were mass-imported and
then fixed up separately for edk2 compatibility at some point -- much to
my dismay. They feel totally foreign in edk2 and they've never stopped
being a thorn in my side. It's not just the headers of course: when you
*use* those macros and typedefs in the actual driver code, it shows.

So, as long as it's up to me, OVMF never "just takes" but "adds from
zero", and I do stick with the absolute minimum needed.

Thanks,
Laszlo

> 
> Dave
> 
>> +// Correspondingly, Linux's "include/uapi/linux/fuse.h" is consulted as checked
>> +// out at commit (c6ff213fe5b8^) = d78092e4937d ("fuse: fix page dereference
>> +// after free", 2020-09-18); that is, right before commit c6ff213fe5b8 ("fuse:
>> +// add submount support to <uapi/linux/fuse.h>", 2020-09-18) introduces FUSE
>> +// interface version 7.32.
>> +//
>> +#define VIRTIO_FS_FUSE_MAJOR  7
>> +#define VIRTIO_FS_FUSE_MINOR 31
>> +
>> +#pragma pack (1)
>> +//
>> +// Request-response headers common to all request types.
>> +//
>> +typedef struct {
>> +  UINT32 Len;
>> +  UINT32 Opcode;
>> +  UINT64 Unique;
>> +  UINT64 NodeId;
>> +  UINT32 Uid;
>> +  UINT32 Gid;
>> +  UINT32 Pid;
>> +  UINT32 Padding;
>> +} VIRTIO_FS_FUSE_REQUEST;
>> +
>> +typedef struct {
>> +  UINT32 Len;
>> +  INT32  Error;
>> +  UINT64 Unique;
>> +} VIRTIO_FS_FUSE_RESPONSE;
>> +#pragma pack ()
>> +
>>  #endif // VIRTIO_FS_H_
>> diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
>> index 12acbd6dc359..f7eae9a4b71a 100644
>> --- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
>> +++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
>> @@ -39,16 +39,17 @@ typedef struct {
>>    //                              field         init function       init depth
>>    //                              -----------   ------------------  ----------
>>    UINT64                          Signature; // DriverBindingStart  0
>>    VIRTIO_DEVICE_PROTOCOL          *Virtio;   // DriverBindingStart  0
>>    VIRTIO_FS_LABEL                 Label;     // VirtioFsInit        1
>>    UINT16                          QueueSize; // VirtioFsInit        1
>>    VRING                           Ring;      // VirtioRingInit      2
>>    VOID                            *RingMap;  // VirtioRingMap       2
>> +  UINT64                          RequestId; // DriverBindingStart  0
>>    EFI_EVENT                       ExitBoot;  // DriverBindingStart  0
>>    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL SimpleFs;  // DriverBindingStart  0
>>  } VIRTIO_FS;
>>  
>>  #define VIRTIO_FS_FROM_SIMPLE_FS(SimpleFsReference) \
>>    CR (SimpleFsReference, VIRTIO_FS, SimpleFs, VIRTIO_FS_SIG);
>>  
>>  //
>> @@ -127,16 +128,32 @@ VirtioFsSgListsValidate (
>>  
>>  EFI_STATUS
>>  VirtioFsSgListsSubmit (
>>    IN OUT VIRTIO_FS                     *VirtioFs,
>>    IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *RequestSgList,
>>    IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList OPTIONAL
>>    );
>>  
>> +EFI_STATUS
>> +VirtioFsFuseNewRequest (
>> +  IN OUT VIRTIO_FS              *VirtioFs,
>> +     OUT VIRTIO_FS_FUSE_REQUEST *Request,
>> +  IN     UINT32                 RequestSize,
>> +  IN     UINT32                 Opcode,
>> +  IN     UINT64                 NodeId
>> +  );
>> +
>> +EFI_STATUS
>> +VirtioFsFuseCheckResponse (
>> +  IN  VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList,
>> +  IN  UINT64                        RequestId,
>> +  OUT UINTN                         *TailBufferFill
>> +  );
>> +
>>  //
>>  // EFI_SIMPLE_FILE_SYSTEM_PROTOCOL member functions for the Virtio Filesystem
>>  // driver.
>>  //
>>  
>>  EFI_STATUS
>>  EFIAPI
>>  VirtioFsOpenVolume (
>> diff --git a/OvmfPkg/VirtioFsDxe/DriverBinding.c b/OvmfPkg/VirtioFsDxe/DriverBinding.c
>> index b888158a805d..4a2787a50a6e 100644
>> --- a/OvmfPkg/VirtioFsDxe/DriverBinding.c
>> +++ b/OvmfPkg/VirtioFsDxe/DriverBinding.c
>> @@ -79,16 +79,21 @@ VirtioFsBindingStart (
>>      goto FreeVirtioFs;
>>    }
>>  
>>    Status = VirtioFsInit (VirtioFs);
>>    if (EFI_ERROR (Status)) {
>>      goto CloseVirtio;
>>    }
>>  
>> +  //
>> +  // Initialize the FUSE request counter.
>> +  //
>> +  VirtioFs->RequestId = 1;
>> +
>>    Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
>>                    VirtioFsExitBoot, VirtioFs, &VirtioFs->ExitBoot);
>>    if (EFI_ERROR (Status)) {
>>      goto UninitVirtioFs;
>>    }
>>  
>>    VirtioFs->SimpleFs.Revision   = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
>>    VirtioFs->SimpleFs.OpenVolume = VirtioFsOpenVolume;
>> diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
>> index 88264d4b264c..5bd2dc641f6d 100644
>> --- a/OvmfPkg/VirtioFsDxe/Helpers.c
>> +++ b/OvmfPkg/VirtioFsDxe/Helpers.c
>> @@ -693,8 +693,224 @@ VirtioFsSgListsSubmit (
>>        if (!EFI_ERROR (Status) && EFI_ERROR (UnmapStatus)) {
>>          Status = UnmapStatus;
>>        }
>>      }
>>    }
>>  
>>    return Status;
>>  }
>> +
>> +/**
>> +  Set up the fields of a new VIRTIO_FS_FUSE_REQUEST object.
>> +
>> +  The function may only be called after VirtioFsInit() returns successfully and
>> +  before VirtioFsUninit() is called.
>> +
>> +  @param[in,out] VirtioFs The Virtio Filesystem device that the request is
>> +                          being prepared for. The "VirtioFs->RequestId" field
>> +                          will be copied into "Request->Unique". On output (on
>> +                          successful return), "VirtioFs->RequestId" will be
>> +                          incremented.
>> +
>> +  @param[out] Request     The VIRTIO_FS_FUSE_REQUEST object whose fields are to
>> +                          be set.
>> +
>> +  @param[in] RequestSize  The total size of the request, including
>> +                          sizeof(VIRTIO_FS_FUSE_REQUEST).
>> +
>> +  @param[in] Opcode       The VIRTIO_FS_FUSE_OPCODE that identifies the command
>> +                          to send.
>> +
>> +  @param[in] NodeId       The inode number of the file that the request refers
>> +                          to.
>> +
>> +  @retval EFI_INVALID_PARAMETER  RequestSize is smaller than
>> +                                 sizeof(VIRTIO_FS_FUSE_REQUEST).
>> +
>> +  @retval EFI_OUT_OF_RESOURCES   "VirtioFs->RequestId" is MAX_UINT64, and can
>> +                                 be incremented no more.
>> +
>> +  @retval EFI_SUCCESS            Request has been populated,
>> +                                 "VirtioFs->RequestId" has been incremented.
>> +**/
>> +EFI_STATUS
>> +VirtioFsFuseNewRequest (
>> +  IN OUT VIRTIO_FS              *VirtioFs,
>> +     OUT VIRTIO_FS_FUSE_REQUEST *Request,
>> +  IN     UINT32                 RequestSize,
>> +  IN     UINT32                 Opcode,
>> +  IN     UINT64                 NodeId
>> +  )
>> +{
>> +  if (RequestSize < sizeof *Request) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  if (VirtioFs->RequestId == MAX_UINT64) {
>> +    return EFI_OUT_OF_RESOURCES;
>> +  }
>> +
>> +  Request->Len     = RequestSize;
>> +  Request->Opcode  = Opcode;
>> +  Request->Unique  = VirtioFs->RequestId++;
>> +  Request->NodeId  = NodeId;
>> +  Request->Uid     = 0;
>> +  Request->Gid     = 0;
>> +  Request->Pid     = 1;
>> +  Request->Padding = 0;
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Check the common FUSE response format.
>> +
>> +  The first buffer in the response scatter-gather list is assumed a
>> +  VIRTIO_FS_FUSE_RESPONSE structure. Subsequent response buffers, if any, up to
>> +  and excluding the last one, are assumed fixed size. The last response buffer
>> +  may or may not be fixed size, as specified by the caller.
>> +
>> +  This function may only be called after VirtioFsSgListsSubmit() returns
>> +  successfully.
>> +
>> +  @param[in] ResponseSgList   The scatter-gather list that describes the
>> +                              response part of the exchange -- the buffers that
>> +                              the Virtio Filesystem device filled in during the
>> +                              virtio transfer.
>> +
>> +  @param[in] RequestId        The request identifier to which the response is
>> +                              expected to belong.
>> +
>> +  @param[out] TailBufferFill  If NULL, then the last buffer in ResponseSgList
>> +                              is considered fixed size. Otherwise, the last
>> +                              buffer is considered variable size, and on
>> +                              successful return, TailBufferFill reports the
>> +                              number of bytes in the last buffer.
>> +
>> +  @retval EFI_INVALID_PARAMETER  TailBufferFill is not NULL (i.e., the last
>> +                                 buffer is considered variable size), and
>> +                                 ResponseSgList->NumVec is 1.
>> +
>> +  @retval EFI_INVALID_PARAMETER  The allocated size of the first buffer does
>> +                                 not match sizeof(VIRTIO_FS_FUSE_RESPONSE).
>> +
>> +  @retval EFI_PROTOCOL_ERROR     The VIRTIO_FS_FUSE_RESPONSE structure in the
>> +                                 first buffer has not been fully populated.
>> +
>> +  @retval EFI_PROTOCOL_ERROR     "VIRTIO_FS_FUSE_RESPONSE.Len" in the first
>> +                                 buffer does not equal the sum of the
>> +                                 individual buffer sizes (as populated).
>> +
>> +  @retval EFI_PROTOCOL_ERROR     "VIRTIO_FS_FUSE_RESPONSE.Unique" in the first
>> +                                 buffer does not equal RequestId.
>> +
>> +  @retval EFI_PROTOCOL_ERROR     "VIRTIO_FS_FUSE_RESPONSE.Error" in the first
>> +                                 buffer is zero, but a subsequent fixed size
>> +                                 buffer has not been fully populated.
>> +
>> +  @retval EFI_DEVICE_ERROR       "VIRTIO_FS_FUSE_RESPONSE.Error" in the first
>> +                                 buffer is nonzero. The caller may investigate
>> +                                 "VIRTIO_FS_FUSE_RESPONSE.Error". Note that the
>> +                                 completeness of the subsequent fixed size
>> +                                 buffers is not verified in this case.
>> +
>> +  @retval EFI_SUCCESS            Verification successful.
>> +**/
>> +EFI_STATUS
>> +VirtioFsFuseCheckResponse (
>> +  IN  VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList,
>> +  IN  UINT64                        RequestId,
>> +  OUT UINTN                         *TailBufferFill
>> +  )
>> +{
>> +  UINTN                   NumFixedSizeVec;
>> +  VIRTIO_FS_FUSE_RESPONSE *CommonResp;
>> +  UINT32                  TotalTransferred;
>> +  UINTN                   Idx;
>> +
>> +  //
>> +  // Ensured by VirtioFsSgListsValidate().
>> +  //
>> +  ASSERT (ResponseSgList->NumVec > 0);
>> +
>> +  if (TailBufferFill == NULL) {
>> +    //
>> +    // All buffers are considered fixed size.
>> +    //
>> +    NumFixedSizeVec = ResponseSgList->NumVec;
>> +  } else {
>> +    //
>> +    // If the last buffer is variable size, then we need that buffer to be
>> +    // different from the first buffer, which is considered a
>> +    // VIRTIO_FS_FUSE_RESPONSE (fixed size) structure.
>> +    //
>> +    if (ResponseSgList->NumVec == 1) {
>> +      return EFI_INVALID_PARAMETER;
>> +    }
>> +    NumFixedSizeVec = ResponseSgList->NumVec - 1;
>> +  }
>> +
>> +  //
>> +  // The first buffer is supposed to carry a (fully populated)
>> +  // VIRTIO_FS_FUSE_RESPONSE structure.
>> +  //
>> +  if (ResponseSgList->IoVec[0].Size != sizeof *CommonResp) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +  if (ResponseSgList->IoVec[0].Transferred != ResponseSgList->IoVec[0].Size) {
>> +    return EFI_PROTOCOL_ERROR;
>> +  }
>> +
>> +  //
>> +  // FUSE must report the same number of bytes, written by the Virtio
>> +  // Filesystem device, as the virtio transport does.
>> +  //
>> +  CommonResp = ResponseSgList->IoVec[0].Buffer;
>> +  TotalTransferred = 0;
>> +  for (Idx = 0; Idx < ResponseSgList->NumVec; Idx++) {
>> +    //
>> +    // Integer overflow and truncation are not possible, based on
>> +    // VirtioFsSgListsValidate() and VirtioFsSgListsSubmit().
>> +    //
>> +    TotalTransferred += (UINT32)ResponseSgList->IoVec[Idx].Transferred;
>> +  }
>> +  if (CommonResp->Len != TotalTransferred) {
>> +    return EFI_PROTOCOL_ERROR;
>> +  }
>> +
>> +  //
>> +  // Enforce that FUSE match our request ID in the response.
>> +  //
>> +  if (CommonResp->Unique != RequestId) {
>> +    return EFI_PROTOCOL_ERROR;
>> +  }
>> +
>> +  //
>> +  // If there is an explicit error report, skip checking the transfer
>> +  // counts for the rest of the fixed size buffers.
>> +  //
>> +  if (CommonResp->Error != 0) {
>> +    return EFI_DEVICE_ERROR;
>> +  }
>> +
>> +  //
>> +  // There was no error reported, so we require that the Virtio Filesystem
>> +  // device populate all fixed size buffers. We checked this for the very first
>> +  // buffer above; let's check the rest (if any).
>> +  //
>> +  ASSERT (NumFixedSizeVec >= 1);
>> +  for (Idx = 1; Idx < NumFixedSizeVec; Idx++) {
>> +    if (ResponseSgList->IoVec[Idx].Transferred !=
>> +        ResponseSgList->IoVec[Idx].Size) {
>> +      return EFI_PROTOCOL_ERROR;
>> +    }
>> +  }
>> +
>> +  //
>> +  // If the last buffer is considered variable size, report its filled size.
>> +  //
>> +  if (TailBufferFill != NULL) {
>> +    *TailBufferFill = ResponseSgList->IoVec[NumFixedSizeVec].Transferred;
>> +  }
>> +
>> +  return EFI_SUCCESS;
>> +}
>> -- 
>> 2.19.1.3.g30247aa5d201
>>
>>
>>
>> _______________________________________________
>> Virtio-fs mailing list
>> Virtio-fs@redhat.com
>> https://www.redhat.com/mailman/listinfo/virtio-fs


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Virtio-fs] [edk2 PATCH 06/48] OvmfPkg/VirtioFsDxe: introduce the basic FUSE request/response headers
  2020-12-17 13:57     ` Laszlo Ersek
@ 2020-12-17 14:06       ` Dr. David Alan Gilbert
  2020-12-17 14:32       ` Laszlo Ersek
  1 sibling, 0 replies; 65+ messages in thread
From: Dr. David Alan Gilbert @ 2020-12-17 14:06 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: devel, virtio-fs, Jordan Justen, Philippe Mathieu-Daudé,
	Ard Biesheuvel

* Laszlo Ersek (lersek@redhat.com) wrote:
> On 12/17/20 12:49, Dr. David Alan Gilbert wrote:
> > * Laszlo Ersek (lersek@redhat.com) wrote:
> >> Introduce the VIRTIO_FS_FUSE_REQUEST and VIRTIO_FS_FUSE_RESPONSE
> >> structures, which are the common headers for the various FUSE
> >> request/response structures.
> >>
> >> Introduce the VirtioFsFuseNewRequest() helper function for populating
> >> VIRTIO_FS_FUSE_REQUEST, from parameters and from a VIRTIO_FS-level request
> >> counter.
> >>
> >> Introduce the VirtioFsFuseCheckResponse() helper function for verifying
> >> most FUSE response types that begin with the VIRTIO_FS_FUSE_RESPONSE
> >> header.
> >>
> >> 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>
> >> ---
> >>  OvmfPkg/Include/IndustryStandard/VirtioFs.h |  49 +++++
> >>  OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |  17 ++
> >>  OvmfPkg/VirtioFsDxe/DriverBinding.c         |   5 +
> >>  OvmfPkg/VirtioFsDxe/Helpers.c               | 216 ++++++++++++++++++++
> >>  4 files changed, 287 insertions(+)
> >>
> >> diff --git a/OvmfPkg/Include/IndustryStandard/VirtioFs.h b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
> >> index ea7d80d15d0b..521288b03f1c 100644
> >> --- a/OvmfPkg/Include/IndustryStandard/VirtioFs.h
> >> +++ b/OvmfPkg/Include/IndustryStandard/VirtioFs.h
> >> @@ -44,9 +44,58 @@ typedef struct {
> >>    //
> >>    // The total number of request virtqueues exposed by the device (i.e.,
> >>    // excluding the "hiprio" queue).
> >>    //
> >>    UINT32 NumReqQueues;
> >>  } VIRTIO_FS_CONFIG;
> >>  #pragma pack ()
> >>  
> >> +//
> >> +// FUSE-related definitions follow.
> >> +//
> >> +// From virtio-v1.1-cs01-87fa6b5d8155, 5.11 File System Device: "[...] The
> >> +// driver acts as the FUSE client mounting the file system. The virtio file
> >> +// system device provides the mechanism for transporting FUSE requests [...]"
> >> +//
> >> +// Unfortunately, the documentation of the FUSE wire protocol is lacking. The
> >> +// Virtio spec (as of this writing) simply defers to
> >> +// "include/uapi/linux/fuse.h" in the Linux kernel source -- see the reference
> >> +// in virtio spec file "introduction.tex", at commit 87fa6b5d8155.
> >> +//
> >> +// Of course, "include/uapi/linux/fuse.h" is a moving target (the virtio spec
> >> +// does not specify a particular FUSE interface version). The OvmfPkg code
> >> +// targets version 7.31, because that's the lowest version that the QEMU
> >> +// virtio-fs daemon supports at this time -- see QEMU commit 72c42e2d6551
> >> +// ("virtiofsd: Trim out compatibility code", 2020-01-23).
> > 
> > Fuse has it's own in built protocol versioning and negotiation, and as I
> > remember it's the version of the header in the Linux kernel that's the
> > definitive version.  I think it would have also been safe just to take
> > the latest released kernel version
> 
> 7.32 (kernel commit c6ff213fe5b8) introduces "submount support", which
> does not seem relevant. The strategy we follow in
> "OvmfPkg/Include/IndustryStandard" is: never include or convert header
> files from other projects whole-sale; always evaluate every structure
> and macro one by one for necessity, and if a definition is really needed
> for the driver being implemented, add that definition manually, and
> natively to the edk2 coding style.

The 'submount support' is probably something you don't need; note that
you may end up with multiple files with the same inode number
(if the host fs has submounts).

> This approach has caused zero friction or extra maintenance work over
> the years, and it allows for a native look & feel. (It's a different
> question what people think of edk2's "native" look and feel :))
> 
> A counter-example is the Xen headers in
> "OvmfPkg/Include/IndustryStandard/Xen", which were mass-imported and
> then fixed up separately for edk2 compatibility at some point -- much to
> my dismay. They feel totally foreign in edk2 and they've never stopped
> being a thorn in my side. It's not just the headers of course: when you
> *use* those macros and typedefs in the actual driver code, it shows.
> 
> So, as long as it's up to me, OVMF never "just takes" but "adds from
> zero", and I do stick with the absolute minimum needed.

Yep that's fine.

Dave

> Thanks,
> Laszlo
> 
> > 
> > Dave
> > 
> >> +// Correspondingly, Linux's "include/uapi/linux/fuse.h" is consulted as checked
> >> +// out at commit (c6ff213fe5b8^) = d78092e4937d ("fuse: fix page dereference
> >> +// after free", 2020-09-18); that is, right before commit c6ff213fe5b8 ("fuse:
> >> +// add submount support to <uapi/linux/fuse.h>", 2020-09-18) introduces FUSE
> >> +// interface version 7.32.
> >> +//
> >> +#define VIRTIO_FS_FUSE_MAJOR  7
> >> +#define VIRTIO_FS_FUSE_MINOR 31
> >> +
> >> +#pragma pack (1)
> >> +//
> >> +// Request-response headers common to all request types.
> >> +//
> >> +typedef struct {
> >> +  UINT32 Len;
> >> +  UINT32 Opcode;
> >> +  UINT64 Unique;
> >> +  UINT64 NodeId;
> >> +  UINT32 Uid;
> >> +  UINT32 Gid;
> >> +  UINT32 Pid;
> >> +  UINT32 Padding;
> >> +} VIRTIO_FS_FUSE_REQUEST;
> >> +
> >> +typedef struct {
> >> +  UINT32 Len;
> >> +  INT32  Error;
> >> +  UINT64 Unique;
> >> +} VIRTIO_FS_FUSE_RESPONSE;
> >> +#pragma pack ()
> >> +
> >>  #endif // VIRTIO_FS_H_
> >> diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
> >> index 12acbd6dc359..f7eae9a4b71a 100644
> >> --- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
> >> +++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
> >> @@ -39,16 +39,17 @@ typedef struct {
> >>    //                              field         init function       init depth
> >>    //                              -----------   ------------------  ----------
> >>    UINT64                          Signature; // DriverBindingStart  0
> >>    VIRTIO_DEVICE_PROTOCOL          *Virtio;   // DriverBindingStart  0
> >>    VIRTIO_FS_LABEL                 Label;     // VirtioFsInit        1
> >>    UINT16                          QueueSize; // VirtioFsInit        1
> >>    VRING                           Ring;      // VirtioRingInit      2
> >>    VOID                            *RingMap;  // VirtioRingMap       2
> >> +  UINT64                          RequestId; // DriverBindingStart  0
> >>    EFI_EVENT                       ExitBoot;  // DriverBindingStart  0
> >>    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL SimpleFs;  // DriverBindingStart  0
> >>  } VIRTIO_FS;
> >>  
> >>  #define VIRTIO_FS_FROM_SIMPLE_FS(SimpleFsReference) \
> >>    CR (SimpleFsReference, VIRTIO_FS, SimpleFs, VIRTIO_FS_SIG);
> >>  
> >>  //
> >> @@ -127,16 +128,32 @@ VirtioFsSgListsValidate (
> >>  
> >>  EFI_STATUS
> >>  VirtioFsSgListsSubmit (
> >>    IN OUT VIRTIO_FS                     *VirtioFs,
> >>    IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *RequestSgList,
> >>    IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList OPTIONAL
> >>    );
> >>  
> >> +EFI_STATUS
> >> +VirtioFsFuseNewRequest (
> >> +  IN OUT VIRTIO_FS              *VirtioFs,
> >> +     OUT VIRTIO_FS_FUSE_REQUEST *Request,
> >> +  IN     UINT32                 RequestSize,
> >> +  IN     UINT32                 Opcode,
> >> +  IN     UINT64                 NodeId
> >> +  );
> >> +
> >> +EFI_STATUS
> >> +VirtioFsFuseCheckResponse (
> >> +  IN  VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList,
> >> +  IN  UINT64                        RequestId,
> >> +  OUT UINTN                         *TailBufferFill
> >> +  );
> >> +
> >>  //
> >>  // EFI_SIMPLE_FILE_SYSTEM_PROTOCOL member functions for the Virtio Filesystem
> >>  // driver.
> >>  //
> >>  
> >>  EFI_STATUS
> >>  EFIAPI
> >>  VirtioFsOpenVolume (
> >> diff --git a/OvmfPkg/VirtioFsDxe/DriverBinding.c b/OvmfPkg/VirtioFsDxe/DriverBinding.c
> >> index b888158a805d..4a2787a50a6e 100644
> >> --- a/OvmfPkg/VirtioFsDxe/DriverBinding.c
> >> +++ b/OvmfPkg/VirtioFsDxe/DriverBinding.c
> >> @@ -79,16 +79,21 @@ VirtioFsBindingStart (
> >>      goto FreeVirtioFs;
> >>    }
> >>  
> >>    Status = VirtioFsInit (VirtioFs);
> >>    if (EFI_ERROR (Status)) {
> >>      goto CloseVirtio;
> >>    }
> >>  
> >> +  //
> >> +  // Initialize the FUSE request counter.
> >> +  //
> >> +  VirtioFs->RequestId = 1;
> >> +
> >>    Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
> >>                    VirtioFsExitBoot, VirtioFs, &VirtioFs->ExitBoot);
> >>    if (EFI_ERROR (Status)) {
> >>      goto UninitVirtioFs;
> >>    }
> >>  
> >>    VirtioFs->SimpleFs.Revision   = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
> >>    VirtioFs->SimpleFs.OpenVolume = VirtioFsOpenVolume;
> >> diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
> >> index 88264d4b264c..5bd2dc641f6d 100644
> >> --- a/OvmfPkg/VirtioFsDxe/Helpers.c
> >> +++ b/OvmfPkg/VirtioFsDxe/Helpers.c
> >> @@ -693,8 +693,224 @@ VirtioFsSgListsSubmit (
> >>        if (!EFI_ERROR (Status) && EFI_ERROR (UnmapStatus)) {
> >>          Status = UnmapStatus;
> >>        }
> >>      }
> >>    }
> >>  
> >>    return Status;
> >>  }
> >> +
> >> +/**
> >> +  Set up the fields of a new VIRTIO_FS_FUSE_REQUEST object.
> >> +
> >> +  The function may only be called after VirtioFsInit() returns successfully and
> >> +  before VirtioFsUninit() is called.
> >> +
> >> +  @param[in,out] VirtioFs The Virtio Filesystem device that the request is
> >> +                          being prepared for. The "VirtioFs->RequestId" field
> >> +                          will be copied into "Request->Unique". On output (on
> >> +                          successful return), "VirtioFs->RequestId" will be
> >> +                          incremented.
> >> +
> >> +  @param[out] Request     The VIRTIO_FS_FUSE_REQUEST object whose fields are to
> >> +                          be set.
> >> +
> >> +  @param[in] RequestSize  The total size of the request, including
> >> +                          sizeof(VIRTIO_FS_FUSE_REQUEST).
> >> +
> >> +  @param[in] Opcode       The VIRTIO_FS_FUSE_OPCODE that identifies the command
> >> +                          to send.
> >> +
> >> +  @param[in] NodeId       The inode number of the file that the request refers
> >> +                          to.
> >> +
> >> +  @retval EFI_INVALID_PARAMETER  RequestSize is smaller than
> >> +                                 sizeof(VIRTIO_FS_FUSE_REQUEST).
> >> +
> >> +  @retval EFI_OUT_OF_RESOURCES   "VirtioFs->RequestId" is MAX_UINT64, and can
> >> +                                 be incremented no more.
> >> +
> >> +  @retval EFI_SUCCESS            Request has been populated,
> >> +                                 "VirtioFs->RequestId" has been incremented.
> >> +**/
> >> +EFI_STATUS
> >> +VirtioFsFuseNewRequest (
> >> +  IN OUT VIRTIO_FS              *VirtioFs,
> >> +     OUT VIRTIO_FS_FUSE_REQUEST *Request,
> >> +  IN     UINT32                 RequestSize,
> >> +  IN     UINT32                 Opcode,
> >> +  IN     UINT64                 NodeId
> >> +  )
> >> +{
> >> +  if (RequestSize < sizeof *Request) {
> >> +    return EFI_INVALID_PARAMETER;
> >> +  }
> >> +
> >> +  if (VirtioFs->RequestId == MAX_UINT64) {
> >> +    return EFI_OUT_OF_RESOURCES;
> >> +  }
> >> +
> >> +  Request->Len     = RequestSize;
> >> +  Request->Opcode  = Opcode;
> >> +  Request->Unique  = VirtioFs->RequestId++;
> >> +  Request->NodeId  = NodeId;
> >> +  Request->Uid     = 0;
> >> +  Request->Gid     = 0;
> >> +  Request->Pid     = 1;
> >> +  Request->Padding = 0;
> >> +
> >> +  return EFI_SUCCESS;
> >> +}
> >> +
> >> +/**
> >> +  Check the common FUSE response format.
> >> +
> >> +  The first buffer in the response scatter-gather list is assumed a
> >> +  VIRTIO_FS_FUSE_RESPONSE structure. Subsequent response buffers, if any, up to
> >> +  and excluding the last one, are assumed fixed size. The last response buffer
> >> +  may or may not be fixed size, as specified by the caller.
> >> +
> >> +  This function may only be called after VirtioFsSgListsSubmit() returns
> >> +  successfully.
> >> +
> >> +  @param[in] ResponseSgList   The scatter-gather list that describes the
> >> +                              response part of the exchange -- the buffers that
> >> +                              the Virtio Filesystem device filled in during the
> >> +                              virtio transfer.
> >> +
> >> +  @param[in] RequestId        The request identifier to which the response is
> >> +                              expected to belong.
> >> +
> >> +  @param[out] TailBufferFill  If NULL, then the last buffer in ResponseSgList
> >> +                              is considered fixed size. Otherwise, the last
> >> +                              buffer is considered variable size, and on
> >> +                              successful return, TailBufferFill reports the
> >> +                              number of bytes in the last buffer.
> >> +
> >> +  @retval EFI_INVALID_PARAMETER  TailBufferFill is not NULL (i.e., the last
> >> +                                 buffer is considered variable size), and
> >> +                                 ResponseSgList->NumVec is 1.
> >> +
> >> +  @retval EFI_INVALID_PARAMETER  The allocated size of the first buffer does
> >> +                                 not match sizeof(VIRTIO_FS_FUSE_RESPONSE).
> >> +
> >> +  @retval EFI_PROTOCOL_ERROR     The VIRTIO_FS_FUSE_RESPONSE structure in the
> >> +                                 first buffer has not been fully populated.
> >> +
> >> +  @retval EFI_PROTOCOL_ERROR     "VIRTIO_FS_FUSE_RESPONSE.Len" in the first
> >> +                                 buffer does not equal the sum of the
> >> +                                 individual buffer sizes (as populated).
> >> +
> >> +  @retval EFI_PROTOCOL_ERROR     "VIRTIO_FS_FUSE_RESPONSE.Unique" in the first
> >> +                                 buffer does not equal RequestId.
> >> +
> >> +  @retval EFI_PROTOCOL_ERROR     "VIRTIO_FS_FUSE_RESPONSE.Error" in the first
> >> +                                 buffer is zero, but a subsequent fixed size
> >> +                                 buffer has not been fully populated.
> >> +
> >> +  @retval EFI_DEVICE_ERROR       "VIRTIO_FS_FUSE_RESPONSE.Error" in the first
> >> +                                 buffer is nonzero. The caller may investigate
> >> +                                 "VIRTIO_FS_FUSE_RESPONSE.Error". Note that the
> >> +                                 completeness of the subsequent fixed size
> >> +                                 buffers is not verified in this case.
> >> +
> >> +  @retval EFI_SUCCESS            Verification successful.
> >> +**/
> >> +EFI_STATUS
> >> +VirtioFsFuseCheckResponse (
> >> +  IN  VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList,
> >> +  IN  UINT64                        RequestId,
> >> +  OUT UINTN                         *TailBufferFill
> >> +  )
> >> +{
> >> +  UINTN                   NumFixedSizeVec;
> >> +  VIRTIO_FS_FUSE_RESPONSE *CommonResp;
> >> +  UINT32                  TotalTransferred;
> >> +  UINTN                   Idx;
> >> +
> >> +  //
> >> +  // Ensured by VirtioFsSgListsValidate().
> >> +  //
> >> +  ASSERT (ResponseSgList->NumVec > 0);
> >> +
> >> +  if (TailBufferFill == NULL) {
> >> +    //
> >> +    // All buffers are considered fixed size.
> >> +    //
> >> +    NumFixedSizeVec = ResponseSgList->NumVec;
> >> +  } else {
> >> +    //
> >> +    // If the last buffer is variable size, then we need that buffer to be
> >> +    // different from the first buffer, which is considered a
> >> +    // VIRTIO_FS_FUSE_RESPONSE (fixed size) structure.
> >> +    //
> >> +    if (ResponseSgList->NumVec == 1) {
> >> +      return EFI_INVALID_PARAMETER;
> >> +    }
> >> +    NumFixedSizeVec = ResponseSgList->NumVec - 1;
> >> +  }
> >> +
> >> +  //
> >> +  // The first buffer is supposed to carry a (fully populated)
> >> +  // VIRTIO_FS_FUSE_RESPONSE structure.
> >> +  //
> >> +  if (ResponseSgList->IoVec[0].Size != sizeof *CommonResp) {
> >> +    return EFI_INVALID_PARAMETER;
> >> +  }
> >> +  if (ResponseSgList->IoVec[0].Transferred != ResponseSgList->IoVec[0].Size) {
> >> +    return EFI_PROTOCOL_ERROR;
> >> +  }
> >> +
> >> +  //
> >> +  // FUSE must report the same number of bytes, written by the Virtio
> >> +  // Filesystem device, as the virtio transport does.
> >> +  //
> >> +  CommonResp = ResponseSgList->IoVec[0].Buffer;
> >> +  TotalTransferred = 0;
> >> +  for (Idx = 0; Idx < ResponseSgList->NumVec; Idx++) {
> >> +    //
> >> +    // Integer overflow and truncation are not possible, based on
> >> +    // VirtioFsSgListsValidate() and VirtioFsSgListsSubmit().
> >> +    //
> >> +    TotalTransferred += (UINT32)ResponseSgList->IoVec[Idx].Transferred;
> >> +  }
> >> +  if (CommonResp->Len != TotalTransferred) {
> >> +    return EFI_PROTOCOL_ERROR;
> >> +  }
> >> +
> >> +  //
> >> +  // Enforce that FUSE match our request ID in the response.
> >> +  //
> >> +  if (CommonResp->Unique != RequestId) {
> >> +    return EFI_PROTOCOL_ERROR;
> >> +  }
> >> +
> >> +  //
> >> +  // If there is an explicit error report, skip checking the transfer
> >> +  // counts for the rest of the fixed size buffers.
> >> +  //
> >> +  if (CommonResp->Error != 0) {
> >> +    return EFI_DEVICE_ERROR;
> >> +  }
> >> +
> >> +  //
> >> +  // There was no error reported, so we require that the Virtio Filesystem
> >> +  // device populate all fixed size buffers. We checked this for the very first
> >> +  // buffer above; let's check the rest (if any).
> >> +  //
> >> +  ASSERT (NumFixedSizeVec >= 1);
> >> +  for (Idx = 1; Idx < NumFixedSizeVec; Idx++) {
> >> +    if (ResponseSgList->IoVec[Idx].Transferred !=
> >> +        ResponseSgList->IoVec[Idx].Size) {
> >> +      return EFI_PROTOCOL_ERROR;
> >> +    }
> >> +  }
> >> +
> >> +  //
> >> +  // If the last buffer is considered variable size, report its filled size.
> >> +  //
> >> +  if (TailBufferFill != NULL) {
> >> +    *TailBufferFill = ResponseSgList->IoVec[NumFixedSizeVec].Transferred;
> >> +  }
> >> +
> >> +  return EFI_SUCCESS;
> >> +}
> >> -- 
> >> 2.19.1.3.g30247aa5d201
> >>
> >>
> >>
> >> _______________________________________________
> >> Virtio-fs mailing list
> >> Virtio-fs@redhat.com
> >> https://www.redhat.com/mailman/listinfo/virtio-fs
> 
-- 
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Virtio-fs] [edk2 PATCH 06/48] OvmfPkg/VirtioFsDxe: introduce the basic FUSE request/response headers
  2020-12-17 13:57     ` Laszlo Ersek
  2020-12-17 14:06       ` Dr. David Alan Gilbert
@ 2020-12-17 14:32       ` Laszlo Ersek
  1 sibling, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-17 14:32 UTC (permalink / raw)
  To: Dr. David Alan Gilbert
  Cc: devel, virtio-fs, Jordan Justen, Philippe Mathieu-Daudé,
	Ard Biesheuvel

On 12/17/20 14:57, Laszlo Ersek wrote:

> So, as long as it's up to me, OVMF never "just takes" but "adds from
> zero", and I do stick with the absolute minimum needed.

... I mean this specific patch only adds the two most basic headers
really (VIRTIO_FS_FUSE_REQUEST, VIRTIO_FS_FUSE_RESPONSE), and the rest
of the structs are introduced gradually over the series -- only when an
actual FUSE request type needs to be sent for the desired functionality.
So it's not a "dump of definitions" at any point in the series.

The complete set of structures+flags, defined in the FUSE 7.31 interface
version, never lands in its entirety in "OvmfPkg/Include/IndustryStandard".

Thanks,
Laszlo


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [edk2 PATCH 42/48] OvmfPkg/VirtioFsDxe: add helper for composing rename/move destination path
  2020-12-16 21:11 ` [edk2 PATCH 42/48] OvmfPkg/VirtioFsDxe: add helper for composing rename/move destination path Laszlo Ersek
@ 2020-12-18 17:39   ` Ard Biesheuvel
  2020-12-19 22:40     ` Laszlo Ersek
  0 siblings, 1 reply; 65+ messages in thread
From: Ard Biesheuvel @ 2020-12-18 17:39 UTC (permalink / raw)
  To: Laszlo Ersek, devel, virtio-fs; +Cc: Jordan Justen, Philippe Mathieu-Daudé

On 12/16/20 10:11 PM, Laszlo Ersek wrote:
> The EFI_FILE_PROTOCOL.SetInfo() member is somewhat under-specified; one of
> its modes of operation is renaming/moving the file.
> 
> In order to create the destination pathname in canonical format, 2*2=4
> cases have to be considered. For the sake of discussion, assume the
> current canonical pathname of a VIRTIO_FS_FILE is "/home/user/f1.txt".
> Then, consider the following rename/move requests from
> EFI_FILE_PROTOCOL.SetInfo():
> 
>   Destination requested  Destination  Move into   Destination in
>   by SetInfo()           relative?    directory?  canonical format
>   ---------------------  -----------  ----------  -----------------------
>   L"\\dir\\f2.txt"       no           no          "/dir/f2.txt"

What happens in the above case if /dir/f2.txt is an existing directory?

>   L"\\dir\\"             no           yes         "/dir/f1.txt"
>   L"dir\\f2.txt"         yes          no          "/home/user/dir/f2.txt"
>   L"dir\\"               yes          yes         "/home/user/dir/f1.txt"
> 
> Add the VirtioFsComposeRenameDestination() function, for composing the
> last column from the current canonical pathname and the SetInfo() input.
> 
> The function works on the following principles:
> 
> - The prefix of the destination path is "/", if the SetInfo() rename
>   request is absolute.
> 
>   Otherwise, the dest prefix is the "current directory" (the most specific
>   parent directory) of the original pathname (in the above example,
>   "/home/user").
> 
> - The suffix of the destination path is precisely the SetInfo() request
>   string, if the "move into directory" convenience format -- the trailing
>   backslash -- is not used. (In the above example, L"\\dir\\f2.txt" and
>   L"dir\\f2.txt".)
> 
>   Otherwise, the suffix is the SetInfo() request, plus the original
>   basename (in the above example, L"\\dir\\f1.txt" and L"dir\\f1.txt").
> 
> - The complete destination is created by fusing the dest prefix and the
>   dest suffix, using the VirtioFsAppendPath() function.
> 
> 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>
> ---
>  OvmfPkg/VirtioFsDxe/VirtioFsDxe.h |   8 +
>  OvmfPkg/VirtioFsDxe/Helpers.c     | 194 ++++++++++++++++++++
>  2 files changed, 202 insertions(+)
> 
> diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
> index 9334e5434c51..a6dfac71f4a7 100644
> --- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
> +++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
> @@ -257,16 +257,24 @@ VirtioFsLookupMostSpecificParentDir (
>  
>  EFI_STATUS
>  VirtioFsGetBasename (
>    IN     CHAR8  *Path,
>       OUT CHAR16 *Basename     OPTIONAL,
>    IN OUT UINTN  *BasenameSize
>    );
>  
> +EFI_STATUS
> +VirtioFsComposeRenameDestination (
> +  IN     CHAR8   *LhsPath8,
> +  IN     CHAR16  *RhsPath16,
> +     OUT CHAR8   **ResultPath8,
> +     OUT BOOLEAN *RootEscape
> +  );
> +
>  EFI_STATUS
>  VirtioFsFuseAttrToEfiFileInfo (
>    IN     VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr,
>       OUT EFI_FILE_INFO                      *FileInfo
>    );
>  
>  EFI_STATUS
>  VirtioFsFuseDirentPlusToEfiFileInfo (
> diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
> index cdaa8557a17b..b741cf753495 100644
> --- a/OvmfPkg/VirtioFsDxe/Helpers.c
> +++ b/OvmfPkg/VirtioFsDxe/Helpers.c
> @@ -1778,16 +1778,210 @@ VirtioFsGetBasename (
>    }
>  
>    for (Idx = LastComponent; Idx < PathSize; Idx++) {
>      Basename[Idx - LastComponent] = Path[Idx];
>    }
>    return EFI_SUCCESS;
>  }
>  
> +/**
> +  Format the destination of a rename/move operation as a dynamically allocated
> +  canonical pathname.
> +
> +  Any dot-dot in RhsPath16 that would remove the root directory is dropped, and
> +  reported through RootEscape, without failing the function call.
> +
> +  @param[in] LhsPath8     The source pathname operand of the rename/move
> +                          operation, expressed as a canonical pathname (as
> +                          defined in the description of VirtioFsAppendPath()).
> +                          The root directory "/" cannot be renamed/moved, and
> +                          will be rejected.
> +
> +  @param[in] RhsPath16    The destination pathname operand expressed as a
> +                          UEFI-style CHAR16 pathname.
> +
> +                          If RhsPath16 starts with a backslash, then RhsPath16
> +                          is considered absolute. Otherwise, RhsPath16 is
> +                          interpreted relative to the most specific parent
> +                          directory found in LhsPath8.
> +
> +                          Independently, if RhsPath16 ends with a backslash
> +                          (i.e., RhsPath16 is given in the "move into
> +                          directory" convenience form), then RhsPath16 is
> +                          interpreted with the basename of LhsPath8 appended.
> +                          Otherwise, the last pathname component of RhsPath16
> +                          is taken as the last pathname component of the
> +                          rename/move destination.
> +
> +                          An empty RhsPath16 is rejected.
> +
> +  @param[out] ResultPath8  The POSIX-style, canonical format pathname that
> +                           leads to the renamed/moved file. After use, the
> +                           caller is responsible for freeing ResultPath8.
> +
> +  @param[out] RootEscape   Set to TRUE if at least one dot-dot component in
> +                           RhsPath16 attempted to escape the root directory;
> +                           set to FALSE otherwise.
> +
> +  @retval EFI_SUCCESS            ResultPath8 has been produced. RootEscape has
> +                                 been output.
> +
> +  @retval EFI_INVALID_PARAMETER  LhsPath8 is "/".
> +
> +  @retval EFI_INVALID_PARAMETER  RhsPath16 is zero-length.
> +
> +  @retval EFI_INVALID_PARAMETER  RhsPath16 failed the
> +                                 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
> +
> +  @retval EFI_OUT_OF_RESOURCES   Memory allocation failed.
> +
> +  @retval EFI_OUT_OF_RESOURCES   ResultPath8 would have failed the
> +                                 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
> +
> +  @retval EFI_UNSUPPORTED        RhsPath16 contains a character that either
> +                                 falls outside of the printable ASCII set, or
> +                                 is a forward slash.
> +**/
> +EFI_STATUS
> +VirtioFsComposeRenameDestination (
> +  IN     CHAR8   *LhsPath8,
> +  IN     CHAR16  *RhsPath16,
> +     OUT CHAR8   **ResultPath8,
> +     OUT BOOLEAN *RootEscape
> +  )
> +{
> +  //
> +  // Lengths are expressed as numbers of characters (CHAR8 or CHAR16),
> +  // excluding terminating NULs. Sizes are expressed as byte counts, including
> +  // the bytes taken up by terminating NULs.
> +  //
> +  UINTN      RhsLen;
> +  UINTN      LhsBasename16Size;
> +  EFI_STATUS Status;
> +  UINTN      LhsBasenameLen;
> +  UINTN      DestSuffix16Size;
> +  CHAR16     *DestSuffix16;
> +  CHAR8      *DestPrefix8;
> +
> +  //
> +  // An empty destination operand for the rename/move operation is not allowed.
> +  //
> +  RhsLen = StrLen (RhsPath16);
> +  if (RhsLen == 0) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +  //
> +  // Enforce length restriction on RhsPath16.
> +  //
> +  if (RhsLen > VIRTIO_FS_MAX_PATHNAME_LENGTH) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // Determine the length of the basename of LhsPath8.
> +  //
> +  LhsBasename16Size = 0;
> +  Status = VirtioFsGetBasename (LhsPath8, NULL, &LhsBasename16Size);
> +  ASSERT (Status == EFI_BUFFER_TOO_SMALL);
> +  ASSERT (LhsBasename16Size >= sizeof (CHAR16));
> +  ASSERT (LhsBasename16Size % sizeof (CHAR16) == 0);
> +  LhsBasenameLen = LhsBasename16Size / sizeof (CHAR16) - 1;
> +  if (LhsBasenameLen == 0) {
> +    //
> +    // The root directory cannot be renamed/moved.
> +    //
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // Resolve the "move into directory" convenience form in RhsPath16.
> +  //
> +  if (RhsPath16[RhsLen - 1] == L'\\') {
> +    //
> +    // Append the basename of LhsPath8 as a CHAR16 string to RhsPath16.
> +    //
> +    DestSuffix16Size = RhsLen * sizeof (CHAR16) + LhsBasename16Size;
> +    DestSuffix16 = AllocatePool (DestSuffix16Size);
> +    if (DestSuffix16 == NULL) {
> +      return EFI_OUT_OF_RESOURCES;
> +    }
> +    CopyMem (DestSuffix16, RhsPath16, RhsLen * sizeof (CHAR16));
> +    Status = VirtioFsGetBasename (LhsPath8, DestSuffix16 + RhsLen,
> +               &LhsBasename16Size);
> +    ASSERT_EFI_ERROR (Status);
> +  } else {
> +    //
> +    // Just create a copy of RhsPath16.
> +    //
> +    DestSuffix16Size = (RhsLen + 1) * sizeof (CHAR16);
> +    DestSuffix16 = AllocateCopyPool (DestSuffix16Size, RhsPath16);
> +    if (DestSuffix16 == NULL) {
> +      return EFI_OUT_OF_RESOURCES;
> +    }
> +  }
> +
> +  //
> +  // If the destination operand is absolute, it will be interpreted relative to
> +  // the root directory.
> +  //
> +  // Otherwise (i.e., if the destination operand is relative), then create the
> +  // canonical pathname that the destination operand is interpreted relatively
> +  // to; that is, the canonical pathname of the most specific parent directory
> +  // found in LhsPath8.
> +  //
> +  if (DestSuffix16[0] == L'\\') {
> +    DestPrefix8 = AllocateCopyPool (sizeof "/", "/");
> +    if (DestPrefix8 == NULL) {
> +      Status = EFI_OUT_OF_RESOURCES;
> +      goto FreeDestSuffix16;
> +    }
> +  } else {
> +    UINTN LhsLen;
> +    UINTN DestPrefixLen;
> +
> +    //
> +    // Strip the basename of LhsPath8.
> +    //
> +    LhsLen = AsciiStrLen (LhsPath8);
> +    ASSERT (LhsBasenameLen < LhsLen);
> +    DestPrefixLen = LhsLen - LhsBasenameLen;
> +    ASSERT (LhsPath8[DestPrefixLen - 1] == '/');
> +    //
> +    // If we're not at the root directory, strip the slash too.
> +    //
> +    if (DestPrefixLen > 1) {
> +      DestPrefixLen--;
> +    }
> +    DestPrefix8 = AllocatePool (DestPrefixLen + 1);
> +    if (DestPrefix8 == NULL) {
> +      Status = EFI_OUT_OF_RESOURCES;
> +      goto FreeDestSuffix16;
> +    }
> +    CopyMem (DestPrefix8, LhsPath8, DestPrefixLen);
> +    DestPrefix8[DestPrefixLen] = '\0';
> +  }
> +
> +  //
> +  // Now combine DestPrefix8 and DestSuffix16 into the final canonical
> +  // pathname.
> +  //
> +  Status = VirtioFsAppendPath (DestPrefix8, DestSuffix16, ResultPath8,
> +             RootEscape);
> +
> +  FreePool (DestPrefix8);
> +  //
> +  // Fall through.
> +  //
> +FreeDestSuffix16:
> +  FreePool (DestSuffix16);
> +
> +  return Status;
> +}
> +
>  /**
>    Convert select fields of a VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
>    corresponding fields in EFI_FILE_INFO.
>  
>    @param[in] FuseAttr   The VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
>                          convert the relevant fields from.
>  
>    @param[out] FileInfo  The EFI_FILE_INFO structure to modify. Importantly, the
> 


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [edk2 PATCH 01/48] OvmfPkg: introduce VirtioFsDxe
  2020-12-16 21:10 ` [edk2 PATCH 01/48] OvmfPkg: introduce VirtioFsDxe Laszlo Ersek
@ 2020-12-18 17:42   ` Ard Biesheuvel
  2020-12-18 18:13     ` [Virtio-fs] " Dr. David Alan Gilbert
  0 siblings, 1 reply; 65+ messages in thread
From: Ard Biesheuvel @ 2020-12-18 17:42 UTC (permalink / raw)
  To: Laszlo Ersek, devel, virtio-fs; +Cc: Jordan Justen, Philippe Mathieu-Daudé

On 12/16/20 10:10 PM, Laszlo Ersek wrote:
> The purpose of the driver is to ease file exchange (file sharing) between
> the guest firmware and the virtualization host. The driver is supposed to
> interoperate with QEMU's "virtiofsd" (Virtio Filesystem Daemon).
> 
> References:
> - https://virtio-fs.gitlab.io/
> - https://libvirt.org/kbase/virtiofs.html
> 
> VirtioFsDxe will bind virtio-fs devices, and produce
> EFI_SIMPLE_FILE_SYSTEM_PROTOCOL instances on them.
> 
> In the longer term, assuming QEMU will create "bootorder" fw_cfg file
> entries for virtio-fs devices, booting guest OSes from host-side
> directories should become possible (dependent on the matching
> QemuBootOrderLib enhancement).
> 
> Add the skeleton of the driver. Install EFI_DRIVER_BINDING_PROTOCOL with
> stub member functions. Install EFI_COMPONENT_NAME2_PROTOCOL with final
> member functions. This suffices for the DRIVERS command in the UEFI Shell
> to list the driver with a human-readable name.
> 
> The file permission model is described immediately in the INF file as a
> comment block, for future reference.
> 
> 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>
> ---
>  OvmfPkg/OvmfPkgIa32.dsc             |   1 +
>  OvmfPkg/OvmfPkgIa32X64.dsc          |   1 +
>  OvmfPkg/OvmfPkgX64.dsc              |   1 +
>  OvmfPkg/OvmfPkgIa32.fdf             |   1 +
>  OvmfPkg/OvmfPkgIa32X64.fdf          |   1 +
>  OvmfPkg/OvmfPkgX64.fdf              |   1 +
>  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf |  92 ++++++++++++++++
>  OvmfPkg/VirtioFsDxe/DriverBinding.c | 112 ++++++++++++++++++++
>  8 files changed, 210 insertions(+)
> 
> diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
> index 8eede796a8bd..4ff70674fb6e 100644
> --- a/OvmfPkg/OvmfPkgIa32.dsc
> +++ b/OvmfPkg/OvmfPkgIa32.dsc
> @@ -807,16 +807,17 @@ [Components]
>    }
>    MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
>    MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
>    MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
>    MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf
>    MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
>    FatPkg/EnhancedFatDxe/Fat.inf
>    MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
> +  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
>    MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf
>    MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf
>    OvmfPkg/SataControllerDxe/SataControllerDxe.inf
>    MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf
>    MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf
>    MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
>    MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
>    MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
> diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
> index f9f82a48f4b9..d40a59183c79 100644
> --- a/OvmfPkg/OvmfPkgIa32X64.dsc
> +++ b/OvmfPkg/OvmfPkgIa32X64.dsc
> @@ -821,16 +821,17 @@ [Components.X64]
>    }
>    MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
>    MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
>    MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
>    MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf
>    MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
>    FatPkg/EnhancedFatDxe/Fat.inf
>    MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
> +  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
>    MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf
>    MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf
>    OvmfPkg/SataControllerDxe/SataControllerDxe.inf
>    MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf
>    MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf
>    MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
>    MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
>    MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
> diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
> index e59ae05b73aa..ec7886235acf 100644
> --- a/OvmfPkg/OvmfPkgX64.dsc
> +++ b/OvmfPkg/OvmfPkgX64.dsc
> @@ -817,16 +817,17 @@ [Components]
>    }
>    MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
>    MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
>    MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
>    MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf
>    MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
>    FatPkg/EnhancedFatDxe/Fat.inf
>    MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
> +  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
>    MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf
>    MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf
>    OvmfPkg/SataControllerDxe/SataControllerDxe.inf
>    MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf
>    MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf
>    MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
>    MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
>    MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
> diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf
> index c07b775d0a2d..f400c845b9c9 100644
> --- a/OvmfPkg/OvmfPkgIa32.fdf
> +++ b/OvmfPkg/OvmfPkgIa32.fdf
> @@ -285,16 +285,17 @@ [FV.DXEFV]
>  INF  OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
>  INF  RuleOverride=ACPITABLE OvmfPkg/AcpiTables/AcpiTables.inf
>  INF  MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
>  INF  MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf
>  INF  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
>  
>  INF  FatPkg/EnhancedFatDxe/Fat.inf
>  INF  MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
> +INF  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
>  
>  !if $(TOOL_CHAIN_TAG) != "XCODE5"
>  INF  ShellPkg/DynamicCommand/TftpDynamicCommand/TftpDynamicCommand.inf
>  INF  ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.inf
>  INF  OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf
>  !endif
>  INF  ShellPkg/Application/Shell/Shell.inf
>  
> diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf
> index 9adf1525c135..d055552fd09f 100644
> --- a/OvmfPkg/OvmfPkgIa32X64.fdf
> +++ b/OvmfPkg/OvmfPkgIa32X64.fdf
> @@ -286,16 +286,17 @@ [FV.DXEFV]
>  INF  OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
>  INF  RuleOverride=ACPITABLE OvmfPkg/AcpiTables/AcpiTables.inf
>  INF  MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
>  INF  MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf
>  INF  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
>  
>  INF  FatPkg/EnhancedFatDxe/Fat.inf
>  INF  MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
> +INF  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
>  
>  !if $(TOOL_CHAIN_TAG) != "XCODE5"
>  INF  ShellPkg/DynamicCommand/TftpDynamicCommand/TftpDynamicCommand.inf
>  INF  ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.inf
>  INF  OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf
>  !endif
>  INF  ShellPkg/Application/Shell/Shell.inf
>  
> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
> index 17ba9e177ac3..1a2ef5bf2ae3 100644
> --- a/OvmfPkg/OvmfPkgX64.fdf
> +++ b/OvmfPkg/OvmfPkgX64.fdf
> @@ -295,16 +295,17 @@ [FV.DXEFV]
>  INF  OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
>  INF  RuleOverride=ACPITABLE OvmfPkg/AcpiTables/AcpiTables.inf
>  INF  MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
>  INF  MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf
>  INF  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
>  
>  INF  FatPkg/EnhancedFatDxe/Fat.inf
>  INF  MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
> +INF  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
>  
>  !if $(TOOL_CHAIN_TAG) != "XCODE5"
>  INF  ShellPkg/DynamicCommand/TftpDynamicCommand/TftpDynamicCommand.inf
>  INF  ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.inf
>  INF  OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf
>  !endif
>  INF  ShellPkg/Application/Shell/Shell.inf
>  
> diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
> new file mode 100644
> index 000000000000..69cb44bc7c96
> --- /dev/null
> +++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
> @@ -0,0 +1,92 @@
> +## @file
> +# Provide EFI_SIMPLE_FILE_SYSTEM_PROTOCOL instances on virtio-fs devices.
> +#
> +# Copyright (C) 2020, Red Hat, Inc.
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +#
> +# Permission Model of this driver:
> +#
> +# Regardless of the UID and GID values this driver send in the FUSE request
> +# header, the daemon (that is, the Virtio Filesystem device) always acts with
> +# root privileges on the host side. The only time the daemon considers said UID
> +# and GID fields is when creating a new file or directory. Thus, the guest
> +# driver cannot rely on the host for enforcing any file mode permissions,
> +# regardless of the "personality" that the guest driver poses as, because
> +# "root" on the host side ignores all file mode bits.
> +#
> +# Therefore the guest driver has to do its own permission checking, and use the
> +# host-side file mode bits only as a kind of "metadata storage" or "reminder"
> +# -- hopefully in a way that makes some sense on the host side too.
> +#

Can you please explain why this is safe? Or should virtio-fs only be
used with guests that can be trusted with root privileges on the host?

-- 
Ard.



> +# The complete mapping between the EFI_FILE_PROTOCOL and the host-side file
> +# mode bits is described below.
> +#
> +# - The guest driver poses as UID 0, GID 0, PID 1.
> +#
> +# - If and only if all "w" bits are missing from a file on the host side, then
> +#   the file or directory is reported as EFI_FILE_READ_ONLY in the guest. When
> +#   setting EFI_FILE_READ_ONLY in the guest, all "w" bits (0222) are cleared on
> +#   the host; when clearing EFI_FILE_READ_ONLY in the guest, all "w" bits are
> +#   set on the host. Viewed from the host side, this sort of reflects that an
> +#   EFI_FILE_READ_ONLY file should not be written by anyone.
> +#
> +# - The attributes EFI_FILE_HIDDEN, EFI_FILE_SYSTEM, EFI_FILE_RESERVED, and
> +#   EFI_FILE_ARCHIVE are never reported in the guest, and they are silently
> +#   ignored when a SetInfo() call or a file-creating Open() call requests them.
> +#
> +# - On the host, files are created with 0666 file mode bits, directories are
> +#   created with 0777 file mode bits.
> +#
> +# - In the guest, the EFI_FILE_READ_ONLY attribute only controls the permitted
> +#   open mode. In particular, on directories, the EFI_FILE_READ_ONLY attribute
> +#   does not prevent the creation or deletion of entries inside the directory;
> +#   EFI_FILE_READ_ONLY only prevents the renaming, deleting, flushing (syncing)
> +#   and touching of the directory itself (with "touching" meaning updating the
> +#   timestamps). The fact that EFI_FILE_READ_ONLY being set on a directory is
> +#   irrelevant in the guest with regard to entry creation/deletion, is
> +#   well-mirrored by the fact that virtiofsd -- which runs as root, regardless
> +#   of guest driver personality -- ignores the absence of "w" permissions on a
> +#   host-side directory, when creating or removing entries in it.
> +#
> +# - When an EFI_FILE_PROTOCOL is opened read-only, then the Delete(), Write()
> +#   and Flush() member functions are disabled for it. Additionally, SetInfo()
> +#   is restricted to flipping the EFI_FILE_READ_ONLY bit (which takes effect at
> +#   the next Open()).
> +#
> +# - As a consequence of the above, for deleting a directory, it must be
> +#   presented in the guest as openable for writing.
> +#
> +# - We diverge from the UEFI spec, and permit Flush() on a directory that has
> +#   been opened read-write; otherwise the only way to invoke FUSE_FSYNCDIR on a
> +#   directory would be to Close() it.
> +#
> +# - OpenVolume() opens the root directory for read-only access. The Open()
> +#   member function may open it for read-write access. While the root directory
> +#   cannot be renamed or deleted, opening it for read-write access is useful
> +#   for calling Flush(), according to the previous paragraph, or for updating
> +#   the root directory's timestamps with SetInfo().
> +##
> +
> +[Defines]
> +  INF_VERSION                           = 1.29
> +  BASE_NAME                             = VirtioFsDxe
> +  FILE_GUID                             = 7BD9DDF7-8B83-488E-AEC9-24C78610289C
> +  MODULE_TYPE                           = UEFI_DRIVER
> +  ENTRY_POINT                           = VirtioFsEntryPoint
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +
> +[Sources]
> +  DriverBinding.c
> +
> +[LibraryClasses]
> +  BaseLib
> +  UefiBootServicesTableLib
> +  UefiDriverEntryPoint
> +
> +[Protocols]
> +  gEfiComponentName2ProtocolGuid        ## PRODUCES
> +  gEfiDriverBindingProtocolGuid         ## PRODUCES
> diff --git a/OvmfPkg/VirtioFsDxe/DriverBinding.c b/OvmfPkg/VirtioFsDxe/DriverBinding.c
> new file mode 100644
> index 000000000000..ac0a6330f01b
> --- /dev/null
> +++ b/OvmfPkg/VirtioFsDxe/DriverBinding.c
> @@ -0,0 +1,112 @@
> +/** @file
> +  Provide EFI_SIMPLE_FILE_SYSTEM_PROTOCOL instances on virtio-fs devices.
> +
> +  Copyright (C) 2020, Red Hat, Inc.
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#include <Library/BaseLib.h>                  // AsciiStrCmp()
> +#include <Library/UefiBootServicesTableLib.h> // gBS
> +#include <Protocol/ComponentName2.h>          // EFI_COMPONENT_NAME2_PROTOCOL
> +#include <Protocol/DriverBinding.h>           // EFI_DRIVER_BINDING_PROTOCOL
> +
> +//
> +// UEFI Driver Model protocol instances.
> +//
> +STATIC EFI_DRIVER_BINDING_PROTOCOL  mDriverBinding;
> +STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2;
> +
> +//
> +// UEFI Driver Model protocol member functions.
> +//
> +EFI_STATUS
> +EFIAPI
> +VirtioFsBindingSupported (
> +  IN EFI_DRIVER_BINDING_PROTOCOL *This,
> +  IN EFI_HANDLE                  ControllerHandle,
> +  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
> +  )
> +{
> +  return EFI_UNSUPPORTED;
> +}
> +
> +EFI_STATUS
> +EFIAPI
> +VirtioFsBindingStart (
> +  IN EFI_DRIVER_BINDING_PROTOCOL *This,
> +  IN EFI_HANDLE                  ControllerHandle,
> +  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
> +  )
> +{
> +  return EFI_DEVICE_ERROR;
> +}
> +
> +EFI_STATUS
> +EFIAPI
> +VirtioFsBindingStop (
> +  IN EFI_DRIVER_BINDING_PROTOCOL *This,
> +  IN EFI_HANDLE                  ControllerHandle,
> +  IN UINTN                       NumberOfChildren,
> +  IN EFI_HANDLE                  *ChildHandleBuffer OPTIONAL
> +  )
> +{
> +  return EFI_DEVICE_ERROR;
> +}
> +
> +EFI_STATUS
> +EFIAPI
> +VirtioFsGetDriverName (
> +  IN  EFI_COMPONENT_NAME2_PROTOCOL *This,
> +  IN  CHAR8                        *Language,
> +  OUT CHAR16                       **DriverName
> +  )
> +{
> +  if (AsciiStrCmp (Language, "en") != 0) {
> +    return EFI_UNSUPPORTED;
> +  }
> +  *DriverName = L"Virtio Filesystem Driver";
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +EFIAPI
> +VirtioFsGetControllerName (
> +  IN  EFI_COMPONENT_NAME2_PROTOCOL *This,
> +  IN  EFI_HANDLE                   ControllerHandle,
> +  IN  EFI_HANDLE                   ChildHandle OPTIONAL,
> +  IN  CHAR8                        *Language,
> +  OUT CHAR16                       **ControllerName
> +  )
> +{
> +  return EFI_UNSUPPORTED;
> +}
> +
> +//
> +// Entry point of this driver.
> +//
> +EFI_STATUS
> +EFIAPI
> +VirtioFsEntryPoint (
> +  IN EFI_HANDLE       ImageHandle,
> +  IN EFI_SYSTEM_TABLE *SystemTable
> +  )
> +{
> +  EFI_STATUS Status;
> +
> +  mDriverBinding.Supported           = VirtioFsBindingSupported;
> +  mDriverBinding.Start               = VirtioFsBindingStart;
> +  mDriverBinding.Stop                = VirtioFsBindingStop;
> +  mDriverBinding.Version             = 0x10;
> +  mDriverBinding.ImageHandle         = ImageHandle;
> +  mDriverBinding.DriverBindingHandle = ImageHandle;
> +
> +  mComponentName2.GetDriverName      = VirtioFsGetDriverName;
> +  mComponentName2.GetControllerName  = VirtioFsGetControllerName;
> +  mComponentName2.SupportedLanguages = "en";
> +
> +  Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle,
> +                  &gEfiDriverBindingProtocolGuid, &mDriverBinding,
> +                  &gEfiComponentName2ProtocolGuid, &mComponentName2, NULL);
> +  return Status;
> +}
> 


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver
  2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
                   ` (47 preceding siblings ...)
  2020-12-16 21:11 ` [edk2 PATCH 48/48] OvmfPkg/VirtioFsDxe: handle attribute updates in EFI_FILE_PROTOCOL.SetInfo Laszlo Ersek
@ 2020-12-18 17:44 ` Ard Biesheuvel
  2020-12-20  0:09   ` Laszlo Ersek
  48 siblings, 1 reply; 65+ messages in thread
From: Ard Biesheuvel @ 2020-12-18 17:44 UTC (permalink / raw)
  To: Laszlo Ersek, devel, virtio-fs
  Cc: Jordan Justen, Leif Lindholm, Philippe Mathieu-Daudé

On 12/16/20 10:10 PM, Laszlo Ersek wrote:
> Ref:    https://bugzilla.tianocore.org/show_bug.cgi?id=3097
> Repo:   https://pagure.io/lersek/edk2.git
> Branch: virtio-fs (@ b8fd76d649d2)
> 
> The first commit and the bugzilla ticket state the use case.
> 
> References, including setup instructions:
> - https://libvirt.org/kbase/virtiofs.html
> - https://virtio-fs.gitlab.io/
> 
> Useful UEFI shell commands for testing: output redirections, attrib,
> connect, cp, disconnect, edit, eficompress, efidecompress, hexedit, ls,
> map, mkdir, mv, rm, setsize, timezone, touch, type, vol.
> 
> The series is largely structured as follows:
> - helper functions and FUSE command wrappers are implemented as required
>   by the next EFI_FILE_PROTOCOL interface,
> - said EFI_FILE_PROTOCOL interface is implemented,
> - lather, rinse, repeat.
> 
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Leif Lindholm <leif@nuviainc.com>
> Cc: Philippe Mathieu-Daudé <philmd@redhat.com>
> 
> Thanks,
> Laszlo
> 
> Laszlo Ersek (48):
>   OvmfPkg: introduce VirtioFsDxe
>   ArmVirtPkg: include VirtioFsDxe in the ArmVirtQemu* platforms
>   OvmfPkg/VirtioFsDxe: DriverBinding: open VirtioDevice, install
>     SimpleFs
>   OvmfPkg/VirtioFsDxe: implement virtio device (un)initialization
>   OvmfPkg/VirtioFsDxe: add a scatter-gather list data type
>   OvmfPkg/VirtioFsDxe: introduce the basic FUSE request/response headers
>   OvmfPkg/VirtioFsDxe: map "errno" values to EFI_STATUS
>   OvmfPkg/VirtioFsDxe: submit the FUSE_INIT request to the device
>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_OPENDIR
>   OvmfPkg/VirtioFsDxe: add shared wrapper for FUSE_RELEASE /
>     FUSE_RELEASEDIR
>   OvmfPkg/VirtioFsDxe: implement
>     EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume()
>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_FORGET
>   OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_FSYNC /
>     FUSE_FSYNCDIR
>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_FLUSH
>   OvmfPkg/VirtioFsDxe: flush, sync, release and forget in Close() /
>     Delete()
>   OvmfPkg/VirtioFsDxe: add helper for appending and sanitizing paths
>   OvmfPkg/VirtioFsDxe: manage path lifecycle in OpenVolume, Close,
>     Delete
>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_OPEN
>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_MKDIR
>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_CREATE
>   OvmfPkg/VirtioFsDxe: convert FUSE inode attributes to EFI_FILE_INFO
>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_LOOKUP
>   OvmfPkg/VirtioFsDxe: split canon. path into last parent + last
>     component
>   OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_UNLINK / FUSE_RMDIR
>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_GETATTR
>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Open()
>   OvmfPkg/VirtioFsDxe: erase the dir. entry in
>     EFI_FILE_PROTOCOL.Delete()
>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_STATFS
>   OvmfPkg/VirtioFsDxe: add helper for formatting UEFI basenames
>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.GetInfo()
>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.GetPosition,
>     .SetPosition
>   OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_READ /
>     FUSE_READDIRPLUS
>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Read() for regular
>     files
>   OvmfPkg/VirtioFsDxe: convert FUSE dirent filename to EFI_FILE_INFO
>   OvmfPkg/VirtioFsDxe: add EFI_FILE_INFO cache fields to VIRTIO_FS_FILE
>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Read() for
>     directories
>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Flush()
>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_WRITE
>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Write()
>   OvmfPkg/VirtioFsDxe: handle the volume label in
>     EFI_FILE_PROTOCOL.SetInfo
>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_RENAME2
>   OvmfPkg/VirtioFsDxe: add helper for composing rename/move destination
>     path
>   OvmfPkg/VirtioFsDxe: handle file rename/move in
>     EFI_FILE_PROTOCOL.SetInfo
>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_SETATTR
>   OvmfPkg/VirtioFsDxe: add helper for determining file size update
>   OvmfPkg/VirtioFsDxe: add helper for determining access time updates
>   OvmfPkg/VirtioFsDxe: add helper for determining file mode bits update
>   OvmfPkg/VirtioFsDxe: handle attribute updates in
>     EFI_FILE_PROTOCOL.SetInfo
> 

This looks carefully crafted, and modulo my two questions, I won't be
able to spend more time on this than I have going through these patches
today.

So please feel free to merge this whenever you feel it's ready.

For the series,

Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com>



>  ArmVirtPkg/ArmVirtQemu.dsc                  |    3 +-
>  ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc        |    3 +-
>  ArmVirtPkg/ArmVirtQemuKernel.dsc            |    3 +-
>  OvmfPkg/Include/IndustryStandard/Virtio10.h |    5 +
>  OvmfPkg/Include/IndustryStandard/VirtioFs.h |  454 ++++
>  OvmfPkg/OvmfPkgIa32.dsc                     |    2 +
>  OvmfPkg/OvmfPkgIa32.fdf                     |    1 +
>  OvmfPkg/OvmfPkgIa32X64.dsc                  |    2 +
>  OvmfPkg/OvmfPkgIa32X64.fdf                  |    1 +
>  OvmfPkg/OvmfPkgX64.dsc                      |    2 +
>  OvmfPkg/OvmfPkgX64.fdf                      |    1 +
>  OvmfPkg/VirtioFsDxe/DriverBinding.c         |  232 ++
>  OvmfPkg/VirtioFsDxe/FuseFlush.c             |  111 +
>  OvmfPkg/VirtioFsDxe/FuseForget.c            |   85 +
>  OvmfPkg/VirtioFsDxe/FuseFsync.c             |  121 +
>  OvmfPkg/VirtioFsDxe/FuseGetAttr.c           |  116 +
>  OvmfPkg/VirtioFsDxe/FuseInit.c              |  142 ++
>  OvmfPkg/VirtioFsDxe/FuseLookup.c            |  148 ++
>  OvmfPkg/VirtioFsDxe/FuseMkDir.c             |  134 ++
>  OvmfPkg/VirtioFsDxe/FuseOpen.c              |  126 +
>  OvmfPkg/VirtioFsDxe/FuseOpenDir.c           |  120 +
>  OvmfPkg/VirtioFsDxe/FuseOpenOrCreate.c      |  155 ++
>  OvmfPkg/VirtioFsDxe/FuseRead.c              |  191 ++
>  OvmfPkg/VirtioFsDxe/FuseRelease.c           |  121 +
>  OvmfPkg/VirtioFsDxe/FuseRename.c            |  131 ++
>  OvmfPkg/VirtioFsDxe/FuseSetAttr.c           |  174 ++
>  OvmfPkg/VirtioFsDxe/FuseStatFs.c            |  102 +
>  OvmfPkg/VirtioFsDxe/FuseUnlink.c            |  114 +
>  OvmfPkg/VirtioFsDxe/FuseWrite.c             |  155 ++
>  OvmfPkg/VirtioFsDxe/Helpers.c               | 2416 ++++++++++++++++++++
>  OvmfPkg/VirtioFsDxe/SimpleFsClose.c         |   68 +
>  OvmfPkg/VirtioFsDxe/SimpleFsDelete.c        |  110 +
>  OvmfPkg/VirtioFsDxe/SimpleFsFlush.c         |   42 +
>  OvmfPkg/VirtioFsDxe/SimpleFsGetInfo.c       |  209 ++
>  OvmfPkg/VirtioFsDxe/SimpleFsGetPosition.c   |   27 +
>  OvmfPkg/VirtioFsDxe/SimpleFsOpen.c          |  505 ++++
>  OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c    |   98 +
>  OvmfPkg/VirtioFsDxe/SimpleFsRead.c          |  434 ++++
>  OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c       |  582 +++++
>  OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c   |   67 +
>  OvmfPkg/VirtioFsDxe/SimpleFsWrite.c         |   81 +
>  OvmfPkg/VirtioFsDxe/VirtioFsDxe.h           |  544 +++++
>  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf         |  136 ++
>  43 files changed, 8271 insertions(+), 3 deletions(-)
>  create mode 100644 OvmfPkg/Include/IndustryStandard/VirtioFs.h
>  create mode 100644 OvmfPkg/VirtioFsDxe/DriverBinding.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/FuseFlush.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/FuseForget.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/FuseFsync.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/FuseGetAttr.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/FuseInit.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/FuseLookup.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/FuseMkDir.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/FuseOpen.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/FuseOpenDir.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/FuseOpenOrCreate.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/FuseRead.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/FuseRelease.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/FuseRename.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/FuseSetAttr.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/FuseStatFs.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/FuseUnlink.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/FuseWrite.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/Helpers.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsClose.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsDelete.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsFlush.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsGetInfo.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsGetPosition.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsOpen.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsOpenVolume.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsRead.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsSetPosition.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/SimpleFsWrite.c
>  create mode 100644 OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
>  create mode 100644 OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
> 
> 
> base-commit: e6ae24e1d676bb2bdc0fc715b49b04908f41fc10
> 


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Virtio-fs] [edk2 PATCH 01/48] OvmfPkg: introduce VirtioFsDxe
  2020-12-18 17:42   ` Ard Biesheuvel
@ 2020-12-18 18:13     ` Dr. David Alan Gilbert
  2020-12-19 21:16       ` Laszlo Ersek
  0 siblings, 1 reply; 65+ messages in thread
From: Dr. David Alan Gilbert @ 2020-12-18 18:13 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: Laszlo Ersek, devel, virtio-fs, Jordan Justen,
	Philippe Mathieu-Daudé

* Ard Biesheuvel (ard.biesheuvel@arm.com) wrote:
> On 12/16/20 10:10 PM, Laszlo Ersek wrote:
> > The purpose of the driver is to ease file exchange (file sharing) between
> > the guest firmware and the virtualization host. The driver is supposed to
> > interoperate with QEMU's "virtiofsd" (Virtio Filesystem Daemon).
> > 
> > References:
> > - https://virtio-fs.gitlab.io/
> > - https://libvirt.org/kbase/virtiofs.html
> > 
> > VirtioFsDxe will bind virtio-fs devices, and produce
> > EFI_SIMPLE_FILE_SYSTEM_PROTOCOL instances on them.
> > 
> > In the longer term, assuming QEMU will create "bootorder" fw_cfg file
> > entries for virtio-fs devices, booting guest OSes from host-side
> > directories should become possible (dependent on the matching
> > QemuBootOrderLib enhancement).
> > 
> > Add the skeleton of the driver. Install EFI_DRIVER_BINDING_PROTOCOL with
> > stub member functions. Install EFI_COMPONENT_NAME2_PROTOCOL with final
> > member functions. This suffices for the DRIVERS command in the UEFI Shell
> > to list the driver with a human-readable name.
> > 
> > The file permission model is described immediately in the INF file as a
> > comment block, for future reference.
> > 
> > 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>
> > ---
> >  OvmfPkg/OvmfPkgIa32.dsc             |   1 +
> >  OvmfPkg/OvmfPkgIa32X64.dsc          |   1 +
> >  OvmfPkg/OvmfPkgX64.dsc              |   1 +
> >  OvmfPkg/OvmfPkgIa32.fdf             |   1 +
> >  OvmfPkg/OvmfPkgIa32X64.fdf          |   1 +
> >  OvmfPkg/OvmfPkgX64.fdf              |   1 +
> >  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf |  92 ++++++++++++++++
> >  OvmfPkg/VirtioFsDxe/DriverBinding.c | 112 ++++++++++++++++++++
> >  8 files changed, 210 insertions(+)
> > 
> > diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
> > index 8eede796a8bd..4ff70674fb6e 100644
> > --- a/OvmfPkg/OvmfPkgIa32.dsc
> > +++ b/OvmfPkg/OvmfPkgIa32.dsc
> > @@ -807,16 +807,17 @@ [Components]
> >    }
> >    MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
> >    MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
> >    MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
> >    MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf
> >    MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
> >    FatPkg/EnhancedFatDxe/Fat.inf
> >    MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
> > +  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
> >    MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf
> >    MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf
> >    OvmfPkg/SataControllerDxe/SataControllerDxe.inf
> >    MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf
> >    MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf
> >    MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
> >    MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
> >    MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
> > diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
> > index f9f82a48f4b9..d40a59183c79 100644
> > --- a/OvmfPkg/OvmfPkgIa32X64.dsc
> > +++ b/OvmfPkg/OvmfPkgIa32X64.dsc
> > @@ -821,16 +821,17 @@ [Components.X64]
> >    }
> >    MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
> >    MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
> >    MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
> >    MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf
> >    MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
> >    FatPkg/EnhancedFatDxe/Fat.inf
> >    MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
> > +  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
> >    MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf
> >    MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf
> >    OvmfPkg/SataControllerDxe/SataControllerDxe.inf
> >    MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf
> >    MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf
> >    MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
> >    MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
> >    MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
> > diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
> > index e59ae05b73aa..ec7886235acf 100644
> > --- a/OvmfPkg/OvmfPkgX64.dsc
> > +++ b/OvmfPkg/OvmfPkgX64.dsc
> > @@ -817,16 +817,17 @@ [Components]
> >    }
> >    MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
> >    MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
> >    MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
> >    MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf
> >    MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
> >    FatPkg/EnhancedFatDxe/Fat.inf
> >    MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
> > +  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
> >    MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf
> >    MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf
> >    OvmfPkg/SataControllerDxe/SataControllerDxe.inf
> >    MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf
> >    MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf
> >    MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
> >    MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
> >    MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
> > diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf
> > index c07b775d0a2d..f400c845b9c9 100644
> > --- a/OvmfPkg/OvmfPkgIa32.fdf
> > +++ b/OvmfPkg/OvmfPkgIa32.fdf
> > @@ -285,16 +285,17 @@ [FV.DXEFV]
> >  INF  OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
> >  INF  RuleOverride=ACPITABLE OvmfPkg/AcpiTables/AcpiTables.inf
> >  INF  MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
> >  INF  MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf
> >  INF  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
> >  
> >  INF  FatPkg/EnhancedFatDxe/Fat.inf
> >  INF  MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
> > +INF  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
> >  
> >  !if $(TOOL_CHAIN_TAG) != "XCODE5"
> >  INF  ShellPkg/DynamicCommand/TftpDynamicCommand/TftpDynamicCommand.inf
> >  INF  ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.inf
> >  INF  OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf
> >  !endif
> >  INF  ShellPkg/Application/Shell/Shell.inf
> >  
> > diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf
> > index 9adf1525c135..d055552fd09f 100644
> > --- a/OvmfPkg/OvmfPkgIa32X64.fdf
> > +++ b/OvmfPkg/OvmfPkgIa32X64.fdf
> > @@ -286,16 +286,17 @@ [FV.DXEFV]
> >  INF  OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
> >  INF  RuleOverride=ACPITABLE OvmfPkg/AcpiTables/AcpiTables.inf
> >  INF  MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
> >  INF  MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf
> >  INF  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
> >  
> >  INF  FatPkg/EnhancedFatDxe/Fat.inf
> >  INF  MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
> > +INF  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
> >  
> >  !if $(TOOL_CHAIN_TAG) != "XCODE5"
> >  INF  ShellPkg/DynamicCommand/TftpDynamicCommand/TftpDynamicCommand.inf
> >  INF  ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.inf
> >  INF  OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf
> >  !endif
> >  INF  ShellPkg/Application/Shell/Shell.inf
> >  
> > diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
> > index 17ba9e177ac3..1a2ef5bf2ae3 100644
> > --- a/OvmfPkg/OvmfPkgX64.fdf
> > +++ b/OvmfPkg/OvmfPkgX64.fdf
> > @@ -295,16 +295,17 @@ [FV.DXEFV]
> >  INF  OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
> >  INF  RuleOverride=ACPITABLE OvmfPkg/AcpiTables/AcpiTables.inf
> >  INF  MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
> >  INF  MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf
> >  INF  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
> >  
> >  INF  FatPkg/EnhancedFatDxe/Fat.inf
> >  INF  MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
> > +INF  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
> >  
> >  !if $(TOOL_CHAIN_TAG) != "XCODE5"
> >  INF  ShellPkg/DynamicCommand/TftpDynamicCommand/TftpDynamicCommand.inf
> >  INF  ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.inf
> >  INF  OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf
> >  !endif
> >  INF  ShellPkg/Application/Shell/Shell.inf
> >  
> > diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
> > new file mode 100644
> > index 000000000000..69cb44bc7c96
> > --- /dev/null
> > +++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
> > @@ -0,0 +1,92 @@
> > +## @file
> > +# Provide EFI_SIMPLE_FILE_SYSTEM_PROTOCOL instances on virtio-fs devices.
> > +#
> > +# Copyright (C) 2020, Red Hat, Inc.
> > +#
> > +# SPDX-License-Identifier: BSD-2-Clause-Patent
> > +#
> > +#
> > +# Permission Model of this driver:
> > +#
> > +# Regardless of the UID and GID values this driver send in the FUSE request
> > +# header, the daemon (that is, the Virtio Filesystem device) always acts with
> > +# root privileges on the host side. The only time the daemon considers said UID
> > +# and GID fields is when creating a new file or directory. Thus, the guest
> > +# driver cannot rely on the host for enforcing any file mode permissions,
> > +# regardless of the "personality" that the guest driver poses as, because
> > +# "root" on the host side ignores all file mode bits.
> > +#
> > +# Therefore the guest driver has to do its own permission checking, and use the
> > +# host-side file mode bits only as a kind of "metadata storage" or "reminder"
> > +# -- hopefully in a way that makes some sense on the host side too.
> > +#
> 
> Can you please explain why this is safe? Or should virtio-fs only be
> used with guests that can be trusted with root privileges on the host?

The daemon sandboxes itself and generally you only expose a private area
of a filesystem to the guest; i.e. a per-guest rootfs or temporary or
whatever.

Dave
> -- 
> Ard.
> 
> 
> 
> > +# The complete mapping between the EFI_FILE_PROTOCOL and the host-side file
> > +# mode bits is described below.
> > +#
> > +# - The guest driver poses as UID 0, GID 0, PID 1.
> > +#
> > +# - If and only if all "w" bits are missing from a file on the host side, then
> > +#   the file or directory is reported as EFI_FILE_READ_ONLY in the guest. When
> > +#   setting EFI_FILE_READ_ONLY in the guest, all "w" bits (0222) are cleared on
> > +#   the host; when clearing EFI_FILE_READ_ONLY in the guest, all "w" bits are
> > +#   set on the host. Viewed from the host side, this sort of reflects that an
> > +#   EFI_FILE_READ_ONLY file should not be written by anyone.
> > +#
> > +# - The attributes EFI_FILE_HIDDEN, EFI_FILE_SYSTEM, EFI_FILE_RESERVED, and
> > +#   EFI_FILE_ARCHIVE are never reported in the guest, and they are silently
> > +#   ignored when a SetInfo() call or a file-creating Open() call requests them.
> > +#
> > +# - On the host, files are created with 0666 file mode bits, directories are
> > +#   created with 0777 file mode bits.
> > +#
> > +# - In the guest, the EFI_FILE_READ_ONLY attribute only controls the permitted
> > +#   open mode. In particular, on directories, the EFI_FILE_READ_ONLY attribute
> > +#   does not prevent the creation or deletion of entries inside the directory;
> > +#   EFI_FILE_READ_ONLY only prevents the renaming, deleting, flushing (syncing)
> > +#   and touching of the directory itself (with "touching" meaning updating the
> > +#   timestamps). The fact that EFI_FILE_READ_ONLY being set on a directory is
> > +#   irrelevant in the guest with regard to entry creation/deletion, is
> > +#   well-mirrored by the fact that virtiofsd -- which runs as root, regardless
> > +#   of guest driver personality -- ignores the absence of "w" permissions on a
> > +#   host-side directory, when creating or removing entries in it.
> > +#
> > +# - When an EFI_FILE_PROTOCOL is opened read-only, then the Delete(), Write()
> > +#   and Flush() member functions are disabled for it. Additionally, SetInfo()
> > +#   is restricted to flipping the EFI_FILE_READ_ONLY bit (which takes effect at
> > +#   the next Open()).
> > +#
> > +# - As a consequence of the above, for deleting a directory, it must be
> > +#   presented in the guest as openable for writing.
> > +#
> > +# - We diverge from the UEFI spec, and permit Flush() on a directory that has
> > +#   been opened read-write; otherwise the only way to invoke FUSE_FSYNCDIR on a
> > +#   directory would be to Close() it.
> > +#
> > +# - OpenVolume() opens the root directory for read-only access. The Open()
> > +#   member function may open it for read-write access. While the root directory
> > +#   cannot be renamed or deleted, opening it for read-write access is useful
> > +#   for calling Flush(), according to the previous paragraph, or for updating
> > +#   the root directory's timestamps with SetInfo().
> > +##
> > +
> > +[Defines]
> > +  INF_VERSION                           = 1.29
> > +  BASE_NAME                             = VirtioFsDxe
> > +  FILE_GUID                             = 7BD9DDF7-8B83-488E-AEC9-24C78610289C
> > +  MODULE_TYPE                           = UEFI_DRIVER
> > +  ENTRY_POINT                           = VirtioFsEntryPoint
> > +
> > +[Packages]
> > +  MdePkg/MdePkg.dec
> > +
> > +[Sources]
> > +  DriverBinding.c
> > +
> > +[LibraryClasses]
> > +  BaseLib
> > +  UefiBootServicesTableLib
> > +  UefiDriverEntryPoint
> > +
> > +[Protocols]
> > +  gEfiComponentName2ProtocolGuid        ## PRODUCES
> > +  gEfiDriverBindingProtocolGuid         ## PRODUCES
> > diff --git a/OvmfPkg/VirtioFsDxe/DriverBinding.c b/OvmfPkg/VirtioFsDxe/DriverBinding.c
> > new file mode 100644
> > index 000000000000..ac0a6330f01b
> > --- /dev/null
> > +++ b/OvmfPkg/VirtioFsDxe/DriverBinding.c
> > @@ -0,0 +1,112 @@
> > +/** @file
> > +  Provide EFI_SIMPLE_FILE_SYSTEM_PROTOCOL instances on virtio-fs devices.
> > +
> > +  Copyright (C) 2020, Red Hat, Inc.
> > +
> > +  SPDX-License-Identifier: BSD-2-Clause-Patent
> > +**/
> > +
> > +#include <Library/BaseLib.h>                  // AsciiStrCmp()
> > +#include <Library/UefiBootServicesTableLib.h> // gBS
> > +#include <Protocol/ComponentName2.h>          // EFI_COMPONENT_NAME2_PROTOCOL
> > +#include <Protocol/DriverBinding.h>           // EFI_DRIVER_BINDING_PROTOCOL
> > +
> > +//
> > +// UEFI Driver Model protocol instances.
> > +//
> > +STATIC EFI_DRIVER_BINDING_PROTOCOL  mDriverBinding;
> > +STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2;
> > +
> > +//
> > +// UEFI Driver Model protocol member functions.
> > +//
> > +EFI_STATUS
> > +EFIAPI
> > +VirtioFsBindingSupported (
> > +  IN EFI_DRIVER_BINDING_PROTOCOL *This,
> > +  IN EFI_HANDLE                  ControllerHandle,
> > +  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
> > +  )
> > +{
> > +  return EFI_UNSUPPORTED;
> > +}
> > +
> > +EFI_STATUS
> > +EFIAPI
> > +VirtioFsBindingStart (
> > +  IN EFI_DRIVER_BINDING_PROTOCOL *This,
> > +  IN EFI_HANDLE                  ControllerHandle,
> > +  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
> > +  )
> > +{
> > +  return EFI_DEVICE_ERROR;
> > +}
> > +
> > +EFI_STATUS
> > +EFIAPI
> > +VirtioFsBindingStop (
> > +  IN EFI_DRIVER_BINDING_PROTOCOL *This,
> > +  IN EFI_HANDLE                  ControllerHandle,
> > +  IN UINTN                       NumberOfChildren,
> > +  IN EFI_HANDLE                  *ChildHandleBuffer OPTIONAL
> > +  )
> > +{
> > +  return EFI_DEVICE_ERROR;
> > +}
> > +
> > +EFI_STATUS
> > +EFIAPI
> > +VirtioFsGetDriverName (
> > +  IN  EFI_COMPONENT_NAME2_PROTOCOL *This,
> > +  IN  CHAR8                        *Language,
> > +  OUT CHAR16                       **DriverName
> > +  )
> > +{
> > +  if (AsciiStrCmp (Language, "en") != 0) {
> > +    return EFI_UNSUPPORTED;
> > +  }
> > +  *DriverName = L"Virtio Filesystem Driver";
> > +  return EFI_SUCCESS;
> > +}
> > +
> > +EFI_STATUS
> > +EFIAPI
> > +VirtioFsGetControllerName (
> > +  IN  EFI_COMPONENT_NAME2_PROTOCOL *This,
> > +  IN  EFI_HANDLE                   ControllerHandle,
> > +  IN  EFI_HANDLE                   ChildHandle OPTIONAL,
> > +  IN  CHAR8                        *Language,
> > +  OUT CHAR16                       **ControllerName
> > +  )
> > +{
> > +  return EFI_UNSUPPORTED;
> > +}
> > +
> > +//
> > +// Entry point of this driver.
> > +//
> > +EFI_STATUS
> > +EFIAPI
> > +VirtioFsEntryPoint (
> > +  IN EFI_HANDLE       ImageHandle,
> > +  IN EFI_SYSTEM_TABLE *SystemTable
> > +  )
> > +{
> > +  EFI_STATUS Status;
> > +
> > +  mDriverBinding.Supported           = VirtioFsBindingSupported;
> > +  mDriverBinding.Start               = VirtioFsBindingStart;
> > +  mDriverBinding.Stop                = VirtioFsBindingStop;
> > +  mDriverBinding.Version             = 0x10;
> > +  mDriverBinding.ImageHandle         = ImageHandle;
> > +  mDriverBinding.DriverBindingHandle = ImageHandle;
> > +
> > +  mComponentName2.GetDriverName      = VirtioFsGetDriverName;
> > +  mComponentName2.GetControllerName  = VirtioFsGetControllerName;
> > +  mComponentName2.SupportedLanguages = "en";
> > +
> > +  Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle,
> > +                  &gEfiDriverBindingProtocolGuid, &mDriverBinding,
> > +                  &gEfiComponentName2ProtocolGuid, &mComponentName2, NULL);
> > +  return Status;
> > +}
> > 
> 
> 
> _______________________________________________
> Virtio-fs mailing list
> Virtio-fs@redhat.com
> https://www.redhat.com/mailman/listinfo/virtio-fs
-- 
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Virtio-fs] [edk2 PATCH 01/48] OvmfPkg: introduce VirtioFsDxe
  2020-12-18 18:13     ` [Virtio-fs] " Dr. David Alan Gilbert
@ 2020-12-19 21:16       ` Laszlo Ersek
  0 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-19 21:16 UTC (permalink / raw)
  To: Dr. David Alan Gilbert, Ard Biesheuvel
  Cc: devel, virtio-fs, Jordan Justen, Philippe Mathieu-Daudé

(I'm breaking my PTO rules for this, because writing this driver was a
very intense experience for me, and it's not like I can put it out of my
mind from a Friday to a Saturday, after waking with it and going to bed
with it for three weeks... I just couldn't resist checking the mailing
list archive, and then logging in.)

On 12/18/20 19:13, Dr. David Alan Gilbert wrote:
> * Ard Biesheuvel (ard.biesheuvel@arm.com) wrote:
>> On 12/16/20 10:10 PM, Laszlo Ersek wrote:

[...]

>>> +# Permission Model of this driver:
>>> +#
>>> +# Regardless of the UID and GID values this driver send in the FUSE request
>>> +# header, the daemon (that is, the Virtio Filesystem device) always acts with
>>> +# root privileges on the host side. The only time the daemon considers said UID
>>> +# and GID fields is when creating a new file or directory. Thus, the guest
>>> +# driver cannot rely on the host for enforcing any file mode permissions,
>>> +# regardless of the "personality" that the guest driver poses as, because
>>> +# "root" on the host side ignores all file mode bits.
>>> +#
>>> +# Therefore the guest driver has to do its own permission checking, and use the
>>> +# host-side file mode bits only as a kind of "metadata storage" or "reminder"
>>> +# -- hopefully in a way that makes some sense on the host side too.
>>> +#
>>
>> Can you please explain why this is safe? Or should virtio-fs only be
>> used with guests that can be trusted with root privileges on the host?
>
> The daemon sandboxes itself and generally you only expose a private area
> of a filesystem to the guest; i.e. a per-guest rootfs or temporary or
> whatever.

Stefan wrote a document about this:

  [PULL 059/111] virtiofsd: add security guide document
  https://lists.gnu.org/archive/html/qemu-devel/2020-01/msg05464.html

some excerpts:

> +Security Requirements
> +=====================
> +Guests have root access to the shared directory.  This is necessary for root
> +file systems on virtio-fs and similar use cases.

and

> +Deployment Best Practices
> +=========================
> +The shared directory should be a separate file system so that untrusted guests
> +cannot cause a denial-of-service by using up all available inodes or exhausting
> +free space.
> +
> +If the shared directory is also accessible from a host mount namespace, it is
> +recommended to keep a parent directory with rwx------ permissions so that other
> +users on the host are unable to access any setuid executables or device nodes
> +in the shared directory.  The `nosuid` and `nodev` mount options can also be
> +used to prevent this issue.

This document, originally proposed as
"docs/tools/virtiofsd-security.rst", doesn't seem to have made it to the
QEMU tree yet; it was put aside while a good location for it would be
figured out. See this subthread under the v1 PULL:

  https://lists.gnu.org/archive/html/qemu-devel/2020-01/msg05733.html

and then see the PULL v2 changelog -- "drop the docs while we discuss
where they should live":

  https://lists.gnu.org/archive/html/qemu-devel/2020-01/msg05780.html

(If there have been developments in this area since then, I'm not aware
of them; sorry if my info on the docs' location is out-of-date.)

Thanks!
Laszlo


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [edk2 PATCH 42/48] OvmfPkg/VirtioFsDxe: add helper for composing rename/move destination path
  2020-12-18 17:39   ` Ard Biesheuvel
@ 2020-12-19 22:40     ` Laszlo Ersek
  2020-12-19 22:54       ` Laszlo Ersek
  0 siblings, 1 reply; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-19 22:40 UTC (permalink / raw)
  To: Ard Biesheuvel, devel, virtio-fs
  Cc: Jordan Justen, Philippe Mathieu-Daudé

On 12/18/20 18:39, Ard Biesheuvel wrote:
> On 12/16/20 10:11 PM, Laszlo Ersek wrote:
>> The EFI_FILE_PROTOCOL.SetInfo() member is somewhat under-specified; one of
>> its modes of operation is renaming/moving the file.
>>
>> In order to create the destination pathname in canonical format, 2*2=4
>> cases have to be considered. For the sake of discussion, assume the
>> current canonical pathname of a VIRTIO_FS_FILE is "/home/user/f1.txt".
>> Then, consider the following rename/move requests from
>> EFI_FILE_PROTOCOL.SetInfo():
>>
>>   Destination requested  Destination  Move into   Destination in
>>   by SetInfo()           relative?    directory?  canonical format
>>   ---------------------  -----------  ----------  -----------------------
>>   L"\\dir\\f2.txt"       no           no          "/dir/f2.txt"
>
> What happens in the above case if /dir/f2.txt is an existing directory?


Short answer:

The present patch only constructs the destination pathname. The rename
attempt you describe is caught

- either by the subsequent patch, if the existing dest directory is open
  by the guest driver,

- or else, by the host kernel, due to the RENAME_NOREPLACE flag set in
  the patch before this one.


Long (very long) answer, in opposite order of the above cases:

- If "/dir/f2.txt" were an existing name (regardless of the type of the
  host-side inode that it referred to), then the FUSE_RENAME2 request
  would fail: the host kernel would reject the renameat2() system call
  made by virtiofsd. This would be due to the RENAME_NOREPLACE flag:

    https://man7.org/linux/man-pages/man2/rename.2.html
    include/uapi/linux/fs.h

  which is set in

    [edk2 PATCH 41/48]
    OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_RENAME2

  using the macro name VIRTIO_FS_FUSE_RENAME2_REQ_F_NOREPLACE.

  Thus, if the request reached the host kernel, an -EEXIST errno would
  come back to the guest driver.

(

- If the movement source were a non-directory and the destination were a
  directory, then that would fail (also in the host kernel at the
  latest) even with the simpler (flag-less) FUSE_RENAME request, which
  virtiofsd translates to the renameat() syscall, with -EISDIR.

- If both source and destination were directories, and the destination
  were not empty, then even the flag-less renameat() would fail with
  -ENOTEMPTY.

- If both source and destination were directories, and the destination
  were empty, then renameat() would replace the destination [*]; but
  renameat2() with RENAME_NOREPLACE will not.

  [*] mkdir source-dir target-dir
      ls -lid source-dir target-dir
      touch source-dir/file.txt
      mv -T source-dir target-dir
      ls -lid target-dir
      ls -l target-dir/file.txt

)


Then, in addition to RENAME_NOREPLACE, there's a guest-side check in

  [edk2 PATCH 43/48]
  OvmfPkg/VirtioFsDxe: handle file rename/move in EFI_FILE_PROTOCOL.SetInfo

that was inspired by "FatPkg/EnhancedFatDxe". Namely:

> +  //
> +  // Check if the rename would break the canonical pathnames of other
> +  // VIRTIO_FS_FILE instances of the same VIRTIO_FS.
> +  //
> +  if (VirtioFsFile->IsDirectory) {
> +    UINTN      PathLen;
> +    LIST_ENTRY *OpenFilesEntry;
> +
> +    PathLen = AsciiStrLen (VirtioFsFile->CanonicalPathname);
> +    BASE_LIST_FOR_EACH (OpenFilesEntry, &VirtioFs->OpenFiles) {
> +      VIRTIO_FS_FILE *OtherFile;
> +
> +      OtherFile = VIRTIO_FS_FILE_FROM_OPEN_FILES_ENTRY (OpenFilesEntry);
> +      if (OtherFile != VirtioFsFile &&
> +          AsciiStrnCmp (VirtioFsFile->CanonicalPathname,
> +            OtherFile->CanonicalPathname, PathLen) == 0 &&
> +          (OtherFile->CanonicalPathname[PathLen] == '\0' ||
> +           OtherFile->CanonicalPathname[PathLen] == '/')) {
> +        //
> +        // OtherFile refers to the same directory as VirtioFsFile, or is a
> +        // (possibly indirect) child of the directory referred to by
> +        // VirtioFsFile.
> +        //
> +        Status = EFI_ACCESS_DENIED;
> +        goto FreeDestination;
> +      }
> +    }
> +  }

This is why "VIRTIO_FS.OpenFiles" is a list, and not just a reference
count -- for this simple guest-side check, I needed to go through the
other open VIRTIO_FS_FILEs for the same VIRTIO_FS one by one. Just
knowing how many of them existed wouldn't be enough.

This guest-side check is by no means foolproof; after all, you can do
whatever you want on the host side, underneath the guest driver's feet.

But, catching such "async tricks" is not a goal for this driver.
EFI_FILE_PROTOCOL is not equipped to deal with such async changes by
design. At best, it can return EFI_MEDIA_CHANGED, when (e.g.) removable
media is replaced. But even if I could detect such a situation in the
virtio-fs driver, it would be counter-productive: when a host-side file
changes, that's something the guest wants to pick up one way or another,
we don't want the driver to switch to returning EFI_MEDIA_CHANGED for
all further requests indiscriminately.

Synchronization between host and guest is pretty simple for the
interactive use case: whenever your shell prompt returns, on the host or
in the guest (meaning the UEFI shell prompt in the latter), you can
Alt-TAB to the other terminal, and manipulate files from there.

Synchronization between host-side and guest-side scripts should be
possible with polling directory listings, and renaming / moving regular
files (files should be prepared in "private" directories, and then moved
into place when done, for the other side to notice).

So, the above guest-side check exists for the usual case when the
relevant sub-hierarchy of the shared directory doesn't change
asynchronously to VIRTIO_FS_FILE's being open; when the guest-side check
fires in this optimistic situation, the FUSE_RENAME2 request isn't even
sent. And for when the guest-side check isn't good enough, that's when
the RENAME_NOREPLACE flag becomes relevant -- I wanted to avoid
unwittingly deleting entries in the shared directory by guest-initiated
renames.

Sorry about the long answer. I feel it's not really possible to address
your question without talking about asynchrony between host and guest. I
had thought about it before, and I figured, shoehorning a shared
filesystem into a non-shared filesystem abstraction should be acceptable
this way. The use case is to support Alt-TABbing between your guest and
host terminals, and running such UEFI unit tests in the guest that take
input from the host filesystem and produce output to the host
filesystem. A highly async / parallel operation is a non-goal.

Thanks!
Laszlo


>
>>   L"\\dir\\"             no           yes         "/dir/f1.txt"
>>   L"dir\\f2.txt"         yes          no          "/home/user/dir/f2.txt"
>>   L"dir\\"               yes          yes         "/home/user/dir/f1.txt"
>>
>> Add the VirtioFsComposeRenameDestination() function, for composing the
>> last column from the current canonical pathname and the SetInfo() input.
>>
>> The function works on the following principles:
>>
>> - The prefix of the destination path is "/", if the SetInfo() rename
>>   request is absolute.
>>
>>   Otherwise, the dest prefix is the "current directory" (the most specific
>>   parent directory) of the original pathname (in the above example,
>>   "/home/user").
>>
>> - The suffix of the destination path is precisely the SetInfo() request
>>   string, if the "move into directory" convenience format -- the trailing
>>   backslash -- is not used. (In the above example, L"\\dir\\f2.txt" and
>>   L"dir\\f2.txt".)
>>
>>   Otherwise, the suffix is the SetInfo() request, plus the original
>>   basename (in the above example, L"\\dir\\f1.txt" and L"dir\\f1.txt").
>>
>> - The complete destination is created by fusing the dest prefix and the
>>   dest suffix, using the VirtioFsAppendPath() function.
>>
>> 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>
>> ---
>>  OvmfPkg/VirtioFsDxe/VirtioFsDxe.h |   8 +
>>  OvmfPkg/VirtioFsDxe/Helpers.c     | 194 ++++++++++++++++++++
>>  2 files changed, 202 insertions(+)
>>
>> diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
>> index 9334e5434c51..a6dfac71f4a7 100644
>> --- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
>> +++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
>> @@ -257,16 +257,24 @@ VirtioFsLookupMostSpecificParentDir (
>>
>>  EFI_STATUS
>>  VirtioFsGetBasename (
>>    IN     CHAR8  *Path,
>>       OUT CHAR16 *Basename     OPTIONAL,
>>    IN OUT UINTN  *BasenameSize
>>    );
>>
>> +EFI_STATUS
>> +VirtioFsComposeRenameDestination (
>> +  IN     CHAR8   *LhsPath8,
>> +  IN     CHAR16  *RhsPath16,
>> +     OUT CHAR8   **ResultPath8,
>> +     OUT BOOLEAN *RootEscape
>> +  );
>> +
>>  EFI_STATUS
>>  VirtioFsFuseAttrToEfiFileInfo (
>>    IN     VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr,
>>       OUT EFI_FILE_INFO                      *FileInfo
>>    );
>>
>>  EFI_STATUS
>>  VirtioFsFuseDirentPlusToEfiFileInfo (
>> diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c
>> index cdaa8557a17b..b741cf753495 100644
>> --- a/OvmfPkg/VirtioFsDxe/Helpers.c
>> +++ b/OvmfPkg/VirtioFsDxe/Helpers.c
>> @@ -1778,16 +1778,210 @@ VirtioFsGetBasename (
>>    }
>>
>>    for (Idx = LastComponent; Idx < PathSize; Idx++) {
>>      Basename[Idx - LastComponent] = Path[Idx];
>>    }
>>    return EFI_SUCCESS;
>>  }
>>
>> +/**
>> +  Format the destination of a rename/move operation as a dynamically allocated
>> +  canonical pathname.
>> +
>> +  Any dot-dot in RhsPath16 that would remove the root directory is dropped, and
>> +  reported through RootEscape, without failing the function call.
>> +
>> +  @param[in] LhsPath8     The source pathname operand of the rename/move
>> +                          operation, expressed as a canonical pathname (as
>> +                          defined in the description of VirtioFsAppendPath()).
>> +                          The root directory "/" cannot be renamed/moved, and
>> +                          will be rejected.
>> +
>> +  @param[in] RhsPath16    The destination pathname operand expressed as a
>> +                          UEFI-style CHAR16 pathname.
>> +
>> +                          If RhsPath16 starts with a backslash, then RhsPath16
>> +                          is considered absolute. Otherwise, RhsPath16 is
>> +                          interpreted relative to the most specific parent
>> +                          directory found in LhsPath8.
>> +
>> +                          Independently, if RhsPath16 ends with a backslash
>> +                          (i.e., RhsPath16 is given in the "move into
>> +                          directory" convenience form), then RhsPath16 is
>> +                          interpreted with the basename of LhsPath8 appended.
>> +                          Otherwise, the last pathname component of RhsPath16
>> +                          is taken as the last pathname component of the
>> +                          rename/move destination.
>> +
>> +                          An empty RhsPath16 is rejected.
>> +
>> +  @param[out] ResultPath8  The POSIX-style, canonical format pathname that
>> +                           leads to the renamed/moved file. After use, the
>> +                           caller is responsible for freeing ResultPath8.
>> +
>> +  @param[out] RootEscape   Set to TRUE if at least one dot-dot component in
>> +                           RhsPath16 attempted to escape the root directory;
>> +                           set to FALSE otherwise.
>> +
>> +  @retval EFI_SUCCESS            ResultPath8 has been produced. RootEscape has
>> +                                 been output.
>> +
>> +  @retval EFI_INVALID_PARAMETER  LhsPath8 is "/".
>> +
>> +  @retval EFI_INVALID_PARAMETER  RhsPath16 is zero-length.
>> +
>> +  @retval EFI_INVALID_PARAMETER  RhsPath16 failed the
>> +                                 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
>> +
>> +  @retval EFI_OUT_OF_RESOURCES   Memory allocation failed.
>> +
>> +  @retval EFI_OUT_OF_RESOURCES   ResultPath8 would have failed the
>> +                                 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
>> +
>> +  @retval EFI_UNSUPPORTED        RhsPath16 contains a character that either
>> +                                 falls outside of the printable ASCII set, or
>> +                                 is a forward slash.
>> +**/
>> +EFI_STATUS
>> +VirtioFsComposeRenameDestination (
>> +  IN     CHAR8   *LhsPath8,
>> +  IN     CHAR16  *RhsPath16,
>> +     OUT CHAR8   **ResultPath8,
>> +     OUT BOOLEAN *RootEscape
>> +  )
>> +{
>> +  //
>> +  // Lengths are expressed as numbers of characters (CHAR8 or CHAR16),
>> +  // excluding terminating NULs. Sizes are expressed as byte counts, including
>> +  // the bytes taken up by terminating NULs.
>> +  //
>> +  UINTN      RhsLen;
>> +  UINTN      LhsBasename16Size;
>> +  EFI_STATUS Status;
>> +  UINTN      LhsBasenameLen;
>> +  UINTN      DestSuffix16Size;
>> +  CHAR16     *DestSuffix16;
>> +  CHAR8      *DestPrefix8;
>> +
>> +  //
>> +  // An empty destination operand for the rename/move operation is not allowed.
>> +  //
>> +  RhsLen = StrLen (RhsPath16);
>> +  if (RhsLen == 0) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +  //
>> +  // Enforce length restriction on RhsPath16.
>> +  //
>> +  if (RhsLen > VIRTIO_FS_MAX_PATHNAME_LENGTH) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  //
>> +  // Determine the length of the basename of LhsPath8.
>> +  //
>> +  LhsBasename16Size = 0;
>> +  Status = VirtioFsGetBasename (LhsPath8, NULL, &LhsBasename16Size);
>> +  ASSERT (Status == EFI_BUFFER_TOO_SMALL);
>> +  ASSERT (LhsBasename16Size >= sizeof (CHAR16));
>> +  ASSERT (LhsBasename16Size % sizeof (CHAR16) == 0);
>> +  LhsBasenameLen = LhsBasename16Size / sizeof (CHAR16) - 1;
>> +  if (LhsBasenameLen == 0) {
>> +    //
>> +    // The root directory cannot be renamed/moved.
>> +    //
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  //
>> +  // Resolve the "move into directory" convenience form in RhsPath16.
>> +  //
>> +  if (RhsPath16[RhsLen - 1] == L'\\') {
>> +    //
>> +    // Append the basename of LhsPath8 as a CHAR16 string to RhsPath16.
>> +    //
>> +    DestSuffix16Size = RhsLen * sizeof (CHAR16) + LhsBasename16Size;
>> +    DestSuffix16 = AllocatePool (DestSuffix16Size);
>> +    if (DestSuffix16 == NULL) {
>> +      return EFI_OUT_OF_RESOURCES;
>> +    }
>> +    CopyMem (DestSuffix16, RhsPath16, RhsLen * sizeof (CHAR16));
>> +    Status = VirtioFsGetBasename (LhsPath8, DestSuffix16 + RhsLen,
>> +               &LhsBasename16Size);
>> +    ASSERT_EFI_ERROR (Status);
>> +  } else {
>> +    //
>> +    // Just create a copy of RhsPath16.
>> +    //
>> +    DestSuffix16Size = (RhsLen + 1) * sizeof (CHAR16);
>> +    DestSuffix16 = AllocateCopyPool (DestSuffix16Size, RhsPath16);
>> +    if (DestSuffix16 == NULL) {
>> +      return EFI_OUT_OF_RESOURCES;
>> +    }
>> +  }
>> +
>> +  //
>> +  // If the destination operand is absolute, it will be interpreted relative to
>> +  // the root directory.
>> +  //
>> +  // Otherwise (i.e., if the destination operand is relative), then create the
>> +  // canonical pathname that the destination operand is interpreted relatively
>> +  // to; that is, the canonical pathname of the most specific parent directory
>> +  // found in LhsPath8.
>> +  //
>> +  if (DestSuffix16[0] == L'\\') {
>> +    DestPrefix8 = AllocateCopyPool (sizeof "/", "/");
>> +    if (DestPrefix8 == NULL) {
>> +      Status = EFI_OUT_OF_RESOURCES;
>> +      goto FreeDestSuffix16;
>> +    }
>> +  } else {
>> +    UINTN LhsLen;
>> +    UINTN DestPrefixLen;
>> +
>> +    //
>> +    // Strip the basename of LhsPath8.
>> +    //
>> +    LhsLen = AsciiStrLen (LhsPath8);
>> +    ASSERT (LhsBasenameLen < LhsLen);
>> +    DestPrefixLen = LhsLen - LhsBasenameLen;
>> +    ASSERT (LhsPath8[DestPrefixLen - 1] == '/');
>> +    //
>> +    // If we're not at the root directory, strip the slash too.
>> +    //
>> +    if (DestPrefixLen > 1) {
>> +      DestPrefixLen--;
>> +    }
>> +    DestPrefix8 = AllocatePool (DestPrefixLen + 1);
>> +    if (DestPrefix8 == NULL) {
>> +      Status = EFI_OUT_OF_RESOURCES;
>> +      goto FreeDestSuffix16;
>> +    }
>> +    CopyMem (DestPrefix8, LhsPath8, DestPrefixLen);
>> +    DestPrefix8[DestPrefixLen] = '\0';
>> +  }
>> +
>> +  //
>> +  // Now combine DestPrefix8 and DestSuffix16 into the final canonical
>> +  // pathname.
>> +  //
>> +  Status = VirtioFsAppendPath (DestPrefix8, DestSuffix16, ResultPath8,
>> +             RootEscape);
>> +
>> +  FreePool (DestPrefix8);
>> +  //
>> +  // Fall through.
>> +  //
>> +FreeDestSuffix16:
>> +  FreePool (DestSuffix16);
>> +
>> +  return Status;
>> +}
>> +
>>  /**
>>    Convert select fields of a VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
>>    corresponding fields in EFI_FILE_INFO.
>>
>>    @param[in] FuseAttr   The VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
>>                          convert the relevant fields from.
>>
>>    @param[out] FileInfo  The EFI_FILE_INFO structure to modify. Importantly, the
>>
>


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [edk2 PATCH 42/48] OvmfPkg/VirtioFsDxe: add helper for composing rename/move destination path
  2020-12-19 22:40     ` Laszlo Ersek
@ 2020-12-19 22:54       ` Laszlo Ersek
  0 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-19 22:54 UTC (permalink / raw)
  To: Ard Biesheuvel, devel, virtio-fs
  Cc: Jordan Justen, Philippe Mathieu-Daudé

On 12/19/20 23:40, Laszlo Ersek wrote:
> On 12/18/20 18:39, Ard Biesheuvel wrote:
>> On 12/16/20 10:11 PM, Laszlo Ersek wrote:
>>> The EFI_FILE_PROTOCOL.SetInfo() member is somewhat under-specified; one of
>>> its modes of operation is renaming/moving the file.
>>>
>>> In order to create the destination pathname in canonical format, 2*2=4
>>> cases have to be considered. For the sake of discussion, assume the
>>> current canonical pathname of a VIRTIO_FS_FILE is "/home/user/f1.txt".
>>> Then, consider the following rename/move requests from
>>> EFI_FILE_PROTOCOL.SetInfo():
>>>
>>>   Destination requested  Destination  Move into   Destination in
>>>   by SetInfo()           relative?    directory?  canonical format
>>>   ---------------------  -----------  ----------  -----------------------
>>>   L"\\dir\\f2.txt"       no           no          "/dir/f2.txt"
>>
>> What happens in the above case if /dir/f2.txt is an existing directory?
> 
> 
> Short answer:
> 
> The present patch only constructs the destination pathname. The rename
> attempt you describe is caught
> 
> - either by the subsequent patch, if the existing dest directory is open
>   by the guest driver,
> 
> - or else, by the host kernel, due to the RENAME_NOREPLACE flag set in
>   the patch before this one.

... I'm sorry, I need to correct a bit: the guest-side check I quoted
below is not meant to protect from the destination being overwritten, it
is supposed to protect against the *source disappearing* (and against
breaking other VIRTIO_FS_FILEs' canonical pathnames due to that). So,
the answer to your question is: it is caught by RENAME_NOREPLACE.

Everything else I said stands, it's just that the particular guest-side
check is related to a different question -- namely, "what if
'/home/user/f1.txt' is a directory".

Thanks, and sorry about the confusion,
Laszlo


> Long (very long) answer, in opposite order of the above cases:
> 
> - If "/dir/f2.txt" were an existing name (regardless of the type of the
>   host-side inode that it referred to), then the FUSE_RENAME2 request
>   would fail: the host kernel would reject the renameat2() system call
>   made by virtiofsd. This would be due to the RENAME_NOREPLACE flag:
> 
>     https://man7.org/linux/man-pages/man2/rename.2.html
>     include/uapi/linux/fs.h
> 
>   which is set in
> 
>     [edk2 PATCH 41/48]
>     OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_RENAME2
> 
>   using the macro name VIRTIO_FS_FUSE_RENAME2_REQ_F_NOREPLACE.
> 
>   Thus, if the request reached the host kernel, an -EEXIST errno would
>   come back to the guest driver.
> 
> (
> 
> - If the movement source were a non-directory and the destination were a
>   directory, then that would fail (also in the host kernel at the
>   latest) even with the simpler (flag-less) FUSE_RENAME request, which
>   virtiofsd translates to the renameat() syscall, with -EISDIR.
> 
> - If both source and destination were directories, and the destination
>   were not empty, then even the flag-less renameat() would fail with
>   -ENOTEMPTY.
> 
> - If both source and destination were directories, and the destination
>   were empty, then renameat() would replace the destination [*]; but
>   renameat2() with RENAME_NOREPLACE will not.
> 
>   [*] mkdir source-dir target-dir
>       ls -lid source-dir target-dir
>       touch source-dir/file.txt
>       mv -T source-dir target-dir
>       ls -lid target-dir
>       ls -l target-dir/file.txt
> 
> )
> 
> 
> Then, in addition to RENAME_NOREPLACE, there's a guest-side check in
> 
>   [edk2 PATCH 43/48]
>   OvmfPkg/VirtioFsDxe: handle file rename/move in EFI_FILE_PROTOCOL.SetInfo
> 
> that was inspired by "FatPkg/EnhancedFatDxe". Namely:
> 
>> +  //
>> +  // Check if the rename would break the canonical pathnames of other
>> +  // VIRTIO_FS_FILE instances of the same VIRTIO_FS.
>> +  //
>> +  if (VirtioFsFile->IsDirectory) {
>> +    UINTN      PathLen;
>> +    LIST_ENTRY *OpenFilesEntry;
>> +
>> +    PathLen = AsciiStrLen (VirtioFsFile->CanonicalPathname);
>> +    BASE_LIST_FOR_EACH (OpenFilesEntry, &VirtioFs->OpenFiles) {
>> +      VIRTIO_FS_FILE *OtherFile;
>> +
>> +      OtherFile = VIRTIO_FS_FILE_FROM_OPEN_FILES_ENTRY (OpenFilesEntry);
>> +      if (OtherFile != VirtioFsFile &&
>> +          AsciiStrnCmp (VirtioFsFile->CanonicalPathname,
>> +            OtherFile->CanonicalPathname, PathLen) == 0 &&
>> +          (OtherFile->CanonicalPathname[PathLen] == '\0' ||
>> +           OtherFile->CanonicalPathname[PathLen] == '/')) {
>> +        //
>> +        // OtherFile refers to the same directory as VirtioFsFile, or is a
>> +        // (possibly indirect) child of the directory referred to by
>> +        // VirtioFsFile.
>> +        //
>> +        Status = EFI_ACCESS_DENIED;
>> +        goto FreeDestination;
>> +      }
>> +    }
>> +  }
> 
> This is why "VIRTIO_FS.OpenFiles" is a list, and not just a reference
> count -- for this simple guest-side check, I needed to go through the
> other open VIRTIO_FS_FILEs for the same VIRTIO_FS one by one. Just
> knowing how many of them existed wouldn't be enough.
> 
> This guest-side check is by no means foolproof; after all, you can do
> whatever you want on the host side, underneath the guest driver's feet.
> 
> But, catching such "async tricks" is not a goal for this driver.
> EFI_FILE_PROTOCOL is not equipped to deal with such async changes by
> design. At best, it can return EFI_MEDIA_CHANGED, when (e.g.) removable
> media is replaced. But even if I could detect such a situation in the
> virtio-fs driver, it would be counter-productive: when a host-side file
> changes, that's something the guest wants to pick up one way or another,
> we don't want the driver to switch to returning EFI_MEDIA_CHANGED for
> all further requests indiscriminately.
> 
> Synchronization between host and guest is pretty simple for the
> interactive use case: whenever your shell prompt returns, on the host or
> in the guest (meaning the UEFI shell prompt in the latter), you can
> Alt-TAB to the other terminal, and manipulate files from there.
> 
> Synchronization between host-side and guest-side scripts should be
> possible with polling directory listings, and renaming / moving regular
> files (files should be prepared in "private" directories, and then moved
> into place when done, for the other side to notice).
> 
> So, the above guest-side check exists for the usual case when the
> relevant sub-hierarchy of the shared directory doesn't change
> asynchronously to VIRTIO_FS_FILE's being open; when the guest-side check
> fires in this optimistic situation, the FUSE_RENAME2 request isn't even
> sent. And for when the guest-side check isn't good enough, that's when
> the RENAME_NOREPLACE flag becomes relevant -- I wanted to avoid
> unwittingly deleting entries in the shared directory by guest-initiated
> renames.
> 
> Sorry about the long answer. I feel it's not really possible to address
> your question without talking about asynchrony between host and guest. I
> had thought about it before, and I figured, shoehorning a shared
> filesystem into a non-shared filesystem abstraction should be acceptable
> this way. The use case is to support Alt-TABbing between your guest and
> host terminals, and running such UEFI unit tests in the guest that take
> input from the host filesystem and produce output to the host
> filesystem. A highly async / parallel operation is a non-goal.
> 
> Thanks!
> Laszlo


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver
  2020-12-18 17:44 ` [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Ard Biesheuvel
@ 2020-12-20  0:09   ` Laszlo Ersek
  2020-12-20 10:15     ` Ard Biesheuvel
  0 siblings, 1 reply; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-20  0:09 UTC (permalink / raw)
  To: Ard Biesheuvel, devel, virtio-fs
  Cc: Jordan Justen, Leif Lindholm, Philippe Mathieu-Daudé

On 12/18/20 18:44, Ard Biesheuvel wrote:
> On 12/16/20 10:10 PM, Laszlo Ersek wrote:
>> Ref:    https://bugzilla.tianocore.org/show_bug.cgi?id=3097
>> Repo:   https://pagure.io/lersek/edk2.git
>> Branch: virtio-fs (@ b8fd76d649d2)
>>
>> The first commit and the bugzilla ticket state the use case.
>>
>> References, including setup instructions:
>> - https://libvirt.org/kbase/virtiofs.html
>> - https://virtio-fs.gitlab.io/
>>
>> Useful UEFI shell commands for testing: output redirections, attrib,
>> connect, cp, disconnect, edit, eficompress, efidecompress, hexedit, ls,
>> map, mkdir, mv, rm, setsize, timezone, touch, type, vol.
>>
>> The series is largely structured as follows:
>> - helper functions and FUSE command wrappers are implemented as required
>>   by the next EFI_FILE_PROTOCOL interface,
>> - said EFI_FILE_PROTOCOL interface is implemented,
>> - lather, rinse, repeat.
>>
>> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Leif Lindholm <leif@nuviainc.com>
>> Cc: Philippe Mathieu-Daudé <philmd@redhat.com>
>>
>> Thanks,
>> Laszlo
>>
>> Laszlo Ersek (48):
>>   OvmfPkg: introduce VirtioFsDxe
>>   ArmVirtPkg: include VirtioFsDxe in the ArmVirtQemu* platforms
>>   OvmfPkg/VirtioFsDxe: DriverBinding: open VirtioDevice, install
>>     SimpleFs
>>   OvmfPkg/VirtioFsDxe: implement virtio device (un)initialization
>>   OvmfPkg/VirtioFsDxe: add a scatter-gather list data type
>>   OvmfPkg/VirtioFsDxe: introduce the basic FUSE request/response headers
>>   OvmfPkg/VirtioFsDxe: map "errno" values to EFI_STATUS
>>   OvmfPkg/VirtioFsDxe: submit the FUSE_INIT request to the device
>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_OPENDIR
>>   OvmfPkg/VirtioFsDxe: add shared wrapper for FUSE_RELEASE /
>>     FUSE_RELEASEDIR
>>   OvmfPkg/VirtioFsDxe: implement
>>     EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume()
>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_FORGET
>>   OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_FSYNC /
>>     FUSE_FSYNCDIR
>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_FLUSH
>>   OvmfPkg/VirtioFsDxe: flush, sync, release and forget in Close() /
>>     Delete()
>>   OvmfPkg/VirtioFsDxe: add helper for appending and sanitizing paths
>>   OvmfPkg/VirtioFsDxe: manage path lifecycle in OpenVolume, Close,
>>     Delete
>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_OPEN
>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_MKDIR
>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_CREATE
>>   OvmfPkg/VirtioFsDxe: convert FUSE inode attributes to EFI_FILE_INFO
>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_LOOKUP
>>   OvmfPkg/VirtioFsDxe: split canon. path into last parent + last
>>     component
>>   OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_UNLINK / FUSE_RMDIR
>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_GETATTR
>>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Open()
>>   OvmfPkg/VirtioFsDxe: erase the dir. entry in
>>     EFI_FILE_PROTOCOL.Delete()
>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_STATFS
>>   OvmfPkg/VirtioFsDxe: add helper for formatting UEFI basenames
>>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.GetInfo()
>>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.GetPosition,
>>     .SetPosition
>>   OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_READ /
>>     FUSE_READDIRPLUS
>>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Read() for regular
>>     files
>>   OvmfPkg/VirtioFsDxe: convert FUSE dirent filename to EFI_FILE_INFO
>>   OvmfPkg/VirtioFsDxe: add EFI_FILE_INFO cache fields to VIRTIO_FS_FILE
>>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Read() for
>>     directories
>>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Flush()
>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_WRITE
>>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Write()
>>   OvmfPkg/VirtioFsDxe: handle the volume label in
>>     EFI_FILE_PROTOCOL.SetInfo
>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_RENAME2
>>   OvmfPkg/VirtioFsDxe: add helper for composing rename/move destination
>>     path
>>   OvmfPkg/VirtioFsDxe: handle file rename/move in
>>     EFI_FILE_PROTOCOL.SetInfo
>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_SETATTR
>>   OvmfPkg/VirtioFsDxe: add helper for determining file size update
>>   OvmfPkg/VirtioFsDxe: add helper for determining access time updates
>>   OvmfPkg/VirtioFsDxe: add helper for determining file mode bits update
>>   OvmfPkg/VirtioFsDxe: handle attribute updates in
>>     EFI_FILE_PROTOCOL.SetInfo
>>
>
> This looks carefully crafted, and modulo my two questions, I won't be
> able to spend more time on this than I have going through these
> patches today.
>
> So please feel free to merge this whenever you feel it's ready.
>
> For the series,
>
> Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com>

Thank you.

Before I posted the series, I tested it very thoroughly, including build
tests (at every patch, and a Local CI run as well). I created a test
plan and went through it meticulously (it took hours).

The only thing I couldn't test that way was (obviously) building on
Windows. So clearly now that I've tried merging the series at
<https://github.com/tianocore/edk2/pull/1248>, that's what fails.
Because, this is the first time that EmbeddedPkg/TimeBaseLib is seen by
the Visual Studio compiler, and Visual Studio complains:

> ERROR - Compiler #2220 from
> d:\a\1\s\EmbeddedPkg\Library\TimeBaseLib\TimeBaseLib.c(111):   the
> following warning is treated as an error
> WARNING - Compiler #4244 from
> d:\a\1\s\EmbeddedPkg\Library\TimeBaseLib\TimeBaseLib.c(111):   '=':
> conversion from 'UINTN' to 'UINT32', possible loss of data

It happens to be correct:

    99  /**
   100    Converts EFI_TIME to Epoch seconds (elapsed since 1970 JANUARY 01, 00:00:00 UTC)
   101   **/
   102  UINT32
   103  EFIAPI
   104  EfiTimeToEpoch (
   105    IN  EFI_TIME  *Time
   106    )
   107  {
   108    UINT32 EpochDays;   // Number of days elapsed since EPOCH_JULIAN_DAY
   109    UINT32 EpochSeconds;
   110
   111    EpochDays = EfiGetEpochDays (Time);
   112
   113    EpochSeconds = (EpochDays * SEC_PER_DAY) + ((UINTN)Time->Hour * SEC_PER_HOUR) + (Time->Minute * SEC_PER_MIN) + Time->Second;
   114
   115    return EpochSeconds;
   116  }

This was discussed on the list earlier, when the function was duplicated
for the HTTP dynamic command:

- https://edk2.groups.io/g/devel/message/64933
- https://edk2.groups.io/g/devel/message/65186

Compare EfiTimeToEpoch() in
"ShellPkg/DynamicCommand/HttpDynamicCommand/Http.c".

   430  STATIC
   431  UINTN
   432  EFIAPI
   433  EfiTimeToEpoch (
   434    IN  EFI_TIME  *Time
   435    )
   436  {
   437    //
   438    // Number of days elapsed since EPOCH_JULIAN_DAY.
   439    //
   440    UINTN EpochDays;
   441    UINTN EpochSeconds;
   442
   443    EpochDays = EfiGetEpochDays (Time);
   444
   445    EpochSeconds = (EpochDays * SEC_PER_DAY) +
   446                   ((UINTN)Time->Hour * SEC_PER_HOUR) +
   447                   (Time->Minute * SEC_PER_MIN) + Time->Second;
   448
   449    return EpochSeconds;
   450  }

So, in order to merge this series, I'll first have to fix
EfiTimeToEpoch() in EmbeddedPkg :(

I wish the fixed version in
"ShellPkg/DynamicCommand/HttpDynamicCommand/Http.c" had been contributed
back to EmbeddedPkg.

Anyway, that is for 2021.

Thank you for checking the series so quickly; I know it's a lot!
Laszlo


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver
  2020-12-20  0:09   ` Laszlo Ersek
@ 2020-12-20 10:15     ` Ard Biesheuvel
  2020-12-21  1:46       ` Laszlo Ersek
  0 siblings, 1 reply; 65+ messages in thread
From: Ard Biesheuvel @ 2020-12-20 10:15 UTC (permalink / raw)
  To: Laszlo Ersek, devel, virtio-fs
  Cc: Jordan Justen, Leif Lindholm, Philippe Mathieu-Daudé

On 12/20/20 1:09 AM, Laszlo Ersek wrote:
> On 12/18/20 18:44, Ard Biesheuvel wrote:
>> On 12/16/20 10:10 PM, Laszlo Ersek wrote:
>>> Ref:    https://bugzilla.tianocore.org/show_bug.cgi?id=3097
>>> Repo:   https://pagure.io/lersek/edk2.git
>>> Branch: virtio-fs (@ b8fd76d649d2)
>>>
>>> The first commit and the bugzilla ticket state the use case.
>>>
>>> References, including setup instructions:
>>> - https://libvirt.org/kbase/virtiofs.html
>>> - https://virtio-fs.gitlab.io/
>>>
>>> Useful UEFI shell commands for testing: output redirections, attrib,
>>> connect, cp, disconnect, edit, eficompress, efidecompress, hexedit, ls,
>>> map, mkdir, mv, rm, setsize, timezone, touch, type, vol.
>>>
>>> The series is largely structured as follows:
>>> - helper functions and FUSE command wrappers are implemented as required
>>>   by the next EFI_FILE_PROTOCOL interface,
>>> - said EFI_FILE_PROTOCOL interface is implemented,
>>> - lather, rinse, repeat.
>>>
>>> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
>>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>>> Cc: Leif Lindholm <leif@nuviainc.com>
>>> Cc: Philippe Mathieu-Daudé <philmd@redhat.com>
>>>
>>> Thanks,
>>> Laszlo
>>>
>>> Laszlo Ersek (48):
>>>   OvmfPkg: introduce VirtioFsDxe
>>>   ArmVirtPkg: include VirtioFsDxe in the ArmVirtQemu* platforms
>>>   OvmfPkg/VirtioFsDxe: DriverBinding: open VirtioDevice, install
>>>     SimpleFs
>>>   OvmfPkg/VirtioFsDxe: implement virtio device (un)initialization
>>>   OvmfPkg/VirtioFsDxe: add a scatter-gather list data type
>>>   OvmfPkg/VirtioFsDxe: introduce the basic FUSE request/response headers
>>>   OvmfPkg/VirtioFsDxe: map "errno" values to EFI_STATUS
>>>   OvmfPkg/VirtioFsDxe: submit the FUSE_INIT request to the device
>>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_OPENDIR
>>>   OvmfPkg/VirtioFsDxe: add shared wrapper for FUSE_RELEASE /
>>>     FUSE_RELEASEDIR
>>>   OvmfPkg/VirtioFsDxe: implement
>>>     EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume()
>>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_FORGET
>>>   OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_FSYNC /
>>>     FUSE_FSYNCDIR
>>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_FLUSH
>>>   OvmfPkg/VirtioFsDxe: flush, sync, release and forget in Close() /
>>>     Delete()
>>>   OvmfPkg/VirtioFsDxe: add helper for appending and sanitizing paths
>>>   OvmfPkg/VirtioFsDxe: manage path lifecycle in OpenVolume, Close,
>>>     Delete
>>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_OPEN
>>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_MKDIR
>>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_CREATE
>>>   OvmfPkg/VirtioFsDxe: convert FUSE inode attributes to EFI_FILE_INFO
>>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_LOOKUP
>>>   OvmfPkg/VirtioFsDxe: split canon. path into last parent + last
>>>     component
>>>   OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_UNLINK / FUSE_RMDIR
>>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_GETATTR
>>>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Open()
>>>   OvmfPkg/VirtioFsDxe: erase the dir. entry in
>>>     EFI_FILE_PROTOCOL.Delete()
>>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_STATFS
>>>   OvmfPkg/VirtioFsDxe: add helper for formatting UEFI basenames
>>>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.GetInfo()
>>>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.GetPosition,
>>>     .SetPosition
>>>   OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_READ /
>>>     FUSE_READDIRPLUS
>>>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Read() for regular
>>>     files
>>>   OvmfPkg/VirtioFsDxe: convert FUSE dirent filename to EFI_FILE_INFO
>>>   OvmfPkg/VirtioFsDxe: add EFI_FILE_INFO cache fields to VIRTIO_FS_FILE
>>>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Read() for
>>>     directories
>>>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Flush()
>>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_WRITE
>>>   OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Write()
>>>   OvmfPkg/VirtioFsDxe: handle the volume label in
>>>     EFI_FILE_PROTOCOL.SetInfo
>>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_RENAME2
>>>   OvmfPkg/VirtioFsDxe: add helper for composing rename/move destination
>>>     path
>>>   OvmfPkg/VirtioFsDxe: handle file rename/move in
>>>     EFI_FILE_PROTOCOL.SetInfo
>>>   OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_SETATTR
>>>   OvmfPkg/VirtioFsDxe: add helper for determining file size update
>>>   OvmfPkg/VirtioFsDxe: add helper for determining access time updates
>>>   OvmfPkg/VirtioFsDxe: add helper for determining file mode bits update
>>>   OvmfPkg/VirtioFsDxe: handle attribute updates in
>>>     EFI_FILE_PROTOCOL.SetInfo
>>>
>>
>> This looks carefully crafted, and modulo my two questions, I won't be
>> able to spend more time on this than I have going through these
>> patches today.
>>
>> So please feel free to merge this whenever you feel it's ready.
>>
>> For the series,
>>
>> Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
> 
> Thank you.
> 
> Before I posted the series, I tested it very thoroughly, including build
> tests (at every patch, and a Local CI run as well). I created a test
> plan and went through it meticulously (it took hours).
> 
> The only thing I couldn't test that way was (obviously) building on
> Windows. So clearly now that I've tried merging the series at
> <https://github.com/tianocore/edk2/pull/1248>, that's what fails.
> Because, this is the first time that EmbeddedPkg/TimeBaseLib is seen by
> the Visual Studio compiler, and Visual Studio complains:
> 
>> ERROR - Compiler #2220 from
>> d:\a\1\s\EmbeddedPkg\Library\TimeBaseLib\TimeBaseLib.c(111):   the
>> following warning is treated as an error
>> WARNING - Compiler #4244 from
>> d:\a\1\s\EmbeddedPkg\Library\TimeBaseLib\TimeBaseLib.c(111):   '=':
>> conversion from 'UINTN' to 'UINT32', possible loss of data
> 
> It happens to be correct:
> 
>     99  /**
>    100    Converts EFI_TIME to Epoch seconds (elapsed since 1970 JANUARY 01, 00:00:00 UTC)
>    101   **/
>    102  UINT32
>    103  EFIAPI
>    104  EfiTimeToEpoch (
>    105    IN  EFI_TIME  *Time
>    106    )
>    107  {
>    108    UINT32 EpochDays;   // Number of days elapsed since EPOCH_JULIAN_DAY
>    109    UINT32 EpochSeconds;
>    110
>    111    EpochDays = EfiGetEpochDays (Time);
>    112
>    113    EpochSeconds = (EpochDays * SEC_PER_DAY) + ((UINTN)Time->Hour * SEC_PER_HOUR) + (Time->Minute * SEC_PER_MIN) + Time->Second;
>    114
>    115    return EpochSeconds;
>    116  }
> 
> This was discussed on the list earlier, when the function was duplicated
> for the HTTP dynamic command:
> 
> - https://edk2.groups.io/g/devel/message/64933
> - https://edk2.groups.io/g/devel/message/65186
> 
> Compare EfiTimeToEpoch() in
> "ShellPkg/DynamicCommand/HttpDynamicCommand/Http.c".
> 
>    430  STATIC
>    431  UINTN
>    432  EFIAPI
>    433  EfiTimeToEpoch (
>    434    IN  EFI_TIME  *Time
>    435    )
>    436  {
>    437    //
>    438    // Number of days elapsed since EPOCH_JULIAN_DAY.
>    439    //
>    440    UINTN EpochDays;
>    441    UINTN EpochSeconds;
>    442
>    443    EpochDays = EfiGetEpochDays (Time);
>    444
>    445    EpochSeconds = (EpochDays * SEC_PER_DAY) +
>    446                   ((UINTN)Time->Hour * SEC_PER_HOUR) +
>    447                   (Time->Minute * SEC_PER_MIN) + Time->Second;
>    448
>    449    return EpochSeconds;
>    450  }
> 
> So, in order to merge this series, I'll first have to fix
> EfiTimeToEpoch() in EmbeddedPkg :(
> 
> I wish the fixed version in
> "ShellPkg/DynamicCommand/HttpDynamicCommand/Http.c" had been contributed
> back to EmbeddedPkg.
> 
> Anyway, that is for 2021.
> 
> Thank you for checking the series so quickly; I know it's a lot!
> Laszlo
> 

Thanks for clearing up the outstanding questions.

For the EmbeddedPkg change from UINT32 to UINTN

Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com>

although I suppose you will have to preserve the prototype, so adding a
(UINT32) cast to line 111 is probably the best approach here.

-- 
Ard.




^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver
  2020-12-20 10:15     ` Ard Biesheuvel
@ 2020-12-21  1:46       ` Laszlo Ersek
  2020-12-21 10:10         ` Ard Biesheuvel
  0 siblings, 1 reply; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-21  1:46 UTC (permalink / raw)
  To: Ard Biesheuvel, devel, virtio-fs
  Cc: Jordan Justen, Leif Lindholm, Philippe Mathieu-Daudé

On 12/20/20 11:15, Ard Biesheuvel wrote:
> On 12/20/20 1:09 AM, Laszlo Ersek wrote:

>> The only thing I couldn't test that way was (obviously) building on
>> Windows. So clearly now that I've tried merging the series at
>> <https://github.com/tianocore/edk2/pull/1248>, that's what fails.
>> Because, this is the first time that EmbeddedPkg/TimeBaseLib is seen
>> by the Visual Studio compiler, and Visual Studio complains:
>>
>>> ERROR - Compiler #2220 from
>>> d:\a\1\s\EmbeddedPkg\Library\TimeBaseLib\TimeBaseLib.c(111):   the
>>> following warning is treated as an error
>>> WARNING - Compiler #4244 from
>>> d:\a\1\s\EmbeddedPkg\Library\TimeBaseLib\TimeBaseLib.c(111):   '=':
>>> conversion from 'UINTN' to 'UINT32', possible loss of data
>>
>> It happens to be correct:
>>
>>     99  /**
>>    100    Converts EFI_TIME to Epoch seconds (elapsed since 1970 JANUARY 01, 00:00:00 UTC)
>>    101   **/
>>    102  UINT32
>>    103  EFIAPI
>>    104  EfiTimeToEpoch (
>>    105    IN  EFI_TIME  *Time
>>    106    )
>>    107  {
>>    108    UINT32 EpochDays;   // Number of days elapsed since EPOCH_JULIAN_DAY
>>    109    UINT32 EpochSeconds;
>>    110
>>    111    EpochDays = EfiGetEpochDays (Time);
>>    112
>>    113    EpochSeconds = (EpochDays * SEC_PER_DAY) + ((UINTN)Time->Hour * SEC_PER_HOUR) + (Time->Minute * SEC_PER_MIN) + Time->Second;
>>    114
>>    115    return EpochSeconds;
>>    116  }
>>
>> This was discussed on the list earlier, when the function was duplicated
>> for the HTTP dynamic command:
>>
>> - https://edk2.groups.io/g/devel/message/64933
>> - https://edk2.groups.io/g/devel/message/65186
>>
>> Compare EfiTimeToEpoch() in
>> "ShellPkg/DynamicCommand/HttpDynamicCommand/Http.c".
>>
>>    430  STATIC
>>    431  UINTN
>>    432  EFIAPI
>>    433  EfiTimeToEpoch (
>>    434    IN  EFI_TIME  *Time
>>    435    )
>>    436  {
>>    437    //
>>    438    // Number of days elapsed since EPOCH_JULIAN_DAY.
>>    439    //
>>    440    UINTN EpochDays;
>>    441    UINTN EpochSeconds;
>>    442
>>    443    EpochDays = EfiGetEpochDays (Time);
>>    444
>>    445    EpochSeconds = (EpochDays * SEC_PER_DAY) +
>>    446                   ((UINTN)Time->Hour * SEC_PER_HOUR) +
>>    447                   (Time->Minute * SEC_PER_MIN) + Time->Second;
>>    448
>>    449    return EpochSeconds;
>>    450  }
>>
>> So, in order to merge this series, I'll first have to fix
>> EfiTimeToEpoch() in EmbeddedPkg :(
>>
>> I wish the fixed version in
>> "ShellPkg/DynamicCommand/HttpDynamicCommand/Http.c" had been contributed
>> back to EmbeddedPkg.
>>
>> Anyway, that is for 2021.

(given the fantastic opportunities provided by the COVID-19 pandemic, to
catch up with family and friends around Christmas /s, I might as well
treat the discussion of this patch series during my PTO as something
welcome that takes my mind off of how much I miss the people I can't
meet this year)

> Thanks for clearing up the outstanding questions.
>
> For the EmbeddedPkg change from UINT32 to UINTN
>
> Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
>
> although I suppose you will have to preserve the prototype, so adding
> a (UINT32) cast to line 111 is probably the best approach here.

I ended up installing a brand new Windows 10 + VS2019 virtual machine,
as I got fed up with the nasty surprises in the PRs (and I refuse to
publish my work-in-progress branch just for the sake of setting off CI
on it).

Two consequences:

(1) I'll squash the following trivial updates into two of the patches,
for my next merge request attempt:

 5:  bb254f89067a !  7:  17a76bbec317 OvmfPkg/VirtioFsDxe: add a scatter-gather list data type
    @@ -20,6 +20,8 @@
         Signed-off-by: Laszlo Ersek <lersek@redhat.com>
         Message-Id: <20201216211125.19496-6-lersek@redhat.com>
         Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
    +    [lersek@redhat.com: suppress useless VS2019 warning about signed/unsigned
    +     comparison in VirtioFsSgListsValidate()]

     diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
     --- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
    @@ -213,7 +215,7 @@
     +    // can be added to the virtio queue, after the other descriptors added
     +    // previously.
     +    //
    -+    if (SgList->NumVec > MAX_UINT16 - DescriptorsNeeded ||
    ++    if (SgList->NumVec > (UINTN)(MAX_UINT16 - DescriptorsNeeded) ||
     +        DescriptorsNeeded + SgList->NumVec > VirtioFs->QueueSize) {
     +      return EFI_UNSUPPORTED;
     +    }

and

46:  4f42ecc2d9bb ! 48:  b807d3c0b54b OvmfPkg/VirtioFsDxe: add helper for determining access time updates
    @@ -12,6 +12,8 @@
         Signed-off-by: Laszlo Ersek <lersek@redhat.com>
         Message-Id: <20201216211125.19496-47-lersek@redhat.com>
         Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
    +    [lersek@redhat.com: suppress bogus VS2019 warning about lack of
    +     initialization for ZeroTime]

     diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
     --- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
    @@ -92,7 +94,7 @@
     +  EFI_TIME              *Time[3];
     +  EFI_TIME              *NewTime[ARRAY_SIZE (Time)];
     +  UINTN                 Idx;
    -+  STATIC CONST EFI_TIME ZeroTime;
    ++  STATIC CONST EFI_TIME ZeroTime = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
     +  BOOLEAN               Change[ARRAY_SIZE (Time)];
     +  UINT64                Seconds[ARRAY_SIZE (Time)];
     +

(2) I've written three patches in total for fixing EfiTimeToEpoch(),
including the prototype:

(2a) edk2-platforms:

  1  Silicon/Marvell/RealTimeClockLib: make EpochSeconds, WakeupSeconds UINTN

     Silicon/Marvell/Armada7k8k/Library/RealTimeClockLib/RealTimeClockLib.c | 14 ++++++++++----
     1 file changed, 10 insertions(+), 4 deletions(-)

(other RealTimeClockLib instances / EfiTimeToEpoch() callers in
edk2-platforms need no updates; furthermore, edk2-non-osi contains no
EfiTimeToEpoch() calls at all)

(2b) edk2:

  1  ArmPlatformPkg/PL031RealTimeClockLib: cast EfiTimeToEpoch() val. to UINT32

     ArmPlatformPkg/Library/PL031RealTimeClockLib/PL031RealTimeClockLib.c | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)

  2  EmbeddedPkg/TimeBaseLib: remove useless truncation to 32-bit

     EmbeddedPkg/Include/Library/TimeBaseLib.h     | 2 +-
     EmbeddedPkg/Library/TimeBaseLib/TimeBaseLib.c | 6 +++---
     2 files changed, 4 insertions(+), 4 deletions(-)

If you're reading this before 2021, please let me know if you'd tolerate
receiving these patches for approval still in 2020. (The edk2-platforms
patch theoretically belongs to Leif and Marcin, but if Leif has stopped
consuming work email (which we all should have by now, I guess...), then
I believe you could ACK that patch in Leif's stead.)

Thanks,
Laszlo


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver
  2020-12-21  1:46       ` Laszlo Ersek
@ 2020-12-21 10:10         ` Ard Biesheuvel
  2020-12-21 18:02           ` [edk2-devel] " Laszlo Ersek
  0 siblings, 1 reply; 65+ messages in thread
From: Ard Biesheuvel @ 2020-12-21 10:10 UTC (permalink / raw)
  To: Laszlo Ersek, devel, virtio-fs
  Cc: Jordan Justen, Leif Lindholm, Philippe Mathieu-Daudé

On 12/21/20 2:46 AM, Laszlo Ersek wrote:
> On 12/20/20 11:15, Ard Biesheuvel wrote:
>> On 12/20/20 1:09 AM, Laszlo Ersek wrote:
> 
>>> The only thing I couldn't test that way was (obviously) building on
>>> Windows. So clearly now that I've tried merging the series at
>>> <https://github.com/tianocore/edk2/pull/1248>, that's what fails.
>>> Because, this is the first time that EmbeddedPkg/TimeBaseLib is seen
>>> by the Visual Studio compiler, and Visual Studio complains:
>>>
>>>> ERROR - Compiler #2220 from
>>>> d:\a\1\s\EmbeddedPkg\Library\TimeBaseLib\TimeBaseLib.c(111):   the
>>>> following warning is treated as an error
>>>> WARNING - Compiler #4244 from
>>>> d:\a\1\s\EmbeddedPkg\Library\TimeBaseLib\TimeBaseLib.c(111):   '=':
>>>> conversion from 'UINTN' to 'UINT32', possible loss of data
>>>
>>> It happens to be correct:
>>>
>>>     99  /**
>>>    100    Converts EFI_TIME to Epoch seconds (elapsed since 1970 JANUARY 01, 00:00:00 UTC)
>>>    101   **/
>>>    102  UINT32
>>>    103  EFIAPI
>>>    104  EfiTimeToEpoch (
>>>    105    IN  EFI_TIME  *Time
>>>    106    )
>>>    107  {
>>>    108    UINT32 EpochDays;   // Number of days elapsed since EPOCH_JULIAN_DAY
>>>    109    UINT32 EpochSeconds;
>>>    110
>>>    111    EpochDays = EfiGetEpochDays (Time);
>>>    112
>>>    113    EpochSeconds = (EpochDays * SEC_PER_DAY) + ((UINTN)Time->Hour * SEC_PER_HOUR) + (Time->Minute * SEC_PER_MIN) + Time->Second;
>>>    114
>>>    115    return EpochSeconds;
>>>    116  }
>>>
>>> This was discussed on the list earlier, when the function was duplicated
>>> for the HTTP dynamic command:
>>>
>>> - https://edk2.groups.io/g/devel/message/64933
>>> - https://edk2.groups.io/g/devel/message/65186
>>>
>>> Compare EfiTimeToEpoch() in
>>> "ShellPkg/DynamicCommand/HttpDynamicCommand/Http.c".
>>>
>>>    430  STATIC
>>>    431  UINTN
>>>    432  EFIAPI
>>>    433  EfiTimeToEpoch (
>>>    434    IN  EFI_TIME  *Time
>>>    435    )
>>>    436  {
>>>    437    //
>>>    438    // Number of days elapsed since EPOCH_JULIAN_DAY.
>>>    439    //
>>>    440    UINTN EpochDays;
>>>    441    UINTN EpochSeconds;
>>>    442
>>>    443    EpochDays = EfiGetEpochDays (Time);
>>>    444
>>>    445    EpochSeconds = (EpochDays * SEC_PER_DAY) +
>>>    446                   ((UINTN)Time->Hour * SEC_PER_HOUR) +
>>>    447                   (Time->Minute * SEC_PER_MIN) + Time->Second;
>>>    448
>>>    449    return EpochSeconds;
>>>    450  }
>>>
>>> So, in order to merge this series, I'll first have to fix
>>> EfiTimeToEpoch() in EmbeddedPkg :(
>>>
>>> I wish the fixed version in
>>> "ShellPkg/DynamicCommand/HttpDynamicCommand/Http.c" had been contributed
>>> back to EmbeddedPkg.
>>>
>>> Anyway, that is for 2021.
> 
> (given the fantastic opportunities provided by the COVID-19 pandemic, to
> catch up with family and friends around Christmas /s, I might as well
> treat the discussion of this patch series during my PTO as something
> welcome that takes my mind off of how much I miss the people I can't
> meet this year)
> 
>> Thanks for clearing up the outstanding questions.
>>
>> For the EmbeddedPkg change from UINT32 to UINTN
>>
>> Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
>>
>> although I suppose you will have to preserve the prototype, so adding
>> a (UINT32) cast to line 111 is probably the best approach here.
> 
> I ended up installing a brand new Windows 10 + VS2019 virtual machine,
> as I got fed up with the nasty surprises in the PRs (and I refuse to
> publish my work-in-progress branch just for the sake of setting off CI
> on it).
> 
> Two consequences:
> 
> (1) I'll squash the following trivial updates into two of the patches,
> for my next merge request attempt:
> 
>  5:  bb254f89067a !  7:  17a76bbec317 OvmfPkg/VirtioFsDxe: add a scatter-gather list data type
>     @@ -20,6 +20,8 @@
>          Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>          Message-Id: <20201216211125.19496-6-lersek@redhat.com>
>          Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
>     +    [lersek@redhat.com: suppress useless VS2019 warning about signed/unsigned
>     +     comparison in VirtioFsSgListsValidate()]
> 
>      diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
>      --- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
>     @@ -213,7 +215,7 @@
>      +    // can be added to the virtio queue, after the other descriptors added
>      +    // previously.
>      +    //
>     -+    if (SgList->NumVec > MAX_UINT16 - DescriptorsNeeded ||
>     ++    if (SgList->NumVec > (UINTN)(MAX_UINT16 - DescriptorsNeeded) ||
>      +        DescriptorsNeeded + SgList->NumVec > VirtioFs->QueueSize) {
>      +      return EFI_UNSUPPORTED;
>      +    }
> 
> and
> 
> 46:  4f42ecc2d9bb ! 48:  b807d3c0b54b OvmfPkg/VirtioFsDxe: add helper for determining access time updates
>     @@ -12,6 +12,8 @@
>          Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>          Message-Id: <20201216211125.19496-47-lersek@redhat.com>
>          Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
>     +    [lersek@redhat.com: suppress bogus VS2019 warning about lack of
>     +     initialization for ZeroTime]
> 
>      diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
>      --- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
>     @@ -92,7 +94,7 @@
>      +  EFI_TIME              *Time[3];
>      +  EFI_TIME              *NewTime[ARRAY_SIZE (Time)];
>      +  UINTN                 Idx;
>     -+  STATIC CONST EFI_TIME ZeroTime;
>     ++  STATIC CONST EFI_TIME ZeroTime = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
>      +  BOOLEAN               Change[ARRAY_SIZE (Time)];
>      +  UINT64                Seconds[ARRAY_SIZE (Time)];
>      +
> 
> (2) I've written three patches in total for fixing EfiTimeToEpoch(),
> including the prototype:
> 
> (2a) edk2-platforms:
> 
>   1  Silicon/Marvell/RealTimeClockLib: make EpochSeconds, WakeupSeconds UINTN
> 
>      Silicon/Marvell/Armada7k8k/Library/RealTimeClockLib/RealTimeClockLib.c | 14 ++++++++++----
>      1 file changed, 10 insertions(+), 4 deletions(-)
> 
> (other RealTimeClockLib instances / EfiTimeToEpoch() callers in
> edk2-platforms need no updates; furthermore, edk2-non-osi contains no
> EfiTimeToEpoch() calls at all)
> 
> (2b) edk2:
> 
>   1  ArmPlatformPkg/PL031RealTimeClockLib: cast EfiTimeToEpoch() val. to UINT32
> 
>      ArmPlatformPkg/Library/PL031RealTimeClockLib/PL031RealTimeClockLib.c | 2 +-
>      1 file changed, 1 insertion(+), 1 deletion(-)
> 
>   2  EmbeddedPkg/TimeBaseLib: remove useless truncation to 32-bit
> 
>      EmbeddedPkg/Include/Library/TimeBaseLib.h     | 2 +-
>      EmbeddedPkg/Library/TimeBaseLib/TimeBaseLib.c | 6 +++---
>      2 files changed, 4 insertions(+), 4 deletions(-)
> 
> If you're reading this before 2021, please let me know if you'd tolerate
> receiving these patches for approval still in 2020. (The edk2-platforms
> patch theoretically belongs to Leif and Marcin, but if Leif has stopped
> consuming work email (which we all should have by now, I guess...), then
> I believe you could ACK that patch in Leif's stead.)
> 

No problem.

-- 
Ard.

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [edk2-devel] [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver
  2020-12-21 10:10         ` Ard Biesheuvel
@ 2020-12-21 18:02           ` Laszlo Ersek
  0 siblings, 0 replies; 65+ messages in thread
From: Laszlo Ersek @ 2020-12-21 18:02 UTC (permalink / raw)
  To: devel, ard.biesheuvel, virtio-fs
  Cc: Jordan Justen, Leif Lindholm, Philippe Mathieu-Daudé,
	Liming Gao (Byosoft address)

On 12/21/20 11:10, Ard Biesheuvel wrote:
> On 12/21/20 2:46 AM, Laszlo Ersek wrote:
>> On 12/20/20 11:15, Ard Biesheuvel wrote:
>>> On 12/20/20 1:09 AM, Laszlo Ersek wrote:
>>
>>>> The only thing I couldn't test that way was (obviously) building on
>>>> Windows. So clearly now that I've tried merging the series at
>>>> <https://github.com/tianocore/edk2/pull/1248>, that's what fails.
>>>> Because, this is the first time that EmbeddedPkg/TimeBaseLib is seen
>>>> by the Visual Studio compiler, and Visual Studio complains:
>>>>
>>>>> ERROR - Compiler #2220 from
>>>>> d:\a\1\s\EmbeddedPkg\Library\TimeBaseLib\TimeBaseLib.c(111):   the
>>>>> following warning is treated as an error
>>>>> WARNING - Compiler #4244 from
>>>>> d:\a\1\s\EmbeddedPkg\Library\TimeBaseLib\TimeBaseLib.c(111):   '=':
>>>>> conversion from 'UINTN' to 'UINT32', possible loss of data
>>>>
>>>> It happens to be correct:
>>>>
>>>>     99  /**
>>>>    100    Converts EFI_TIME to Epoch seconds (elapsed since 1970 JANUARY 01, 00:00:00 UTC)
>>>>    101   **/
>>>>    102  UINT32
>>>>    103  EFIAPI
>>>>    104  EfiTimeToEpoch (
>>>>    105    IN  EFI_TIME  *Time
>>>>    106    )
>>>>    107  {
>>>>    108    UINT32 EpochDays;   // Number of days elapsed since EPOCH_JULIAN_DAY
>>>>    109    UINT32 EpochSeconds;
>>>>    110
>>>>    111    EpochDays = EfiGetEpochDays (Time);
>>>>    112
>>>>    113    EpochSeconds = (EpochDays * SEC_PER_DAY) + ((UINTN)Time->Hour * SEC_PER_HOUR) + (Time->Minute * SEC_PER_MIN) + Time->Second;
>>>>    114
>>>>    115    return EpochSeconds;
>>>>    116  }
>>>>
>>>> This was discussed on the list earlier, when the function was duplicated
>>>> for the HTTP dynamic command:
>>>>
>>>> - https://edk2.groups.io/g/devel/message/64933
>>>> - https://edk2.groups.io/g/devel/message/65186
>>>>
>>>> Compare EfiTimeToEpoch() in
>>>> "ShellPkg/DynamicCommand/HttpDynamicCommand/Http.c".
>>>>
>>>>    430  STATIC
>>>>    431  UINTN
>>>>    432  EFIAPI
>>>>    433  EfiTimeToEpoch (
>>>>    434    IN  EFI_TIME  *Time
>>>>    435    )
>>>>    436  {
>>>>    437    //
>>>>    438    // Number of days elapsed since EPOCH_JULIAN_DAY.
>>>>    439    //
>>>>    440    UINTN EpochDays;
>>>>    441    UINTN EpochSeconds;
>>>>    442
>>>>    443    EpochDays = EfiGetEpochDays (Time);
>>>>    444
>>>>    445    EpochSeconds = (EpochDays * SEC_PER_DAY) +
>>>>    446                   ((UINTN)Time->Hour * SEC_PER_HOUR) +
>>>>    447                   (Time->Minute * SEC_PER_MIN) + Time->Second;
>>>>    448
>>>>    449    return EpochSeconds;
>>>>    450  }
>>>>
>>>> So, in order to merge this series, I'll first have to fix
>>>> EfiTimeToEpoch() in EmbeddedPkg :(
>>>>
>>>> I wish the fixed version in
>>>> "ShellPkg/DynamicCommand/HttpDynamicCommand/Http.c" had been contributed
>>>> back to EmbeddedPkg.
>>>>
>>>> Anyway, that is for 2021.
>>
>> (given the fantastic opportunities provided by the COVID-19 pandemic, to
>> catch up with family and friends around Christmas /s, I might as well
>> treat the discussion of this patch series during my PTO as something
>> welcome that takes my mind off of how much I miss the people I can't
>> meet this year)
>>
>>> Thanks for clearing up the outstanding questions.
>>>
>>> For the EmbeddedPkg change from UINT32 to UINTN
>>>
>>> Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
>>>
>>> although I suppose you will have to preserve the prototype, so adding
>>> a (UINT32) cast to line 111 is probably the best approach here.
>>
>> I ended up installing a brand new Windows 10 + VS2019 virtual machine,
>> as I got fed up with the nasty surprises in the PRs (and I refuse to
>> publish my work-in-progress branch just for the sake of setting off CI
>> on it).
>>
>> Two consequences:
>>
>> (1) I'll squash the following trivial updates into two of the patches,
>> for my next merge request attempt:
>>
>>  5:  bb254f89067a !  7:  17a76bbec317 OvmfPkg/VirtioFsDxe: add a scatter-gather list data type
>>     @@ -20,6 +20,8 @@
>>          Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>          Message-Id: <20201216211125.19496-6-lersek@redhat.com>
>>          Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
>>     +    [lersek@redhat.com: suppress useless VS2019 warning about signed/unsigned
>>     +     comparison in VirtioFsSgListsValidate()]
>>
>>      diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
>>      --- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
>>     @@ -213,7 +215,7 @@
>>      +    // can be added to the virtio queue, after the other descriptors added
>>      +    // previously.
>>      +    //
>>     -+    if (SgList->NumVec > MAX_UINT16 - DescriptorsNeeded ||
>>     ++    if (SgList->NumVec > (UINTN)(MAX_UINT16 - DescriptorsNeeded) ||
>>      +        DescriptorsNeeded + SgList->NumVec > VirtioFs->QueueSize) {
>>      +      return EFI_UNSUPPORTED;
>>      +    }
>>
>> and
>>
>> 46:  4f42ecc2d9bb ! 48:  b807d3c0b54b OvmfPkg/VirtioFsDxe: add helper for determining access time updates
>>     @@ -12,6 +12,8 @@
>>          Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>          Message-Id: <20201216211125.19496-47-lersek@redhat.com>
>>          Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
>>     +    [lersek@redhat.com: suppress bogus VS2019 warning about lack of
>>     +     initialization for ZeroTime]
>>
>>      diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
>>      --- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h
>>     @@ -92,7 +94,7 @@
>>      +  EFI_TIME              *Time[3];
>>      +  EFI_TIME              *NewTime[ARRAY_SIZE (Time)];
>>      +  UINTN                 Idx;
>>     -+  STATIC CONST EFI_TIME ZeroTime;
>>     ++  STATIC CONST EFI_TIME ZeroTime = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
>>      +  BOOLEAN               Change[ARRAY_SIZE (Time)];
>>      +  UINT64                Seconds[ARRAY_SIZE (Time)];
>>      +
>>
>> (2) I've written three patches in total for fixing EfiTimeToEpoch(),
>> including the prototype:
>>
>> (2a) edk2-platforms:
>>
>>   1  Silicon/Marvell/RealTimeClockLib: make EpochSeconds, WakeupSeconds UINTN
>>
>>      Silicon/Marvell/Armada7k8k/Library/RealTimeClockLib/RealTimeClockLib.c | 14 ++++++++++----
>>      1 file changed, 10 insertions(+), 4 deletions(-)
>>
>> (other RealTimeClockLib instances / EfiTimeToEpoch() callers in
>> edk2-platforms need no updates; furthermore, edk2-non-osi contains no
>> EfiTimeToEpoch() calls at all)
>>
>> (2b) edk2:
>>
>>   1  ArmPlatformPkg/PL031RealTimeClockLib: cast EfiTimeToEpoch() val. to UINT32
>>
>>      ArmPlatformPkg/Library/PL031RealTimeClockLib/PL031RealTimeClockLib.c | 2 +-
>>      1 file changed, 1 insertion(+), 1 deletion(-)
>>
>>   2  EmbeddedPkg/TimeBaseLib: remove useless truncation to 32-bit
>>
>>      EmbeddedPkg/Include/Library/TimeBaseLib.h     | 2 +-
>>      EmbeddedPkg/Library/TimeBaseLib/TimeBaseLib.c | 6 +++---
>>      2 files changed, 4 insertions(+), 4 deletions(-)
>>
>> If you're reading this before 2021, please let me know if you'd tolerate
>> receiving these patches for approval still in 2020. (The edk2-platforms
>> patch theoretically belongs to Leif and Marcin, but if Leif has stopped
>> consuming work email (which we all should have by now, I guess...), then
>> I believe you could ACK that patch in Leif's stead.)
>>
> 
> No problem.
> 

Merged as commit range c06635ea3f4b..35ed29f207fd, via
<https://github.com/tianocore/edk2/pull/1256>.

I've also added <https://bugzilla.tianocore.org/show_bug.cgi?id=3097> to
<https://github.com/tianocore/tianocore.github.io/wiki/EDK-II-Release-Planning#proposed-features>.

Thank you, Ard!
Laszlo


^ permalink raw reply	[flat|nested] 65+ messages in thread

end of thread, other threads:[~2020-12-21 18:02 UTC | newest]

Thread overview: 65+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-12-16 21:10 [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 01/48] OvmfPkg: introduce VirtioFsDxe Laszlo Ersek
2020-12-18 17:42   ` Ard Biesheuvel
2020-12-18 18:13     ` [Virtio-fs] " Dr. David Alan Gilbert
2020-12-19 21:16       ` Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 02/48] ArmVirtPkg: include VirtioFsDxe in the ArmVirtQemu* platforms Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 03/48] OvmfPkg/VirtioFsDxe: DriverBinding: open VirtioDevice, install SimpleFs Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 04/48] OvmfPkg/VirtioFsDxe: implement virtio device (un)initialization Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 05/48] OvmfPkg/VirtioFsDxe: add a scatter-gather list data type Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 06/48] OvmfPkg/VirtioFsDxe: introduce the basic FUSE request/response headers Laszlo Ersek
2020-12-17 11:49   ` [Virtio-fs] " Dr. David Alan Gilbert
2020-12-17 13:57     ` Laszlo Ersek
2020-12-17 14:06       ` Dr. David Alan Gilbert
2020-12-17 14:32       ` Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 07/48] OvmfPkg/VirtioFsDxe: map "errno" values to EFI_STATUS Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 08/48] OvmfPkg/VirtioFsDxe: submit the FUSE_INIT request to the device Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 09/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_OPENDIR Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 10/48] OvmfPkg/VirtioFsDxe: add shared wrapper for FUSE_RELEASE / FUSE_RELEASEDIR Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 11/48] OvmfPkg/VirtioFsDxe: implement EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume() Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 12/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_FORGET Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 13/48] OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_FSYNC / FUSE_FSYNCDIR Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 14/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_FLUSH Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 15/48] OvmfPkg/VirtioFsDxe: flush, sync, release and forget in Close() / Delete() Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 16/48] OvmfPkg/VirtioFsDxe: add helper for appending and sanitizing paths Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 17/48] OvmfPkg/VirtioFsDxe: manage path lifecycle in OpenVolume, Close, Delete Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 18/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_OPEN Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 19/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_MKDIR Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 20/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_CREATE Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 21/48] OvmfPkg/VirtioFsDxe: convert FUSE inode attributes to EFI_FILE_INFO Laszlo Ersek
2020-12-16 21:10 ` [edk2 PATCH 22/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_LOOKUP Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 23/48] OvmfPkg/VirtioFsDxe: split canon. path into last parent + last component Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 24/48] OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_UNLINK / FUSE_RMDIR Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 25/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_GETATTR Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 26/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Open() Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 27/48] OvmfPkg/VirtioFsDxe: erase the dir. entry in EFI_FILE_PROTOCOL.Delete() Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 28/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_STATFS Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 29/48] OvmfPkg/VirtioFsDxe: add helper for formatting UEFI basenames Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 30/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.GetInfo() Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 31/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.GetPosition, .SetPosition Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 32/48] OvmfPkg/VirtioFsDxe: add a shared wrapper for FUSE_READ / FUSE_READDIRPLUS Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 33/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Read() for regular files Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 34/48] OvmfPkg/VirtioFsDxe: convert FUSE dirent filename to EFI_FILE_INFO Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 35/48] OvmfPkg/VirtioFsDxe: add EFI_FILE_INFO cache fields to VIRTIO_FS_FILE Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 36/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Read() for directories Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 37/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Flush() Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 38/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_WRITE Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 39/48] OvmfPkg/VirtioFsDxe: implement EFI_FILE_PROTOCOL.Write() Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 40/48] OvmfPkg/VirtioFsDxe: handle the volume label in EFI_FILE_PROTOCOL.SetInfo Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 41/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_RENAME2 Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 42/48] OvmfPkg/VirtioFsDxe: add helper for composing rename/move destination path Laszlo Ersek
2020-12-18 17:39   ` Ard Biesheuvel
2020-12-19 22:40     ` Laszlo Ersek
2020-12-19 22:54       ` Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 43/48] OvmfPkg/VirtioFsDxe: handle file rename/move in EFI_FILE_PROTOCOL.SetInfo Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 44/48] OvmfPkg/VirtioFsDxe: implement the wrapper function for FUSE_SETATTR Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 45/48] OvmfPkg/VirtioFsDxe: add helper for determining file size update Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 46/48] OvmfPkg/VirtioFsDxe: add helper for determining access time updates Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 47/48] OvmfPkg/VirtioFsDxe: add helper for determining file mode bits update Laszlo Ersek
2020-12-16 21:11 ` [edk2 PATCH 48/48] OvmfPkg/VirtioFsDxe: handle attribute updates in EFI_FILE_PROTOCOL.SetInfo Laszlo Ersek
2020-12-18 17:44 ` [edk2 PATCH 00/48] ArmVirtPkg, OvmfPkg: virtio filesystem driver Ard Biesheuvel
2020-12-20  0:09   ` Laszlo Ersek
2020-12-20 10:15     ` Ard Biesheuvel
2020-12-21  1:46       ` Laszlo Ersek
2020-12-21 10:10         ` Ard Biesheuvel
2020-12-21 18:02           ` [edk2-devel] " Laszlo Ersek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox