EFI_STATUS FileSetPosition ( IN EFI_FILE *File, IN UINT64 Position ) { SEMIHOST_FCB *Fcb = NULL; UINTN Length; EFI_STATUS Status; Fcb = SEMIHOST_FCB_FROM_THIS(File); if (!Fcb->IsRoot) { Status = SemihostFileLength (Fcb->SemihostHandle, &Length); if (!EFI_ERROR(Status) && (Length < Position)) { Position = Length; } Status = SemihostFileSeek (Fcb->SemihostHandle, (UINT32)Position); if (!EFI_ERROR(Status)) { Fcb->Position = Position; } } else { Fcb->Position = Position; Status = EFI_SUCCESS; } return Status; }
STATIC EFI_STATUS GetFileInfo ( IN SEMIHOST_FCB *Fcb, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { EFI_FILE_INFO *Info = NULL; UINTN NameSize = 0; UINTN ResultSize; UINTN Index; UINTN Length; EFI_STATUS Status; if (Fcb->IsRoot == TRUE) { ResultSize = SIZE_OF_EFI_FILE_INFO + sizeof(CHAR16); } else { NameSize = AsciiStrLen (Fcb->FileName) + 1; ResultSize = SIZE_OF_EFI_FILE_INFO + NameSize * sizeof (CHAR16); } if (*BufferSize < ResultSize) { *BufferSize = ResultSize; return EFI_BUFFER_TOO_SMALL; } Info = Buffer; // Zero out the structure ZeroMem (Info, SIZE_OF_EFI_FILE_INFO); // Fill in the structure Info->Size = ResultSize; if (Fcb->IsRoot == TRUE) { Info->Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY; Info->FileName[0] = L'\0'; } else { Status = SemihostFileLength (Fcb->SemihostHandle, &Length); if (EFI_ERROR(Status)) { return Status; } Info->FileSize = Length; Info->PhysicalSize = Length; for (Index = 0; Index < NameSize; Index++) { Info->FileName[Index] = Fcb->FileName[Index]; } } *BufferSize = ResultSize; return EFI_SUCCESS; }
/** Write data to an open file. @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file handle to write data to. @param[in out] BufferSize On input, the size of the Buffer. On output, the size of the data actually written. In both cases, the size is measured in bytes. @param[in] Buffer The buffer of data to write. @retval EFI_SUCCESS The data was written. @retval EFI_ACCESS_DENIED Attempt to write into a read only file or in a file opened in read only mode. @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. @retval EFI_INVALID_PARAMETER At least one of the three input pointers is NULL. **/ EFI_STATUS FileWrite ( IN EFI_FILE *This, IN OUT UINTN *BufferSize, IN VOID *Buffer ) { SEMIHOST_FCB *Fcb; EFI_STATUS Status; UINTN WriteSize; RETURN_STATUS Return; UINTN Length; if ((This == NULL) || (BufferSize == NULL) || (Buffer == NULL)) { return EFI_INVALID_PARAMETER; } Fcb = SEMIHOST_FCB_FROM_THIS (This); // We cannot write a read-only file if ((Fcb->Info.Attribute & EFI_FILE_READ_ONLY) || !(Fcb->OpenMode & EFI_FILE_MODE_WRITE)) { return EFI_ACCESS_DENIED; } // // If the position has been set past the end of the file, first grow the // file from its current size "Fcb->Info.FileSize" to "Fcb->Position" // size, filling the gap with zeros. // if (Fcb->Position > Fcb->Info.FileSize) { Status = ExtendFile (Fcb, Fcb->Position - Fcb->Info.FileSize); if (EFI_ERROR (Status)) { return Status; } Fcb->Info.FileSize = Fcb->Position; } WriteSize = *BufferSize; Return = SemihostFileWrite (Fcb->SemihostHandle, &WriteSize, Buffer); if (RETURN_ERROR (Return)) { return EFI_DEVICE_ERROR; } Fcb->Position += *BufferSize; if (Fcb->Position > Fcb->Info.FileSize) { Fcb->Info.FileSize = Fcb->Position; } Return = SemihostFileLength (Fcb->SemihostHandle, &Length); if (RETURN_ERROR (Return)) { return EFI_DEVICE_ERROR; } Fcb->Info.PhysicalSize = Length; return EFI_SUCCESS; }
/** Set information about a file. @param[in] Fcb A pointer to the description of the open file. @param[in] Info A pointer to the file information to write. @retval EFI_SUCCESS The information was set. @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file to a file that is already present. @retval EFI_ACCESS_DENIED An attempt is being made to change the EFI_FILE_DIRECTORY Attribute. @retval EFI_ACCESS_DENIED The file is a read-only file or has been opened in read-only mode and an attempt is being made to modify a field other than Attribute. @retval EFI_WRITE_PROTECTED An attempt is being made to modify a read-only attribute. @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. @retval EFI_OUT_OF_RESOURCES A allocation needed to process the request failed. **/ STATIC EFI_STATUS SetFileInfo ( IN SEMIHOST_FCB *Fcb, IN EFI_FILE_INFO *Info ) { EFI_STATUS Status; RETURN_STATUS Return; BOOLEAN FileSizeIsDifferent; BOOLEAN FileNameIsDifferent; BOOLEAN ReadOnlyIsDifferent; CHAR8 *AsciiFileName; UINTN FileSize; UINTN Length; UINTN SemihostHandle; // // A directory can not be changed to a file and a file can // not be changed to a directory. // if (((Info->Attribute & EFI_FILE_DIRECTORY) != 0) != Fcb->IsRoot) { return EFI_ACCESS_DENIED; } AsciiFileName = AllocatePool (StrLen (Info->FileName) + 1); if (AsciiFileName == NULL) { return EFI_OUT_OF_RESOURCES; } UnicodeStrToAsciiStr (Info->FileName, AsciiFileName); FileSizeIsDifferent = (Info->FileSize != Fcb->Info.FileSize); FileNameIsDifferent = (AsciiStrCmp (AsciiFileName, Fcb->FileName) != 0); ReadOnlyIsDifferent = CompareMem ( &Info->CreateTime, &Fcb->Info.CreateTime, 3 * sizeof (EFI_TIME) ) != 0; // // For a read-only file or a file opened in read-only mode, only // the Attribute field can be modified. As the root directory is // read-only (i.e. VolumeOpen()), this protects the root directory // description. // if ((Fcb->OpenMode == EFI_FILE_MODE_READ) || (Fcb->Info.Attribute & EFI_FILE_READ_ONLY) ) { if (FileSizeIsDifferent || FileNameIsDifferent || ReadOnlyIsDifferent) { Status = EFI_ACCESS_DENIED; goto Error; } } if (ReadOnlyIsDifferent) { Status = EFI_WRITE_PROTECTED; goto Error; } Status = EFI_DEVICE_ERROR; if (FileSizeIsDifferent) { FileSize = Info->FileSize; if (Fcb->Info.FileSize < FileSize) { Status = ExtendFile (Fcb, FileSize - Fcb->Info.FileSize); if (EFI_ERROR (Status)) { goto Error; } // // The read/write position from the host file system point of view // is at the end of the file. If the position from this module // point of view is smaller than the new file size, then // ask the host file system to move to that position. // if (Fcb->Position < FileSize) { FileSetPosition (&Fcb->File, Fcb->Position); } } Fcb->Info.FileSize = FileSize; Return = SemihostFileLength (Fcb->SemihostHandle, &Length); if (RETURN_ERROR (Return)) { goto Error; } Fcb->Info.PhysicalSize = Length; } // // Note down in RAM the Attribute field but we can not ask // for its modification to the host file system as the // semi-host interface does not provide this feature. // Fcb->Info.Attribute = Info->Attribute; if (FileNameIsDifferent) { Return = SemihostFileOpen ( AsciiFileName, SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY, &SemihostHandle ); if (!RETURN_ERROR (Return)) { SemihostFileClose (SemihostHandle); Status = EFI_ACCESS_DENIED; goto Error; } Return = SemihostFileRename (Fcb->FileName, AsciiFileName); if (RETURN_ERROR (Return)) { goto Error; } FreePool (Fcb->FileName); Fcb->FileName = AsciiFileName; AsciiFileName = NULL; } Status = EFI_SUCCESS; Error: if (AsciiFileName != NULL) { FreePool (AsciiFileName); } return Status; }
/** Open a file on the host system by means of the semihosting interface. @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file handle to source location. @param[out] NewHandle A pointer to the location to return the opened handle for the new file. @param[in] FileName The Null-terminated string of the name of the file to be opened. @param[in] OpenMode The mode to open the file : Read or Read/Write or Read/Write/Create @param[in] Attributes Only valid for EFI_FILE_MODE_CREATE, in which case these are the attribute bits for the newly created file. The mnemonics of the attribute bits are : EFI_FILE_READ_ONLY, EFI_FILE_HIDDEN, EFI_FILE_SYSTEM, EFI_FILE_RESERVED, EFI_FILE_DIRECTORY and EFI_FILE_ARCHIVE. @retval EFI_SUCCESS The file was open. @retval EFI_NOT_FOUND The specified file could not be found. @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. @retval EFI_WRITE_PROTECTED Attempt to create a directory. This is not possible with the semi-hosting interface. @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. **/ EFI_STATUS FileOpen ( IN EFI_FILE *This, OUT EFI_FILE **NewHandle, IN CHAR16 *FileName, IN UINT64 OpenMode, IN UINT64 Attributes ) { SEMIHOST_FCB *FileFcb; RETURN_STATUS Return; EFI_STATUS Status; UINTN SemihostHandle; CHAR8 *AsciiFileName; UINT32 SemihostMode; UINTN Length; if ((FileName == NULL) || (NewHandle == NULL)) { return EFI_INVALID_PARAMETER; } if ( (OpenMode != EFI_FILE_MODE_READ) && (OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE)) && (OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE)) ) { return EFI_INVALID_PARAMETER; } if ((OpenMode & EFI_FILE_MODE_CREATE) && (Attributes & EFI_FILE_DIRECTORY) ) { return EFI_WRITE_PROTECTED; } AsciiFileName = AllocatePool (StrLen (FileName) + 1); if (AsciiFileName == NULL) { return EFI_OUT_OF_RESOURCES; } UnicodeStrToAsciiStr (FileName, AsciiFileName); // Opening '/', '\', '.', or the NULL pathname is trying to open the root directory if ((AsciiStrCmp (AsciiFileName, "\\") == 0) || (AsciiStrCmp (AsciiFileName, "/") == 0) || (AsciiStrCmp (AsciiFileName, "") == 0) || (AsciiStrCmp (AsciiFileName, ".") == 0) ) { FreePool (AsciiFileName); return (VolumeOpen (&gSemihostFs, NewHandle)); } // // No control is done here concerning the file path. It is passed // as it is to the host operating system through the semi-hosting // interface. We first try to open the file in the read or update // mode even if the file creation has been asked for. That way, if // the file already exists, it is not truncated to zero length. In // write mode (bit SEMIHOST_FILE_MODE_WRITE up), if the file already // exists, it is reset to an empty file. // if (OpenMode == EFI_FILE_MODE_READ) { SemihostMode = SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY; } else { SemihostMode = SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY | SEMIHOST_FILE_MODE_UPDATE; } Return = SemihostFileOpen (AsciiFileName, SemihostMode, &SemihostHandle); if (RETURN_ERROR (Return)) { if (OpenMode & EFI_FILE_MODE_CREATE) { // // In the create if does not exist case, if the opening in update // mode failed, create it and open it in update mode. The update // mode allows for both read and write from and to the file. // Return = SemihostFileOpen ( AsciiFileName, SEMIHOST_FILE_MODE_WRITE | SEMIHOST_FILE_MODE_BINARY | SEMIHOST_FILE_MODE_UPDATE, &SemihostHandle ); if (RETURN_ERROR (Return)) { Status = EFI_DEVICE_ERROR; goto Error; } } else { Status = EFI_NOT_FOUND; goto Error; } } // Allocate a control block and fill it FileFcb = AllocateFCB (); if (FileFcb == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } FileFcb->FileName = AsciiFileName; FileFcb->SemihostHandle = SemihostHandle; FileFcb->Position = 0; FileFcb->IsRoot = 0; FileFcb->OpenMode = OpenMode; Return = SemihostFileLength (SemihostHandle, &Length); if (RETURN_ERROR (Return)) { Status = EFI_DEVICE_ERROR; FreeFCB (FileFcb); goto Error; } FileFcb->Info.FileSize = Length; FileFcb->Info.PhysicalSize = Length; FileFcb->Info.Attribute = (OpenMode & EFI_FILE_MODE_CREATE) ? Attributes : 0; InsertTailList (&gFileList, &FileFcb->Link); *NewHandle = &FileFcb->File; return EFI_SUCCESS; Error: FreePool (AsciiFileName); return Status; }