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>
Message-Id: <
20201216211125.19496-12-lersek@redhat.com>
Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
#define VIRTIO_FS_FUSE_MAJOR 7\r
#define VIRTIO_FS_FUSE_MINOR 31\r
\r
+//\r
+// The inode number of the root directory.\r
+//\r
+#define VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID 1\r
+\r
//\r
// FUSE operation codes.\r
//\r
goto UninitVirtioFs;\r
}\r
\r
+ InitializeListHead (&VirtioFs->OpenFiles);\r
VirtioFs->SimpleFs.Revision = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;\r
VirtioFs->SimpleFs.OpenVolume = VirtioFsOpenVolume;\r
\r
\r
VirtioFs = VIRTIO_FS_FROM_SIMPLE_FS (SimpleFs);\r
\r
+ if (!IsListEmpty (&VirtioFs->OpenFiles)) {\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
Status = gBS->UninstallProtocolInterface (ControllerHandle,\r
&gEfiSimpleFileSystemProtocolGuid, SimpleFs);\r
if (EFI_ERROR (Status)) {\r
--- /dev/null
+/** @file\r
+ EFI_FILE_PROTOCOL.Close() member function for the Virtio Filesystem driver.\r
+\r
+ Copyright (C) 2020, Red Hat, Inc.\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include <Library/BaseLib.h> // RemoveEntryList()\r
+#include <Library/MemoryAllocationLib.h> // FreePool()\r
+\r
+#include "VirtioFsDxe.h"\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileClose (\r
+ IN EFI_FILE_PROTOCOL *This\r
+ )\r
+{\r
+ VIRTIO_FS_FILE *VirtioFsFile;\r
+ VIRTIO_FS *VirtioFs;\r
+\r
+ VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);\r
+ VirtioFs = VirtioFsFile->OwnerFs;\r
+\r
+ //\r
+ // At this point, the implementation is only suitable for closing the\r
+ // VIRTIO_FS_FILE that was created by VirtioFsOpenVolume().\r
+ //\r
+ ASSERT (VirtioFsFile->IsDirectory);\r
+ ASSERT (VirtioFsFile->NodeId == VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID);\r
+ //\r
+ // Close the root directory.\r
+ //\r
+ // Ignore any errors, because EFI_FILE_PROTOCOL.Close() is required to\r
+ // release the EFI_FILE_PROTOCOL object unconditionally.\r
+ //\r
+ VirtioFsFuseReleaseFileOrDir (VirtioFs, VirtioFsFile->NodeId,\r
+ VirtioFsFile->FuseHandle, VirtioFsFile->IsDirectory);\r
+\r
+ //\r
+ // One fewer file left open for the owner filesystem.\r
+ //\r
+ RemoveEntryList (&VirtioFsFile->OpenFilesEntry);\r
+\r
+ FreePool (VirtioFsFile);\r
+ return EFI_SUCCESS;\r
+}\r
--- /dev/null
+/** @file\r
+ EFI_FILE_PROTOCOL.Delete() member function for the Virtio Filesystem driver.\r
+\r
+ Copyright (C) 2020, Red Hat, Inc.\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include "VirtioFsDxe.h"\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileDelete (\r
+ IN EFI_FILE_PROTOCOL *This\r
+ )\r
+{\r
+ //\r
+ // At this point, the implementation is only suitable for closing the\r
+ // VIRTIO_FS_FILE that was created by VirtioFsOpenVolume().\r
+ //\r
+ // Actually deleting the root directory is not possible, so we're only going\r
+ // to release resources, and return EFI_WARN_DELETE_FAILURE.\r
+ //\r
+ // In order to release resources, VirtioFsSimpleFileClose() is just right\r
+ // here.\r
+ //\r
+ VirtioFsSimpleFileClose (This);\r
+ return EFI_WARN_DELETE_FAILURE;\r
+}\r
--- /dev/null
+/** @file\r
+ EFI_FILE_PROTOCOL.Flush() member function for the Virtio Filesystem driver.\r
+\r
+ Copyright (C) 2020, Red Hat, Inc.\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include "VirtioFsDxe.h"\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileFlush (\r
+ IN EFI_FILE_PROTOCOL *This\r
+ )\r
+{\r
+ return EFI_NO_MEDIA;\r
+}\r
--- /dev/null
+/** @file\r
+ EFI_FILE_PROTOCOL.GetInfo() member function for the Virtio Filesystem driver.\r
+\r
+ Copyright (C) 2020, Red Hat, Inc.\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include "VirtioFsDxe.h"\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileGetInfo (\r
+ IN EFI_FILE_PROTOCOL *This,\r
+ IN EFI_GUID *InformationType,\r
+ IN OUT UINTN *BufferSize,\r
+ OUT VOID *Buffer\r
+ )\r
+{\r
+ return EFI_NO_MEDIA;\r
+}\r
--- /dev/null
+/** @file\r
+ EFI_FILE_PROTOCOL.GetPosition() member function for the Virtio Filesystem\r
+ driver.\r
+\r
+ Copyright (C) 2020, Red Hat, Inc.\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include "VirtioFsDxe.h"\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileGetPosition (\r
+ IN EFI_FILE_PROTOCOL *This,\r
+ OUT UINT64 *Position\r
+ )\r
+{\r
+ return EFI_DEVICE_ERROR;\r
+}\r
--- /dev/null
+/** @file\r
+ EFI_FILE_PROTOCOL.Open() member function for the Virtio Filesystem driver.\r
+\r
+ Copyright (C) 2020, Red Hat, Inc.\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include "VirtioFsDxe.h"\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileOpen (\r
+ IN EFI_FILE_PROTOCOL *This,\r
+ OUT EFI_FILE_PROTOCOL **NewHandle,\r
+ IN CHAR16 *FileName,\r
+ IN UINT64 OpenMode,\r
+ IN UINT64 Attributes\r
+ )\r
+{\r
+ return EFI_NO_MEDIA;\r
+}\r
SPDX-License-Identifier: BSD-2-Clause-Patent\r
**/\r
\r
+#include <Library/BaseLib.h> // InsertTailList()\r
+#include <Library/MemoryAllocationLib.h> // AllocatePool()\r
+\r
#include "VirtioFsDxe.h"\r
\r
/**\r
OUT EFI_FILE_PROTOCOL **Root\r
)\r
{\r
- return EFI_NO_MEDIA;\r
+ VIRTIO_FS *VirtioFs;\r
+ VIRTIO_FS_FILE *VirtioFsFile;\r
+ EFI_STATUS Status;\r
+ UINT64 RootDirHandle;\r
+\r
+ VirtioFs = VIRTIO_FS_FROM_SIMPLE_FS (This);\r
+\r
+ VirtioFsFile = AllocatePool (sizeof *VirtioFsFile);\r
+ if (VirtioFsFile == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Open the root directory.\r
+ //\r
+ Status = VirtioFsFuseOpenDir (VirtioFs, VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID,\r
+ &RootDirHandle);\r
+ if (EFI_ERROR (Status)) {\r
+ goto FreeVirtioFsFile;\r
+ }\r
+\r
+ //\r
+ // Populate the new VIRTIO_FS_FILE object.\r
+ //\r
+ VirtioFsFile->Signature = VIRTIO_FS_FILE_SIG;\r
+ VirtioFsFile->SimpleFile.Revision = EFI_FILE_PROTOCOL_REVISION;\r
+ VirtioFsFile->SimpleFile.Open = VirtioFsSimpleFileOpen;\r
+ VirtioFsFile->SimpleFile.Close = VirtioFsSimpleFileClose;\r
+ VirtioFsFile->SimpleFile.Delete = VirtioFsSimpleFileDelete;\r
+ VirtioFsFile->SimpleFile.Read = VirtioFsSimpleFileRead;\r
+ VirtioFsFile->SimpleFile.Write = VirtioFsSimpleFileWrite;\r
+ VirtioFsFile->SimpleFile.GetPosition = VirtioFsSimpleFileGetPosition;\r
+ VirtioFsFile->SimpleFile.SetPosition = VirtioFsSimpleFileSetPosition;\r
+ VirtioFsFile->SimpleFile.GetInfo = VirtioFsSimpleFileGetInfo;\r
+ VirtioFsFile->SimpleFile.SetInfo = VirtioFsSimpleFileSetInfo;\r
+ VirtioFsFile->SimpleFile.Flush = VirtioFsSimpleFileFlush;\r
+ VirtioFsFile->IsDirectory = TRUE;\r
+ VirtioFsFile->OwnerFs = VirtioFs;\r
+ VirtioFsFile->NodeId = VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID;\r
+ VirtioFsFile->FuseHandle = RootDirHandle;\r
+\r
+ //\r
+ // One more file open for the filesystem.\r
+ //\r
+ InsertTailList (&VirtioFs->OpenFiles, &VirtioFsFile->OpenFilesEntry);\r
+\r
+ *Root = &VirtioFsFile->SimpleFile;\r
+ return EFI_SUCCESS;\r
+\r
+FreeVirtioFsFile:\r
+ FreePool (VirtioFsFile);\r
+\r
+ return Status;\r
}\r
--- /dev/null
+/** @file\r
+ EFI_FILE_PROTOCOL.Read() member function for the Virtio Filesystem driver.\r
+\r
+ Copyright (C) 2020, Red Hat, Inc.\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include "VirtioFsDxe.h"\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileRead (\r
+ IN EFI_FILE_PROTOCOL *This,\r
+ IN OUT UINTN *BufferSize,\r
+ OUT VOID *Buffer\r
+ )\r
+{\r
+ return EFI_NO_MEDIA;\r
+}\r
--- /dev/null
+/** @file\r
+ EFI_FILE_PROTOCOL.SetInfo() member function for the Virtio Filesystem driver.\r
+\r
+ Copyright (C) 2020, Red Hat, Inc.\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include "VirtioFsDxe.h"\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileSetInfo (\r
+ IN EFI_FILE_PROTOCOL *This,\r
+ IN EFI_GUID *InformationType,\r
+ IN UINTN BufferSize,\r
+ IN VOID *Buffer\r
+ )\r
+{\r
+ return EFI_NO_MEDIA;\r
+}\r
--- /dev/null
+/** @file\r
+ EFI_FILE_PROTOCOL.SetPosition() member function for the Virtio Filesystem\r
+ driver.\r
+\r
+ Copyright (C) 2020, Red Hat, Inc.\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include "VirtioFsDxe.h"\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileSetPosition (\r
+ IN EFI_FILE_PROTOCOL *This,\r
+ IN UINT64 Position\r
+ )\r
+{\r
+ return EFI_DEVICE_ERROR;\r
+}\r
--- /dev/null
+/** @file\r
+ EFI_FILE_PROTOCOL.Write() member function for the Virtio Filesystem driver.\r
+\r
+ Copyright (C) 2020, Red Hat, Inc.\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include "VirtioFsDxe.h"\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileWrite (\r
+ IN EFI_FILE_PROTOCOL *This,\r
+ IN OUT UINTN *BufferSize,\r
+ IN VOID *Buffer\r
+ )\r
+{\r
+ return EFI_NO_MEDIA;\r
+}\r
\r
#define VIRTIO_FS_SIG SIGNATURE_64 ('V', 'I', 'R', 'T', 'I', 'O', 'F', 'S')\r
\r
+#define VIRTIO_FS_FILE_SIG \\r
+ SIGNATURE_64 ('V', 'I', 'O', 'F', 'S', 'F', 'I', 'L')\r
+\r
//\r
// Filesystem label encoded in UCS-2, transformed from the UTF-8 representation\r
// in "VIRTIO_FS_CONFIG.Tag", and NUL-terminated. Only the printable ASCII code\r
VOID *RingMap; // VirtioRingMap 2\r
UINT64 RequestId; // FuseInitSession 1\r
EFI_EVENT ExitBoot; // DriverBindingStart 0\r
+ LIST_ENTRY OpenFiles; // DriverBindingStart 0\r
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL SimpleFs; // DriverBindingStart 0\r
} VIRTIO_FS;\r
\r
UINT32 TotalSize;\r
} VIRTIO_FS_SCATTER_GATHER_LIST;\r
\r
+//\r
+// Private context structure that exposes EFI_FILE_PROTOCOL on top of an open\r
+// FUSE file reference.\r
+//\r
+typedef struct {\r
+ UINT64 Signature;\r
+ EFI_FILE_PROTOCOL SimpleFile;\r
+ BOOLEAN IsDirectory;\r
+ VIRTIO_FS *OwnerFs;\r
+ LIST_ENTRY OpenFilesEntry;\r
+ //\r
+ // In the FUSE wire protocol, every request except FUSE_INIT refers to a\r
+ // file, namely by the "VIRTIO_FS_FUSE_REQUEST.NodeId" field; that is, by the\r
+ // inode number of the file. However, some of the FUSE requests that we need\r
+ // for some of the EFI_FILE_PROTOCOL member functions require an open file\r
+ // handle *in addition* to the inode number. For simplicity, whenever a\r
+ // VIRTIO_FS_FILE object is created, primarily defined by its NodeId field,\r
+ // we also *open* the referenced file at once, and save the returned file\r
+ // handle in the FuseHandle field. This way, when an EFI_FILE_PROTOCOL member\r
+ // function must send a FUSE request that needs the file handle *in addition*\r
+ // to the inode number, FuseHandle will be at our disposal at once.\r
+ //\r
+ UINT64 NodeId;\r
+ UINT64 FuseHandle;\r
+} VIRTIO_FS_FILE;\r
+\r
+#define VIRTIO_FS_FILE_FROM_SIMPLE_FILE(SimpleFileReference) \\r
+ CR (SimpleFileReference, VIRTIO_FS_FILE, SimpleFile, VIRTIO_FS_FILE_SIG);\r
+\r
+#define VIRTIO_FS_FILE_FROM_OPEN_FILES_ENTRY(OpenFilesEntryReference) \\r
+ CR (OpenFilesEntryReference, VIRTIO_FS_FILE, OpenFilesEntry, \\r
+ VIRTIO_FS_FILE_SIG);\r
+\r
//\r
// Initialization and helper routines for the Virtio Filesystem device.\r
//\r
OUT EFI_FILE_PROTOCOL **Root\r
);\r
\r
+//\r
+// EFI_FILE_PROTOCOL member functions for the Virtio Filesystem driver.\r
+//\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileClose (\r
+ IN EFI_FILE_PROTOCOL *This\r
+ );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileDelete (\r
+ IN EFI_FILE_PROTOCOL *This\r
+ );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileFlush (\r
+ IN EFI_FILE_PROTOCOL *This\r
+ );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileGetInfo (\r
+ IN EFI_FILE_PROTOCOL *This,\r
+ IN EFI_GUID *InformationType,\r
+ IN OUT UINTN *BufferSize,\r
+ OUT VOID *Buffer\r
+ );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileGetPosition (\r
+ IN EFI_FILE_PROTOCOL *This,\r
+ OUT UINT64 *Position\r
+ );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileOpen (\r
+ IN EFI_FILE_PROTOCOL *This,\r
+ OUT EFI_FILE_PROTOCOL **NewHandle,\r
+ IN CHAR16 *FileName,\r
+ IN UINT64 OpenMode,\r
+ IN UINT64 Attributes\r
+ );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileRead (\r
+ IN EFI_FILE_PROTOCOL *This,\r
+ IN OUT UINTN *BufferSize,\r
+ OUT VOID *Buffer\r
+ );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileSetInfo (\r
+ IN EFI_FILE_PROTOCOL *This,\r
+ IN EFI_GUID *InformationType,\r
+ IN UINTN BufferSize,\r
+ IN VOID *Buffer\r
+ );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileSetPosition (\r
+ IN EFI_FILE_PROTOCOL *This,\r
+ IN UINT64 Position\r
+ );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFsSimpleFileWrite (\r
+ IN EFI_FILE_PROTOCOL *This,\r
+ IN OUT UINTN *BufferSize,\r
+ IN VOID *Buffer\r
+ );\r
+\r
#endif // VIRTIO_FS_DXE_H_\r
FuseOpenDir.c\r
FuseRelease.c\r
Helpers.c\r
+ SimpleFsClose.c\r
+ SimpleFsDelete.c\r
+ SimpleFsFlush.c\r
+ SimpleFsGetInfo.c\r
+ SimpleFsGetPosition.c\r
+ SimpleFsOpen.c\r
SimpleFsOpenVolume.c\r
+ SimpleFsRead.c\r
+ SimpleFsSetInfo.c\r
+ SimpleFsSetPosition.c\r
+ SimpleFsWrite.c\r
VirtioFsDxe.h\r
\r
[LibraryClasses]\r