summaryrefslogtreecommitdiffstats
path: root/OvmfPkg
diff options
context:
space:
mode:
authorLaszlo Ersek <lersek@redhat.com>2020-12-16 22:11:20 +0100
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>2020-12-21 17:16:23 +0000
commitdd0911859f046f98528dc1875c86252a0565c14b (patch)
treeb61ec48626daabc69bd56330f24a6e04429b3bb9 /OvmfPkg
parentc3f76ef89d4934089c69e8407174a8176e4bf90c (diff)
downloadedk2-dd0911859f046f98528dc1875c86252a0565c14b.tar.gz
edk2-dd0911859f046f98528dc1875c86252a0565c14b.tar.bz2
edk2-dd0911859f046f98528dc1875c86252a0565c14b.zip
OvmfPkg/VirtioFsDxe: handle file rename/move in EFI_FILE_PROTOCOL.SetInfo
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>
Diffstat (limited to 'OvmfPkg')
-rw-r--r--OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c234
1 files changed, 233 insertions, 1 deletions
diff --git a/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c b/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c
index 895b5c029a..55169dde78 100644
--- a/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c
+++ b/OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c
@@ -10,6 +10,7 @@
#include <Guid/FileSystemVolumeLabelInfo.h> // gEfiFileSystemVolumeLabelInfo...
#include <Library/BaseLib.h> // StrCmp()
#include <Library/BaseMemoryLib.h> // CompareGuid()
+#include <Library/MemoryAllocationLib.h> // FreePool()
#include "VirtioFsDxe.h"
@@ -128,6 +129,237 @@ ValidateInfoStructure (
}
/**
+ 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
@@ -230,7 +462,7 @@ VirtioFsSimpleFileSetInfo (
)
{
if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
- return EFI_UNSUPPORTED;
+ return SetFileInfo (This, BufferSize, Buffer);
}
if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {