From ca61b84586d59bdda1837ba00eb6e24fa900dc56 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Wed, 16 Dec 2020 22:11:00 +0100 Subject: [PATCH] OvmfPkg/VirtioFsDxe: split canon. path into last parent + last component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Cc: Jordan Justen Cc: Philippe Mathieu-Daudé Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=3097 Signed-off-by: Laszlo Ersek Message-Id: <20201216211125.19496-24-lersek@redhat.com> Acked-by: Ard Biesheuvel --- OvmfPkg/VirtioFsDxe/Helpers.c | 131 ++++++++++++++++++++++++++++++ OvmfPkg/VirtioFsDxe/VirtioFsDxe.h | 8 ++ 2 files changed, 139 insertions(+) diff --git a/OvmfPkg/VirtioFsDxe/Helpers.c b/OvmfPkg/VirtioFsDxe/Helpers.c index e9582e46bd..0e4390b005 100644 --- a/OvmfPkg/VirtioFsDxe/Helpers.c +++ b/OvmfPkg/VirtioFsDxe/Helpers.c @@ -1591,6 +1591,137 @@ FreeRhsPath8: 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. diff --git a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h index b2e4adce09..6ae5c36f7f 100644 --- a/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h +++ b/OvmfPkg/VirtioFsDxe/VirtioFsDxe.h @@ -226,6 +226,14 @@ VirtioFsAppendPath ( 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,