]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg/VirtioFsDxe: add helper for composing rename/move destination path
authorLaszlo Ersek <lersek@redhat.com>
Wed, 16 Dec 2020 21:11:19 +0000 (22:11 +0100)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Mon, 21 Dec 2020 17:16:23 +0000 (17:16 +0000)
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>
Message-Id: <20201216211125.19496-43-lersek@redhat.com>
Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com>
OvmfPkg/VirtioFsDxe/Helpers.c
OvmfPkg/VirtioFsDxe/VirtioFsDxe.h

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