123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641 |
- /** @file
- EFI_FILE_PROTOCOL.SetInfo() member function for the Virtio Filesystem driver.
- Copyright (C) 2020, Red Hat, Inc.
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #include <Guid/FileSystemInfo.h> // gEfiFileSystemInfoGuid
- #include <Guid/FileSystemVolumeLabelInfo.h> // gEfiFileSystemVolumeLabelInfo...
- #include <Library/BaseLib.h> // StrCmp()
- #include <Library/BaseMemoryLib.h> // CompareGuid()
- #include <Library/MemoryAllocationLib.h> // FreePool()
- #include "VirtioFsDxe.h"
- /**
- Validate a buffer that the EFI_FILE_PROTOCOL.SetInfo() caller passes in for a
- particular InformationType GUID.
- The structure to be validated is supposed to end with a variable-length,
- NUL-terminated CHAR16 Name string.
- @param[in] SizeByProtocolCaller The BufferSize parameter as provided by the
- EFI_FILE_PROTOCOL.SetInfo() caller.
- @param[in] MinimumStructSize The minimum structure size that is required
- for the given InformationType GUID,
- including a single CHAR16 element from the
- trailing Name field.
- @param[in] IsSizeByInfoPresent TRUE if and only if the expected structure
- starts with a UINT64 Size field that reports
- the actual structure size.
- @param[in] Buffer The Buffer parameter as provided by the
- EFI_FILE_PROTOCOL.SetInfo() caller.
- @retval EFI_SUCCESS Validation successful, Buffer is well-formed.
- @retval EFI_BAD_BUFFER_SIZE The EFI_FILE_PROTOCOL.SetInfo()
- caller provided a BufferSize that is smaller
- than the minimum structure size required for
- the given InformationType GUID.
- @retval EFI_INVALID_PARAMETER IsSizeByInfoPresent is TRUE, and the leading
- UINT64 Size field does not match the
- EFI_FILE_PROTOCOL.SetInfo() caller-provided
- BufferSize.
- @retval EFI_INVALID_PARAMETER The trailing Name field does not consist of a
- whole multiple of CHAR16 elements.
- @retval EFI_INVALID_PARAMETER The trailing Name field is not NUL-terminated.
- **/
- STATIC
- EFI_STATUS
- ValidateInfoStructure (
- IN UINTN SizeByProtocolCaller,
- IN UINTN MinimumStructSize,
- IN BOOLEAN IsSizeByInfoPresent,
- IN VOID *Buffer
- )
- {
- UINTN NameFieldByteOffset;
- UINTN NameFieldBytes;
- UINTN NameFieldChar16s;
- CHAR16 *NameField;
- //
- // Make sure the internal function asking for validation passes in sane
- // values.
- //
- ASSERT (MinimumStructSize >= sizeof (CHAR16));
- NameFieldByteOffset = MinimumStructSize - sizeof (CHAR16);
- if (IsSizeByInfoPresent) {
- ASSERT (MinimumStructSize >= sizeof (UINT64) + sizeof (CHAR16));
- ASSERT (NameFieldByteOffset >= sizeof (UINT64));
- }
- //
- // Check whether the protocol caller provided enough bytes for the minimum
- // size of this info structure.
- //
- if (SizeByProtocolCaller < MinimumStructSize) {
- return EFI_BAD_BUFFER_SIZE;
- }
- //
- // If the info structure starts with a UINT64 Size field, check if that
- // agrees with the protocol caller-provided size.
- //
- if (IsSizeByInfoPresent) {
- UINT64 *SizeByInfo;
- SizeByInfo = Buffer;
- if (*SizeByInfo != SizeByProtocolCaller) {
- return EFI_INVALID_PARAMETER;
- }
- }
- //
- // The CHAR16 Name field at the end of the structure must have an even number
- // of bytes.
- //
- // The subtraction below cannot underflow, and yields at least
- // sizeof(CHAR16).
- //
- ASSERT (SizeByProtocolCaller >= NameFieldByteOffset);
- NameFieldBytes = SizeByProtocolCaller - NameFieldByteOffset;
- ASSERT (NameFieldBytes >= sizeof (CHAR16));
- if (NameFieldBytes % sizeof (CHAR16) != 0) {
- return EFI_INVALID_PARAMETER;
- }
- //
- // The CHAR16 Name field at the end of the structure must be NUL-terminated.
- //
- NameFieldChar16s = NameFieldBytes / sizeof (CHAR16);
- ASSERT (NameFieldChar16s >= 1);
- NameField = (CHAR16 *)((UINT8 *)Buffer + NameFieldByteOffset);
- if (NameField[NameFieldChar16s - 1] != L'\0') {
- return EFI_INVALID_PARAMETER;
- }
- return EFI_SUCCESS;
- }
- /**
- 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;
- }
- /**
- Update the attributes of a VIRTIO_FS_FILE as requested in EFI_FILE_INFO.
- @param[in,out] VirtioFsFile The VIRTIO_FS_FILE to update the attributes of.
- @param[in] NewFileInfo The new attributes requested by
- EFI_FILE_PROTOCOL.SetInfo(). NewFileInfo->Size
- and NewFileInfo->FileName are ignored.
- @retval EFI_SUCCESS No attributes had to be updated.
- @retval EFI_SUCCESS The required set of attribute updates has been
- determined and performed successfully.
- @retval EFI_ACCESS_DENIED NewFileInfo requests an update to a property
- different from the EFI_FILE_READ_ONLY bit in the
- Attribute field, but VirtioFsFile is not open for
- writing.
- @return Error codes propagated from underlying functions.
- **/
- STATIC
- EFI_STATUS
- UpdateAttributes (
- IN OUT VIRTIO_FS_FILE *VirtioFsFile,
- IN EFI_FILE_INFO *NewFileInfo
- )
- {
- VIRTIO_FS *VirtioFs;
- EFI_STATUS Status;
- VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
- EFI_FILE_INFO FileInfo;
- BOOLEAN UpdateFileSize;
- UINT64 FileSize;
- BOOLEAN UpdateAtime;
- BOOLEAN UpdateMtime;
- UINT64 Atime;
- UINT64 Mtime;
- BOOLEAN UpdateMode;
- UINT32 Mode;
- VirtioFs = VirtioFsFile->OwnerFs;
- //
- // Fetch the current attributes first, so we can build the difference between
- // them and NewFileInfo.
- //
- Status = VirtioFsFuseGetAttr (VirtioFs, VirtioFsFile->NodeId, &FuseAttr);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- Status = VirtioFsFuseAttrToEfiFileInfo (&FuseAttr, &FileInfo);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- //
- // Collect the updates.
- //
- if (VirtioFsFile->IsDirectory) {
- UpdateFileSize = FALSE;
- } else {
- VirtioFsGetFuseSizeUpdate (
- &FileInfo,
- NewFileInfo,
- &UpdateFileSize,
- &FileSize
- );
- }
- Status = VirtioFsGetFuseTimeUpdates (
- &FileInfo,
- NewFileInfo,
- &UpdateAtime,
- &UpdateMtime,
- &Atime,
- &Mtime
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- Status = VirtioFsGetFuseModeUpdate (
- &FileInfo,
- NewFileInfo,
- &UpdateMode,
- &Mode
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- //
- // If no attribute updates are necessary, we're done.
- //
- if (!UpdateFileSize && !UpdateAtime && !UpdateMtime && !UpdateMode) {
- return EFI_SUCCESS;
- }
- //
- // If the file is not open for writing, then only Mode may be updated (for
- // toggling EFI_FILE_READ_ONLY).
- //
- if (!VirtioFsFile->IsOpenForWriting &&
- (UpdateFileSize || UpdateAtime || UpdateMtime))
- {
- return EFI_ACCESS_DENIED;
- }
- //
- // Send the FUSE_SETATTR request now.
- //
- Status = VirtioFsFuseSetAttr (
- VirtioFs,
- VirtioFsFile->NodeId,
- UpdateFileSize ? &FileSize : NULL,
- UpdateAtime ? &Atime : NULL,
- UpdateMtime ? &Mtime : NULL,
- UpdateMode ? &Mode : NULL
- );
- 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 = UpdateAttributes (VirtioFsFile, FileInfo);
- //
- // 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
- EFI_STATUS
- SetFileSystemInfo (
- IN EFI_FILE_PROTOCOL *This,
- IN UINTN BufferSize,
- IN VOID *Buffer
- )
- {
- VIRTIO_FS_FILE *VirtioFsFile;
- VIRTIO_FS *VirtioFs;
- EFI_STATUS Status;
- EFI_FILE_SYSTEM_INFO *FileSystemInfo;
- VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
- VirtioFs = VirtioFsFile->OwnerFs;
- //
- // Validate if Buffer passes as EFI_FILE_SYSTEM_INFO.
- //
- Status = ValidateInfoStructure (
- BufferSize, // SizeByProtocolCaller
- OFFSET_OF (
- EFI_FILE_SYSTEM_INFO,
- VolumeLabel
- ) + sizeof (CHAR16), // MinimumStructSize
- TRUE, // IsSizeByInfoPresent
- Buffer
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- FileSystemInfo = Buffer;
- //
- // EFI_FILE_SYSTEM_INFO fields other than VolumeLabel cannot be changed, per
- // spec.
- //
- // If the label is being changed to its current value, report success;
- // otherwise, reject the request, as the Virtio Filesystem device does not
- // support changing the label.
- //
- if (StrCmp (FileSystemInfo->VolumeLabel, VirtioFs->Label) == 0) {
- return EFI_SUCCESS;
- }
- return EFI_WRITE_PROTECTED;
- }
- /**
- Process an EFI_FILE_SYSTEM_VOLUME_LABEL setting request.
- **/
- STATIC
- EFI_STATUS
- SetFileSystemVolumeLabelInfo (
- IN EFI_FILE_PROTOCOL *This,
- IN UINTN BufferSize,
- IN VOID *Buffer
- )
- {
- VIRTIO_FS_FILE *VirtioFsFile;
- VIRTIO_FS *VirtioFs;
- EFI_STATUS Status;
- EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel;
- VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
- VirtioFs = VirtioFsFile->OwnerFs;
- //
- // Validate if Buffer passes as EFI_FILE_SYSTEM_VOLUME_LABEL.
- //
- Status = ValidateInfoStructure (
- BufferSize, // SizeByProtocolCaller
- OFFSET_OF (
- EFI_FILE_SYSTEM_VOLUME_LABEL,
- VolumeLabel
- ) + sizeof (CHAR16), // MinimumStructSize
- FALSE, // IsSizeByInfoPresent
- Buffer
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- FileSystemVolumeLabel = Buffer;
- //
- // If the label is being changed to its current value, report success;
- // otherwise, reject the request, as the Virtio Filesystem device does not
- // support changing the label.
- //
- if (StrCmp (FileSystemVolumeLabel->VolumeLabel, VirtioFs->Label) == 0) {
- return EFI_SUCCESS;
- }
- return EFI_WRITE_PROTECTED;
- }
- EFI_STATUS
- EFIAPI
- VirtioFsSimpleFileSetInfo (
- IN EFI_FILE_PROTOCOL *This,
- IN EFI_GUID *InformationType,
- IN UINTN BufferSize,
- IN VOID *Buffer
- )
- {
- if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
- return SetFileInfo (This, BufferSize, Buffer);
- }
- if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
- return SetFileSystemInfo (This, BufferSize, Buffer);
- }
- if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
- return SetFileSystemVolumeLabelInfo (This, BufferSize, Buffer);
- }
- return EFI_UNSUPPORTED;
- }
|