]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg/VirtioFsDxe: handle file rename/move in EFI_FILE_PROTOCOL.SetInfo
authorLaszlo Ersek <lersek@redhat.com>
Wed, 16 Dec 2020 21:11:20 +0000 (22:11 +0100)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Mon, 21 Dec 2020 17:16:23 +0000 (17:16 +0000)
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>
Message-Id: <20201216211125.19496-44-lersek@redhat.com>
Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c

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