123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565 |
- /** @file
- EFI_FILE_PROTOCOL.Open() member function for the Virtio Filesystem driver.
- Copyright (C) 2020, Red Hat, Inc.
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #include <Library/BaseLib.h> // AsciiStrCmp()
- #include <Library/MemoryAllocationLib.h> // AllocatePool()
- #include "VirtioFsDxe.h"
- /**
- Open the root directory, possibly for writing.
- @param[in,out] VirtioFs The Virtio Filesystem device whose root directory
- should be opened.
- @param[out] NewHandle The new EFI_FILE_PROTOCOL instance through which
- the root directory can be accessed.
- @param[in] OpenForWriting TRUE if the root directory should be opened for
- read-write access. FALSE if the root directory
- should be opened for read-only access. Opening the
- root directory for read-write access is useful for
- calling EFI_FILE_PROTOCOL.Flush() or
- EFI_FILE_PROTOCOL.SetInfo() later, for syncing or
- touching the root directory, respectively.
- @retval EFI_SUCCESS The root directory has been opened successfully.
- @retval EFI_ACCESS_DENIED OpenForWriting is TRUE, but the root directory is
- marked as read-only.
- @return Error codes propagated from underlying functions.
- **/
- STATIC
- EFI_STATUS
- OpenRootDirectory (
- IN OUT VIRTIO_FS *VirtioFs,
- OUT EFI_FILE_PROTOCOL **NewHandle,
- IN BOOLEAN OpenForWriting
- )
- {
- EFI_STATUS Status;
- VIRTIO_FS_FILE *NewVirtioFsFile;
- //
- // VirtioFsOpenVolume() opens the root directory for read-only access. If the
- // current request is to open the root directory for read-write access, so
- // that EFI_FILE_PROTOCOL.Flush() or EFI_FILE_PROTOCOL.SetInfo()+timestamps
- // can be used on the root directory later, then we have to check for write
- // permission first.
- //
- if (OpenForWriting) {
- VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
- EFI_FILE_INFO FileInfo;
- Status = VirtioFsFuseGetAttr (
- VirtioFs,
- VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID,
- &FuseAttr
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- Status = VirtioFsFuseAttrToEfiFileInfo (&FuseAttr, &FileInfo);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- if ((FileInfo.Attribute & EFI_FILE_READ_ONLY) != 0) {
- return EFI_ACCESS_DENIED;
- }
- }
- Status = VirtioFsOpenVolume (&VirtioFs->SimpleFs, NewHandle);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- NewVirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (*NewHandle);
- NewVirtioFsFile->IsOpenForWriting = OpenForWriting;
- return EFI_SUCCESS;
- }
- /**
- Open an existent regular file or non-root directory.
- @param[in,out] VirtioFs The Virtio Filesystem device on which the
- regular file or directory should be opened.
- @param[in] DirNodeId The inode number of the immediate parent
- directory of the regular file or directory to
- open.
- @param[in] Name The single-component filename of the regular
- file or directory to open, under the immediate
- parent directory identified by DirNodeId.
- @param[in] OpenForWriting TRUE if the regular file or directory should be
- opened for read-write access. FALSE if the
- regular file or directory should be opened for
- read-only access. Opening a directory for
- read-write access is useful for deleting,
- renaming, syncing or touching the directory
- later.
- @param[out] NodeId The inode number of the regular file or
- directory, returned by the Virtio Filesystem
- device.
- @param[out] FuseHandle The open handle to the regular file or
- directory, returned by the Virtio Filesystem
- device.
- @param[out] NodeIsDirectory Set to TRUE on output if Name was found to refer
- to a directory. Set to FALSE if Name was found
- to refer to a regular file.
- @retval EFI_SUCCESS The regular file or directory has been looked up
- and opened successfully.
- @retval EFI_ACCESS_DENIED OpenForWriting is TRUE, but the regular file or
- directory is marked read-only.
- @retval EFI_NOT_FOUND A directory entry called Name was not found in the
- directory identified by DirNodeId. (EFI_NOT_FOUND
- is not returned for any other condition.)
- @return Errors propagated from underlying functions. If
- the error code to propagate were EFI_NOT_FOUND, it
- is remapped to EFI_DEVICE_ERROR.
- **/
- STATIC
- EFI_STATUS
- OpenExistentFileOrDirectory (
- IN OUT VIRTIO_FS *VirtioFs,
- IN UINT64 DirNodeId,
- IN CHAR8 *Name,
- IN BOOLEAN OpenForWriting,
- OUT UINT64 *NodeId,
- OUT UINT64 *FuseHandle,
- OUT BOOLEAN *NodeIsDirectory
- )
- {
- EFI_STATUS Status;
- UINT64 ResolvedNodeId;
- VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
- EFI_FILE_INFO FileInfo;
- BOOLEAN IsDirectory;
- UINT64 NewFuseHandle;
- Status = VirtioFsFuseLookup (
- VirtioFs,
- DirNodeId,
- Name,
- &ResolvedNodeId,
- &FuseAttr
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- Status = VirtioFsFuseAttrToEfiFileInfo (&FuseAttr, &FileInfo);
- if (EFI_ERROR (Status)) {
- goto ForgetResolvedNodeId;
- }
- if (OpenForWriting && ((FileInfo.Attribute & EFI_FILE_READ_ONLY) != 0)) {
- Status = EFI_ACCESS_DENIED;
- goto ForgetResolvedNodeId;
- }
- IsDirectory = (BOOLEAN)((FileInfo.Attribute & EFI_FILE_DIRECTORY) != 0);
- if (IsDirectory) {
- //
- // If OpenForWriting is TRUE here, that's not passed to
- // VirtioFsFuseOpenDir(); it does not affect the FUSE_OPENDIR request we
- // send. OpenForWriting=TRUE will only permit attempts to delete, rename,
- // flush (sync), and touch the directory.
- //
- Status = VirtioFsFuseOpenDir (VirtioFs, ResolvedNodeId, &NewFuseHandle);
- } else {
- Status = VirtioFsFuseOpen (
- VirtioFs,
- ResolvedNodeId,
- OpenForWriting,
- &NewFuseHandle
- );
- }
- if (EFI_ERROR (Status)) {
- goto ForgetResolvedNodeId;
- }
- *NodeId = ResolvedNodeId;
- *FuseHandle = NewFuseHandle;
- *NodeIsDirectory = IsDirectory;
- return EFI_SUCCESS;
- ForgetResolvedNodeId:
- VirtioFsFuseForget (VirtioFs, ResolvedNodeId);
- return (Status == EFI_NOT_FOUND) ? EFI_DEVICE_ERROR : Status;
- }
- /**
- Create a directory.
- @param[in,out] VirtioFs The Virtio Filesystem device on which the directory
- should be created.
- @param[in] DirNodeId The inode number of the immediate parent directory
- of the directory to create.
- @param[in] Name The single-component filename of the directory to
- create, under the immediate parent directory
- identified by DirNodeId.
- @param[out] NodeId The inode number of the directory created, returned
- by the Virtio Filesystem device.
- @param[out] FuseHandle The open handle to the directory created, returned
- by the Virtio Filesystem device.
- @retval EFI_SUCCESS The directory has been created successfully.
- @return Errors propagated from underlying functions.
- **/
- STATIC
- EFI_STATUS
- CreateDirectory (
- IN OUT VIRTIO_FS *VirtioFs,
- IN UINT64 DirNodeId,
- IN CHAR8 *Name,
- OUT UINT64 *NodeId,
- OUT UINT64 *FuseHandle
- )
- {
- EFI_STATUS Status;
- UINT64 NewChildDirNodeId;
- UINT64 NewFuseHandle;
- Status = VirtioFsFuseMkDir (VirtioFs, DirNodeId, Name, &NewChildDirNodeId);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- Status = VirtioFsFuseOpenDir (VirtioFs, NewChildDirNodeId, &NewFuseHandle);
- if (EFI_ERROR (Status)) {
- goto RemoveNewChildDir;
- }
- *NodeId = NewChildDirNodeId;
- *FuseHandle = NewFuseHandle;
- return EFI_SUCCESS;
- RemoveNewChildDir:
- VirtioFsFuseRemoveFileOrDir (VirtioFs, DirNodeId, Name, TRUE /* IsDir */);
- VirtioFsFuseForget (VirtioFs, NewChildDirNodeId);
- return Status;
- }
- /**
- Create a regular file.
- @param[in,out] VirtioFs The Virtio Filesystem device on which the regular
- file should be created.
- @param[in] DirNodeId The inode number of the immediate parent directory
- of the regular file to create.
- @param[in] Name The single-component filename of the regular file to
- create, under the immediate parent directory
- identified by DirNodeId.
- @param[out] NodeId The inode number of the regular file created,
- returned by the Virtio Filesystem device.
- @param[out] FuseHandle The open handle to the regular file created,
- returned by the Virtio Filesystem device.
- @retval EFI_SUCCESS The regular file has been created successfully.
- @return Errors propagated from underlying functions.
- **/
- STATIC
- EFI_STATUS
- CreateRegularFile (
- IN OUT VIRTIO_FS *VirtioFs,
- IN UINT64 DirNodeId,
- IN CHAR8 *Name,
- OUT UINT64 *NodeId,
- OUT UINT64 *FuseHandle
- )
- {
- return VirtioFsFuseOpenOrCreate (
- VirtioFs,
- DirNodeId,
- Name,
- NodeId,
- FuseHandle
- );
- }
- EFI_STATUS
- EFIAPI
- VirtioFsSimpleFileOpen (
- IN EFI_FILE_PROTOCOL *This,
- OUT EFI_FILE_PROTOCOL **NewHandle,
- IN CHAR16 *FileName,
- IN UINT64 OpenMode,
- IN UINT64 Attributes
- )
- {
- VIRTIO_FS_FILE *VirtioFsFile;
- VIRTIO_FS *VirtioFs;
- BOOLEAN OpenForWriting;
- BOOLEAN PermitCreation;
- BOOLEAN CreateDirectoryIfCreating;
- VIRTIO_FS_FILE *NewVirtioFsFile;
- EFI_STATUS Status;
- CHAR8 *NewCanonicalPath;
- BOOLEAN RootEscape;
- UINT64 DirNodeId;
- CHAR8 *LastComponent;
- UINT64 NewNodeId;
- UINT64 NewFuseHandle;
- BOOLEAN NewNodeIsDirectory;
- VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
- VirtioFs = VirtioFsFile->OwnerFs;
- //
- // Validate OpenMode.
- //
- switch (OpenMode) {
- case EFI_FILE_MODE_READ:
- OpenForWriting = FALSE;
- PermitCreation = FALSE;
- break;
- case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE:
- OpenForWriting = TRUE;
- PermitCreation = FALSE;
- break;
- case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE:
- OpenForWriting = TRUE;
- PermitCreation = TRUE;
- break;
- default:
- return EFI_INVALID_PARAMETER;
- }
- //
- // Set CreateDirectoryIfCreating to suppress incorrect compiler/analyzer
- // warnings.
- //
- CreateDirectoryIfCreating = FALSE;
- //
- // Validate the Attributes requested for the case when the file ends up being
- // created, provided creation is permitted.
- //
- if (PermitCreation) {
- if ((Attributes & ~EFI_FILE_VALID_ATTR) != 0) {
- //
- // Unknown attribute requested.
- //
- return EFI_INVALID_PARAMETER;
- }
- ASSERT (OpenForWriting);
- if ((Attributes & EFI_FILE_READ_ONLY) != 0) {
- DEBUG ((
- DEBUG_ERROR,
- ("%a: Label=\"%s\" CanonicalPathname=\"%a\" FileName=\"%s\" "
- "OpenMode=0x%Lx Attributes=0x%Lx: nonsensical request to possibly "
- "create a file marked read-only, for read-write access\n"),
- __FUNCTION__,
- VirtioFs->Label,
- VirtioFsFile->CanonicalPathname,
- FileName,
- OpenMode,
- Attributes
- ));
- return EFI_INVALID_PARAMETER;
- }
- CreateDirectoryIfCreating = (BOOLEAN)((Attributes &
- EFI_FILE_DIRECTORY) != 0);
- }
- //
- // Referring to a file relative to a regular file makes no sense (or at least
- // it cannot be implemented consistently with how a file is referred to
- // relative to a directory).
- //
- if (!VirtioFsFile->IsDirectory) {
- DEBUG ((
- DEBUG_ERROR,
- ("%a: Label=\"%s\" CanonicalPathname=\"%a\" FileName=\"%s\": "
- "nonsensical request to open a file or directory relative to a regular "
- "file\n"),
- __FUNCTION__,
- VirtioFs->Label,
- VirtioFsFile->CanonicalPathname,
- FileName
- ));
- return EFI_INVALID_PARAMETER;
- }
- //
- // Allocate the new VIRTIO_FS_FILE object.
- //
- NewVirtioFsFile = AllocatePool (sizeof *NewVirtioFsFile);
- if (NewVirtioFsFile == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- //
- // Create the canonical pathname at which the desired file is expected to
- // exist.
- //
- Status = VirtioFsAppendPath (
- VirtioFsFile->CanonicalPathname,
- FileName,
- &NewCanonicalPath,
- &RootEscape
- );
- if (EFI_ERROR (Status)) {
- goto FreeNewVirtioFsFile;
- }
- if (RootEscape) {
- Status = EFI_ACCESS_DENIED;
- goto FreeNewCanonicalPath;
- }
- //
- // If the desired file is the root directory, just open the volume one more
- // time, without looking up anything.
- //
- if (AsciiStrCmp (NewCanonicalPath, "/") == 0) {
- FreePool (NewCanonicalPath);
- FreePool (NewVirtioFsFile);
- return OpenRootDirectory (VirtioFs, NewHandle, OpenForWriting);
- }
- //
- // Split the new canonical pathname into most specific parent directory
- // (given by DirNodeId) and last pathname component (i.e., immediate child
- // within that parent directory).
- //
- Status = VirtioFsLookupMostSpecificParentDir (
- VirtioFs,
- NewCanonicalPath,
- &DirNodeId,
- &LastComponent
- );
- if (EFI_ERROR (Status)) {
- goto FreeNewCanonicalPath;
- }
- //
- // Set NewNodeIsDirectory to suppress incorrect compiler/analyzer warnings.
- //
- NewNodeIsDirectory = FALSE;
- //
- // Try to open LastComponent directly under DirNodeId, as an existent regular
- // file or directory.
- //
- Status = OpenExistentFileOrDirectory (
- VirtioFs,
- DirNodeId,
- LastComponent,
- OpenForWriting,
- &NewNodeId,
- &NewFuseHandle,
- &NewNodeIsDirectory
- );
- //
- // If LastComponent could not be found under DirNodeId, but the request
- // allows us to create a new entry, attempt creating the requested regular
- // file or directory.
- //
- if ((Status == EFI_NOT_FOUND) && PermitCreation) {
- ASSERT (OpenForWriting);
- if (CreateDirectoryIfCreating) {
- Status = CreateDirectory (
- VirtioFs,
- DirNodeId,
- LastComponent,
- &NewNodeId,
- &NewFuseHandle
- );
- } else {
- Status = CreateRegularFile (
- VirtioFs,
- DirNodeId,
- LastComponent,
- &NewNodeId,
- &NewFuseHandle
- );
- }
- NewNodeIsDirectory = CreateDirectoryIfCreating;
- }
- //
- // Regardless of the branch taken, we're done with DirNodeId.
- //
- if (DirNodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
- VirtioFsFuseForget (VirtioFs, DirNodeId);
- }
- if (EFI_ERROR (Status)) {
- goto FreeNewCanonicalPath;
- }
- //
- // Populate the new VIRTIO_FS_FILE object.
- //
- NewVirtioFsFile->Signature = VIRTIO_FS_FILE_SIG;
- NewVirtioFsFile->SimpleFile.Revision = EFI_FILE_PROTOCOL_REVISION;
- NewVirtioFsFile->SimpleFile.Open = VirtioFsSimpleFileOpen;
- NewVirtioFsFile->SimpleFile.Close = VirtioFsSimpleFileClose;
- NewVirtioFsFile->SimpleFile.Delete = VirtioFsSimpleFileDelete;
- NewVirtioFsFile->SimpleFile.Read = VirtioFsSimpleFileRead;
- NewVirtioFsFile->SimpleFile.Write = VirtioFsSimpleFileWrite;
- NewVirtioFsFile->SimpleFile.GetPosition = VirtioFsSimpleFileGetPosition;
- NewVirtioFsFile->SimpleFile.SetPosition = VirtioFsSimpleFileSetPosition;
- NewVirtioFsFile->SimpleFile.GetInfo = VirtioFsSimpleFileGetInfo;
- NewVirtioFsFile->SimpleFile.SetInfo = VirtioFsSimpleFileSetInfo;
- NewVirtioFsFile->SimpleFile.Flush = VirtioFsSimpleFileFlush;
- NewVirtioFsFile->IsDirectory = NewNodeIsDirectory;
- NewVirtioFsFile->IsOpenForWriting = OpenForWriting;
- NewVirtioFsFile->OwnerFs = VirtioFs;
- NewVirtioFsFile->CanonicalPathname = NewCanonicalPath;
- NewVirtioFsFile->FilePosition = 0;
- NewVirtioFsFile->NodeId = NewNodeId;
- NewVirtioFsFile->FuseHandle = NewFuseHandle;
- NewVirtioFsFile->FileInfoArray = NULL;
- NewVirtioFsFile->SingleFileInfoSize = 0;
- NewVirtioFsFile->NumFileInfo = 0;
- NewVirtioFsFile->NextFileInfo = 0;
- //
- // One more file is now open for the filesystem.
- //
- InsertTailList (&VirtioFs->OpenFiles, &NewVirtioFsFile->OpenFilesEntry);
- *NewHandle = &NewVirtioFsFile->SimpleFile;
- return EFI_SUCCESS;
- FreeNewCanonicalPath:
- FreePool (NewCanonicalPath);
- FreeNewVirtioFsFile:
- FreePool (NewVirtioFsFile);
- return Status;
- }
|