/** @file Routines dealing with setting/getting file/volume info Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "Fat.h" /** Get the volume's info into Buffer. @param Volume - FAT file system volume. @param BufferSize - Size of Buffer. @param Buffer - Buffer containing volume info. @retval EFI_SUCCESS - Get the volume info successfully. @retval EFI_BUFFER_TOO_SMALL - The buffer is too small. **/ EFI_STATUS FatGetVolumeInfo ( IN FAT_VOLUME *Volume, IN OUT UINTN *BufferSize, OUT VOID *Buffer ); /** Set the volume's info. @param Volume - FAT file system volume. @param BufferSize - Size of Buffer. @param Buffer - Buffer containing the new volume info. @retval EFI_SUCCESS - Set the volume info successfully. @retval EFI_BAD_BUFFER_SIZE - The buffer size is error. @retval EFI_WRITE_PROTECTED - The volume is read only. @return other - An error occurred when operation the disk. **/ EFI_STATUS FatSetVolumeInfo ( IN FAT_VOLUME *Volume, IN UINTN BufferSize, IN VOID *Buffer ); /** Set or Get the some types info of the file into Buffer. @param IsSet - TRUE:The access is set, else is get @param FHand - The handle of file @param Type - The type of the info @param BufferSize - Size of Buffer @param Buffer - Buffer containing volume info @retval EFI_SUCCESS - Get the info successfully @retval EFI_DEVICE_ERROR - Can not find the OFile for the file **/ EFI_STATUS FatSetOrGetInfo ( IN BOOLEAN IsSet, IN EFI_FILE_PROTOCOL *FHand, IN EFI_GUID *Type, IN OUT UINTN *BufferSize, IN OUT VOID *Buffer ); /** Get the open file's info into Buffer. @param OFile - The open file. @param BufferSize - Size of Buffer. @param Buffer - Buffer containing file info. @retval EFI_SUCCESS - Get the file info successfully. @retval EFI_BUFFER_TOO_SMALL - The buffer is too small. **/ EFI_STATUS FatGetFileInfo ( IN FAT_OFILE *OFile, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { return FatGetDirEntInfo (OFile->Volume, OFile->DirEnt, BufferSize, Buffer); } /** Get the volume's info into Buffer. @param Volume - FAT file system volume. @param BufferSize - Size of Buffer. @param Buffer - Buffer containing volume info. @retval EFI_SUCCESS - Get the volume info successfully. @retval EFI_BUFFER_TOO_SMALL - The buffer is too small. **/ EFI_STATUS FatGetVolumeInfo ( IN FAT_VOLUME *Volume, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { UINTN Size; UINTN NameSize; UINTN ResultSize; CHAR16 Name[FAT_NAME_LEN + 1]; EFI_STATUS Status; EFI_FILE_SYSTEM_INFO *Info; UINT8 ClusterAlignment; Size = SIZE_OF_EFI_FILE_SYSTEM_INFO; Status = FatGetVolumeEntry (Volume, Name); NameSize = StrSize (Name); ResultSize = Size + NameSize; ClusterAlignment = Volume->ClusterAlignment; // // If we don't have valid info, compute it now // FatComputeFreeInfo (Volume); Status = EFI_BUFFER_TOO_SMALL; if (*BufferSize >= ResultSize) { Status = EFI_SUCCESS; Info = Buffer; ZeroMem (Info, SIZE_OF_EFI_FILE_SYSTEM_INFO); Info->Size = ResultSize; Info->ReadOnly = Volume->ReadOnly; Info->BlockSize = (UINT32)Volume->ClusterSize; Info->VolumeSize = LShiftU64 (Volume->MaxCluster, ClusterAlignment); Info->FreeSpace = LShiftU64 ( Volume->FatInfoSector.FreeInfo.ClusterCount, ClusterAlignment ); CopyMem ((CHAR8 *)Buffer + Size, Name, NameSize); } *BufferSize = ResultSize; return Status; } /** Get the volume's label info into Buffer. @param Volume - FAT file system volume. @param BufferSize - Size of Buffer. @param Buffer - Buffer containing volume's label info. @retval EFI_SUCCESS - Get the volume's label info successfully. @retval EFI_BUFFER_TOO_SMALL - The buffer is too small. **/ EFI_STATUS FatGetVolumeLabelInfo ( IN FAT_VOLUME *Volume, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { UINTN Size; UINTN NameSize; UINTN ResultSize; CHAR16 Name[FAT_NAME_LEN + 1]; EFI_STATUS Status; Size = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL; Status = FatGetVolumeEntry (Volume, Name); NameSize = StrSize (Name); ResultSize = Size + NameSize; Status = EFI_BUFFER_TOO_SMALL; if (*BufferSize >= ResultSize) { Status = EFI_SUCCESS; CopyMem ((CHAR8 *)Buffer + Size, Name, NameSize); } *BufferSize = ResultSize; return Status; } /** Set the volume's info. @param Volume - FAT file system volume. @param BufferSize - Size of Buffer. @param Buffer - Buffer containing the new volume info. @retval EFI_SUCCESS - Set the volume info successfully. @retval EFI_BAD_BUFFER_SIZE - The buffer size is error. @retval EFI_WRITE_PROTECTED - The volume is read only. @return other - An error occurred when operation the disk. **/ EFI_STATUS FatSetVolumeInfo ( IN FAT_VOLUME *Volume, IN UINTN BufferSize, IN VOID *Buffer ) { EFI_FILE_SYSTEM_INFO *Info; Info = (EFI_FILE_SYSTEM_INFO *)Buffer; if ((BufferSize < SIZE_OF_EFI_FILE_SYSTEM_INFO + 2) || (Info->Size > BufferSize)) { return EFI_BAD_BUFFER_SIZE; } return FatSetVolumeEntry (Volume, Info->VolumeLabel); } /** Set the volume's label info. @param Volume - FAT file system volume. @param BufferSize - Size of Buffer. @param Buffer - Buffer containing the new volume label info. @retval EFI_SUCCESS - Set the volume label info successfully. @retval EFI_WRITE_PROTECTED - The disk is write protected. @retval EFI_BAD_BUFFER_SIZE - The buffer size is error. @return other - An error occurred when operation the disk. **/ EFI_STATUS FatSetVolumeLabelInfo ( IN FAT_VOLUME *Volume, IN UINTN BufferSize, IN VOID *Buffer ) { EFI_FILE_SYSTEM_VOLUME_LABEL *Info; Info = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Buffer; if (BufferSize < SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL + 2) { return EFI_BAD_BUFFER_SIZE; } return FatSetVolumeEntry (Volume, Info->VolumeLabel); } /** Set the file info. @param Volume - FAT file system volume. @param IFile - The instance of the open file. @param OFile - The open file. @param BufferSize - Size of Buffer. @param Buffer - Buffer containing the new file info. @retval EFI_SUCCESS - Set the file info successfully. @retval EFI_ACCESS_DENIED - It is the root directory or the directory attribute bit can not change or try to change a directory size or something else. @retval EFI_UNSUPPORTED - The new file size is larger than 4GB. @retval EFI_WRITE_PROTECTED - The disk is write protected. @retval EFI_BAD_BUFFER_SIZE - The buffer size is error. @retval EFI_INVALID_PARAMETER - The time info or attributes info is error. @retval EFI_OUT_OF_RESOURCES - Can not allocate new memory. @retval EFI_VOLUME_CORRUPTED - The volume is corrupted. @return other - An error occurred when operation the disk. **/ EFI_STATUS FatSetFileInfo ( IN FAT_VOLUME *Volume, IN FAT_IFILE *IFile, IN FAT_OFILE *OFile, IN UINTN BufferSize, IN VOID *Buffer ) { EFI_STATUS Status; EFI_FILE_INFO *NewInfo; FAT_OFILE *DotOFile; FAT_OFILE *Parent; CHAR16 NewFileName[EFI_PATH_STRING_LENGTH]; EFI_TIME ZeroTime; FAT_DIRENT *DirEnt; FAT_DIRENT *TempDirEnt; UINT8 NewAttribute; BOOLEAN ReadOnly; ZeroMem (&ZeroTime, sizeof (EFI_TIME)); Parent = OFile->Parent; DirEnt = OFile->DirEnt; // // If this is the root directory, we can't make any updates // if (Parent == NULL) { return EFI_ACCESS_DENIED; } // // Make sure there's a valid input buffer // NewInfo = Buffer; if ((BufferSize < SIZE_OF_EFI_FILE_INFO + 2) || (NewInfo->Size > BufferSize)) { return EFI_BAD_BUFFER_SIZE; } ReadOnly = (BOOLEAN)(IFile->ReadOnly || (DirEnt->Entry.Attributes & EFI_FILE_READ_ONLY)); // // if a zero time is specified, then the original time is preserved // if (CompareMem (&ZeroTime, &NewInfo->CreateTime, sizeof (EFI_TIME)) != 0) { if (!FatIsValidTime (&NewInfo->CreateTime)) { return EFI_INVALID_PARAMETER; } if (!ReadOnly) { FatEfiTimeToFatTime (&NewInfo->CreateTime, &DirEnt->Entry.FileCreateTime); } } if (CompareMem (&ZeroTime, &NewInfo->ModificationTime, sizeof (EFI_TIME)) != 0) { if (!FatIsValidTime (&NewInfo->ModificationTime)) { return EFI_INVALID_PARAMETER; } if (!ReadOnly) { FatEfiTimeToFatTime (&NewInfo->ModificationTime, &DirEnt->Entry.FileModificationTime); } OFile->PreserveLastModification = TRUE; } if (NewInfo->Attribute & (~EFI_FILE_VALID_ATTR)) { return EFI_INVALID_PARAMETER; } NewAttribute = (UINT8)NewInfo->Attribute; // // Can not change the directory attribute bit // if ((NewAttribute ^ DirEnt->Entry.Attributes) & EFI_FILE_DIRECTORY) { return EFI_ACCESS_DENIED; } // // Set the current attributes even if the IFile->ReadOnly is TRUE // DirEnt->Entry.Attributes = (UINT8)((DirEnt->Entry.Attributes &~EFI_FILE_VALID_ATTR) | NewAttribute); // // Open the filename and see if it refers to an existing file // Status = FatLocateOFile (&Parent, NewInfo->FileName, DirEnt->Entry.Attributes, NewFileName); if (EFI_ERROR (Status)) { return Status; } if (*NewFileName != 0) { // // File was not found. We do not allow rename of the current directory if // there are open files below the current directory // if (!IsListEmpty (&OFile->ChildHead) || (Parent == OFile)) { return EFI_ACCESS_DENIED; } if (ReadOnly) { return EFI_ACCESS_DENIED; } Status = FatRemoveDirEnt (OFile->Parent, DirEnt); if (EFI_ERROR (Status)) { return Status; } // // Create new dirent // Status = FatCreateDirEnt (Parent, NewFileName, DirEnt->Entry.Attributes, &TempDirEnt); if (EFI_ERROR (Status)) { return Status; } FatCloneDirEnt (TempDirEnt, DirEnt); FatFreeDirEnt (DirEnt); DirEnt = TempDirEnt; DirEnt->OFile = OFile; OFile->DirEnt = DirEnt; OFile->Parent = Parent; RemoveEntryList (&OFile->ChildLink); InsertHeadList (&Parent->ChildHead, &OFile->ChildLink); // // If this is a directory, synchronize its dot directory entry // if (OFile->ODir != NULL) { // // Synchronize its dot entry // FatResetODirCursor (OFile); ASSERT (OFile->Parent != NULL); for (DotOFile = OFile; DotOFile != OFile->Parent->Parent; DotOFile = DotOFile->Parent) { Status = FatGetNextDirEnt (OFile, &DirEnt); if (EFI_ERROR (Status) || (DirEnt == NULL) || !FatIsDotDirEnt (DirEnt)) { return EFI_VOLUME_CORRUPTED; } FatCloneDirEnt (DirEnt, DotOFile->DirEnt); Status = FatStoreDirEnt (OFile, DirEnt); if (EFI_ERROR (Status)) { return Status; } } } // // If the file is renamed, we should append the ARCHIVE attribute // OFile->Archive = TRUE; } else if (Parent != OFile) { // // filename is to a different filename that already exists // return EFI_ACCESS_DENIED; } // // If the file size has changed, apply it // if (NewInfo->FileSize != OFile->FileSize) { if ((OFile->ODir != NULL) || ReadOnly) { // // If this is a directory or the file is read only, we can't change the file size // return EFI_ACCESS_DENIED; } if (NewInfo->FileSize > OFile->FileSize) { Status = FatExpandOFile (OFile, NewInfo->FileSize); } else { Status = FatTruncateOFile (OFile, (UINTN)NewInfo->FileSize); } if (EFI_ERROR (Status)) { return Status; } FatUpdateDirEntClusterSizeInfo (OFile); } OFile->Dirty = TRUE; return FatOFileFlush (OFile); } /** Set or Get the some types info of the file into Buffer. @param IsSet - TRUE:The access is set, else is get @param FHand - The handle of file @param Type - The type of the info @param BufferSize - Size of Buffer @param Buffer - Buffer containing volume info @retval EFI_SUCCESS - Get the info successfully @retval EFI_DEVICE_ERROR - Can not find the OFile for the file **/ EFI_STATUS FatSetOrGetInfo ( IN BOOLEAN IsSet, IN EFI_FILE_PROTOCOL *FHand, IN EFI_GUID *Type, IN OUT UINTN *BufferSize, IN OUT VOID *Buffer ) { FAT_IFILE *IFile; FAT_OFILE *OFile; FAT_VOLUME *Volume; EFI_STATUS Status; IFile = IFILE_FROM_FHAND (FHand); OFile = IFile->OFile; Volume = OFile->Volume; Status = OFile->Error; if (Status == EFI_NOT_FOUND) { return EFI_DEVICE_ERROR; } FatWaitNonblockingTask (IFile); FatAcquireLock (); // // Verify the file handle isn't in an error state // if (!EFI_ERROR (Status)) { // // Get the proper information based on the request // Status = EFI_UNSUPPORTED; if (IsSet) { if (CompareGuid (Type, &gEfiFileInfoGuid)) { Status = Volume->ReadOnly ? EFI_WRITE_PROTECTED : FatSetFileInfo (Volume, IFile, OFile, *BufferSize, Buffer); } if (CompareGuid (Type, &gEfiFileSystemInfoGuid)) { Status = Volume->ReadOnly ? EFI_WRITE_PROTECTED : FatSetVolumeInfo (Volume, *BufferSize, Buffer); } if (CompareGuid (Type, &gEfiFileSystemVolumeLabelInfoIdGuid)) { Status = Volume->ReadOnly ? EFI_WRITE_PROTECTED : FatSetVolumeLabelInfo (Volume, *BufferSize, Buffer); } } else { if (CompareGuid (Type, &gEfiFileInfoGuid)) { Status = FatGetFileInfo (OFile, BufferSize, Buffer); } if (CompareGuid (Type, &gEfiFileSystemInfoGuid)) { Status = FatGetVolumeInfo (Volume, BufferSize, Buffer); } if (CompareGuid (Type, &gEfiFileSystemVolumeLabelInfoIdGuid)) { Status = FatGetVolumeLabelInfo (Volume, BufferSize, Buffer); } } } Status = FatCleanupVolume (Volume, NULL, Status, NULL); FatReleaseLock (); return Status; } /** Get the some types info of the file into Buffer. @param FHand - The handle of file. @param Type - The type of the info. @param BufferSize - Size of Buffer. @param Buffer - Buffer containing volume info. @retval EFI_SUCCESS - Get the info successfully. @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. **/ EFI_STATUS EFIAPI FatGetInfo ( IN EFI_FILE_PROTOCOL *FHand, IN EFI_GUID *Type, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { return FatSetOrGetInfo (FALSE, FHand, Type, BufferSize, Buffer); } /** Set the some types info of the file into Buffer. @param FHand - The handle of file. @param Type - The type of the info. @param BufferSize - Size of Buffer @param Buffer - Buffer containing volume info. @retval EFI_SUCCESS - Set the info successfully. @retval EFI_DEVICE_ERROR - Can not find the OFile for the file. **/ EFI_STATUS EFIAPI FatSetInfo ( IN EFI_FILE_PROTOCOL *FHand, IN EFI_GUID *Type, IN UINTN BufferSize, IN VOID *Buffer ) { return FatSetOrGetInfo (TRUE, FHand, Type, &BufferSize, Buffer); }