/** @file EFI_FILE_PROTOCOL wrappers for other items (Like Environment Variables, StdIn, StdOut, StdErr, etc...). Copyright 2016 Dell Inc. Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
(C) Copyright 2013 Hewlett-Packard Development Company, L.P.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "Shell.h" #include "FileHandleInternal.h" #define MEM_WRITE_REALLOC_OVERHEAD 1024 /** File style interface for console (Open). @param[in] This Ignored. @param[out] NewHandle Ignored. @param[in] FileName Ignored. @param[in] OpenMode Ignored. @param[in] Attributes Ignored. @retval EFI_NOT_FOUND **/ EFI_STATUS EFIAPI FileInterfaceOpenNotFound ( IN EFI_FILE_PROTOCOL *This, OUT EFI_FILE_PROTOCOL **NewHandle, IN CHAR16 *FileName, IN UINT64 OpenMode, IN UINT64 Attributes ) { return (EFI_NOT_FOUND); } /** File style interface for console (Close, Delete, & Flush) @param[in] This Ignored. @retval EFI_SUCCESS **/ EFI_STATUS EFIAPI FileInterfaceNopGeneric ( IN EFI_FILE_PROTOCOL *This ) { return (EFI_SUCCESS); } /** File style interface for console (GetPosition). @param[in] This Ignored. @param[out] Position Ignored. @retval EFI_UNSUPPORTED **/ EFI_STATUS EFIAPI FileInterfaceNopGetPosition ( IN EFI_FILE_PROTOCOL *This, OUT UINT64 *Position ) { return (EFI_UNSUPPORTED); } /** File style interface for console (SetPosition). @param[in] This Ignored. @param[in] Position Ignored. @retval EFI_UNSUPPORTED **/ EFI_STATUS EFIAPI FileInterfaceNopSetPosition ( IN EFI_FILE_PROTOCOL *This, IN UINT64 Position ) { return (EFI_UNSUPPORTED); } /** File style interface for console (GetInfo). @param[in] This Ignored. @param[in] InformationType Ignored. @param[in, out] BufferSize Ignored. @param[out] Buffer Ignored. @retval EFI_UNSUPPORTED **/ EFI_STATUS EFIAPI FileInterfaceNopGetInfo ( IN EFI_FILE_PROTOCOL *This, IN EFI_GUID *InformationType, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { return (EFI_UNSUPPORTED); } /** File style interface for console (SetInfo). @param[in] This Ignored. @param[in] InformationType Ignored. @param[in] BufferSize Ignored. @param[in] Buffer Ignored. @retval EFI_UNSUPPORTED **/ EFI_STATUS EFIAPI FileInterfaceNopSetInfo ( IN EFI_FILE_PROTOCOL *This, IN EFI_GUID *InformationType, IN UINTN BufferSize, IN VOID *Buffer ) { return (EFI_UNSUPPORTED); } /** File style interface for StdOut (Write). Writes data to the screen. @param[in] This The pointer to the EFI_FILE_PROTOCOL object. @param[in, out] BufferSize Size in bytes of Buffer. @param[in] Buffer The pointer to the buffer to write. @retval EFI_UNSUPPORTED No output console is supported. @return A return value from gST->ConOut->OutputString. **/ EFI_STATUS EFIAPI FileInterfaceStdOutWrite ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, IN VOID *Buffer ) { if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) { return (EFI_UNSUPPORTED); } if (*((CHAR16 *)Buffer) == gUnicodeFileTag) { return (gST->ConOut->OutputString (gST->ConOut, (CHAR16 *)Buffer + 1)); } return (gST->ConOut->OutputString (gST->ConOut, Buffer)); } /** File style interface for StdIn (Write). @param[in] This Ignored. @param[in, out] BufferSize Ignored. @param[in] Buffer Ignored. @retval EFI_UNSUPPORTED **/ EFI_STATUS EFIAPI FileInterfaceStdInWrite ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, IN VOID *Buffer ) { return (EFI_UNSUPPORTED); } /** File style interface for console StdErr (Write). Writes error to the error output. @param[in] This The pointer to the EFI_FILE_PROTOCOL object. @param[in, out] BufferSize Size in bytes of Buffer. @param[in] Buffer The pointer to the buffer to write. @return A return value from gST->StdErr->OutputString. **/ EFI_STATUS EFIAPI FileInterfaceStdErrWrite ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, IN VOID *Buffer ) { return (gST->StdErr->OutputString (gST->StdErr, Buffer)); } /** File style interface for console StdOut (Read). @param[in] This Ignored. @param[in, out] BufferSize Ignored. @param[out] Buffer Ignored. @retval EFI_UNSUPPORTED **/ EFI_STATUS EFIAPI FileInterfaceStdOutRead ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { return (EFI_UNSUPPORTED); } /** File style interface for console StdErr (Read). @param[in] This Ignored. @param[in, out] BufferSize Ignored. @param[out] Buffer Ignored. @retval EFI_UNSUPPORTED Always. **/ EFI_STATUS EFIAPI FileInterfaceStdErrRead ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { return (EFI_UNSUPPORTED); } /** File style interface for NUL file (Read). @param[in] This Ignored. @param[in, out] BufferSize Poiner to 0 upon return. @param[out] Buffer Ignored. @retval EFI_SUCCESS Always. **/ EFI_STATUS EFIAPI FileInterfaceNulRead ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { *BufferSize = 0; return (EFI_SUCCESS); } /** File style interface for NUL file (Write). @param[in] This Ignored. @param[in, out] BufferSize Ignored. @param[in] Buffer Ignored. @retval EFI_SUCCESS **/ EFI_STATUS EFIAPI FileInterfaceNulWrite ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, IN VOID *Buffer ) { return (EFI_SUCCESS); } /** Create the TAB completion list. @param[in] InputString The command line to expand. @param[in] StringLen Length of the command line. @param[in] BufferSize Buffer size. @param[in, out] TabCompletionList Return the TAB completion list. @param[in, out] TabUpdatePos Return the TAB update position. **/ EFI_STATUS CreateTabCompletionList ( IN CONST CHAR16 *InputString, IN CONST UINTN StringLen, IN CONST UINTN BufferSize, IN OUT EFI_SHELL_FILE_INFO **TabCompletionList, IN OUT UINTN *TabUpdatePos ) { BOOLEAN InQuotation; UINTN TabPos; UINTN Index; CONST CHAR16 *Cwd; EFI_STATUS Status; CHAR16 *TabStr; EFI_SHELL_FILE_INFO *FileList; EFI_SHELL_FILE_INFO *FileInfo; EFI_SHELL_FILE_INFO *TempFileInfo; // // Allocate buffers // TabStr = AllocateZeroPool (BufferSize); if (TabStr == NULL) { return EFI_OUT_OF_RESOURCES; } // // handle auto complete of file and directory names... // E.g.: cd fs0:\EFI\Bo // ^ ^ // TabPos TabUpdatePos // TabPos = 0; *TabUpdatePos = 0; FileList = NULL; InQuotation = FALSE; for (Index = 0; Index < StringLen; Index++) { switch (InputString[Index]) { case L'\"': InQuotation = (BOOLEAN)(!InQuotation); break; case L' ': if (!InQuotation) { TabPos = Index + 1; *TabUpdatePos = TabPos; } break; case L':': // // handle the case "fs0:" // Update the TabUpdatePos as well. // case L'\\': *TabUpdatePos = Index + 1; break; default: break; } } if (StrStr (InputString + TabPos, L":") == NULL) { // // If file path doesn't contain ":", ... // Cwd = ShellInfoObject.NewEfiShellProtocol->GetCurDir (NULL); if (Cwd != NULL) { if (InputString[TabPos] != L'\\') { // // and it doesn't begin with "\\", it's a path relative to current directory. // TabStr = "\\" // StrnCpyS (TabStr, BufferSize / sizeof (CHAR16), Cwd, (BufferSize) / sizeof (CHAR16) - 1); StrCatS (TabStr, (BufferSize) / sizeof (CHAR16), L"\\"); } else { // // and it begins with "\\", it's a path pointing to root directory of current map. // TabStr = "fsx:" // Index = StrStr (Cwd, L":") - Cwd + 1; StrnCpyS (TabStr, BufferSize / sizeof (CHAR16), Cwd, Index); } } } StrnCatS (TabStr, (BufferSize) / sizeof (CHAR16), InputString + TabPos, StringLen - TabPos); StrnCatS (TabStr, (BufferSize) / sizeof (CHAR16), L"*", (BufferSize) / sizeof (CHAR16) - 1 - StrLen (TabStr)); Status = ShellInfoObject.NewEfiShellProtocol->FindFiles (TabStr, &FileList); // // Filter out the non-directory for "CD" command // Filter "." and ".." for all // if (!EFI_ERROR (Status) && (FileList != NULL)) { // // Skip the spaces in the beginning // while (*InputString == L' ') { InputString++; } for (FileInfo = (EFI_SHELL_FILE_INFO *)GetFirstNode (&FileList->Link); !IsNull (&FileList->Link, &FileInfo->Link); ) { if (((StrCmp (FileInfo->FileName, L".") == 0) || (StrCmp (FileInfo->FileName, L"..") == 0)) || ((((InputString[0] == L'c') || (InputString[0] == L'C')) && ((InputString[1] == L'd') || (InputString[1] == L'D'))) && (ShellIsDirectory (FileInfo->FullName) != EFI_SUCCESS))) { TempFileInfo = FileInfo; FileInfo = (EFI_SHELL_FILE_INFO *)RemoveEntryList (&FileInfo->Link); InternalFreeShellFileInfoNode (TempFileInfo); } else { FileInfo = (EFI_SHELL_FILE_INFO *)GetNextNode (&FileList->Link, &FileInfo->Link); } } } if ((FileList != NULL) && !IsListEmpty (&FileList->Link)) { Status = EFI_SUCCESS; } else { ShellInfoObject.NewEfiShellProtocol->FreeFileList (&FileList); Status = EFI_NOT_FOUND; } FreePool (TabStr); *TabCompletionList = FileList; return Status; } /** File style interface for console (Read). This will return a single line of input from the console. @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file handle to read data from. Not used. @param BufferSize On input, the size of the Buffer. On output, the amount of data returned in Buffer. In both cases, the size is measured in bytes. @param Buffer The buffer into which the data is read. @retval EFI_SUCCESS The data was read. @retval EFI_NO_MEDIA The device has no medium. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_DEVICE_ERROR An attempt was made to read from a deleted file. @retval EFI_DEVICE_ERROR On entry, the current file position is beyond the end of the file. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry. BufferSize has been updated with the size needed to complete the request. @retval EFI_OUT_OF_RESOURCES A memory allocation failed. **/ EFI_STATUS EFIAPI FileInterfaceStdInRead ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { CHAR16 *CurrentString; BOOLEAN Done; UINTN TabUpdatePos; // Start index of the string updated by TAB stroke UINTN Column; // Column of current cursor UINTN Row; // Row of current cursor UINTN StartColumn; // Column at the beginning of the line UINTN Update; // Line index for update UINTN Delete; // Num of chars to delete from console after update UINTN StringLen; // Total length of the line UINTN StringCurPos; // Line index corresponding to the cursor UINTN MaxStr; // Maximum possible line length UINTN TotalColumn; // Num of columns in the console UINTN TotalRow; // Num of rows in the console UINTN SkipLength; UINTN OutputLength; // Length of the update string UINTN TailRow; // Row of end of line UINTN TailColumn; // Column of end of line EFI_INPUT_KEY Key; BUFFER_LIST *LinePos; BUFFER_LIST *NewPos; BOOLEAN InScrolling; EFI_STATUS Status; BOOLEAN InTabScrolling; // Whether in TAB-completion state EFI_SHELL_FILE_INFO *TabCompleteList; EFI_SHELL_FILE_INFO *TabCurrent; UINTN EventIndex; CHAR16 *TabOutputStr; // // If buffer is not large enough to hold a CHAR16, return minimum buffer size // if (*BufferSize < sizeof (CHAR16) * 2) { *BufferSize = sizeof (CHAR16) * 2; return (EFI_BUFFER_TOO_SMALL); } Done = FALSE; CurrentString = Buffer; StringLen = 0; StringCurPos = 0; OutputLength = 0; Update = 0; Delete = 0; LinePos = NewPos = (BUFFER_LIST *)(&ShellInfoObject.ViewingSettings.CommandHistory); InScrolling = FALSE; InTabScrolling = FALSE; Status = EFI_SUCCESS; TabOutputStr = NULL; TabUpdatePos = 0; TabCompleteList = NULL; TabCurrent = NULL; // // Get the screen setting and the current cursor location // Column = StartColumn = gST->ConOut->Mode->CursorColumn; Row = gST->ConOut->Mode->CursorRow; gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &TotalColumn, &TotalRow); // // Limit the line length to the buffer size or the minimum size of the // screen. (The smaller takes effect) // MaxStr = TotalColumn * (TotalRow - 1) - StartColumn; if (MaxStr > *BufferSize / sizeof (CHAR16)) { MaxStr = *BufferSize / sizeof (CHAR16); } ZeroMem (CurrentString, MaxStr * sizeof (CHAR16)); do { // // Read a key // gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex); Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); if (EFI_ERROR (Status)) { if (Status == EFI_NOT_READY) { continue; } ZeroMem (CurrentString, MaxStr * sizeof (CHAR16)); StringLen = 0; break; } // // Press PageUp or PageDown to scroll the history screen up or down. // Press any other key to quit scrolling. // if ((Key.UnicodeChar == 0) && ((Key.ScanCode == SCAN_PAGE_UP) || (Key.ScanCode == SCAN_PAGE_DOWN))) { if (Key.ScanCode == SCAN_PAGE_UP) { ConsoleLoggerDisplayHistory (FALSE, 0, ShellInfoObject.ConsoleInfo); } else if (Key.ScanCode == SCAN_PAGE_DOWN) { ConsoleLoggerDisplayHistory (TRUE, 0, ShellInfoObject.ConsoleInfo); } InScrolling = TRUE; } else { if (InScrolling) { ConsoleLoggerStopHistory (ShellInfoObject.ConsoleInfo); InScrolling = FALSE; } } // // If we are quitting TAB scrolling... // if (InTabScrolling && (Key.UnicodeChar != CHAR_TAB)) { if (TabCompleteList != NULL) { ShellInfoObject.NewEfiShellProtocol->FreeFileList (&TabCompleteList); DEBUG_CODE ( TabCompleteList = NULL; ); } InTabScrolling = FALSE; } switch (Key.UnicodeChar) { case CHAR_CARRIAGE_RETURN: // // All done, print a newline at the end of the string // TailRow = Row + (StringLen - StringCurPos + Column) / TotalColumn; TailColumn = (StringLen - StringCurPos + Column) % TotalColumn; ShellPrintEx ((INT32)TailColumn, (INT32)TailRow, L"%N\n"); Done = TRUE; break; case CHAR_BACKSPACE: if (StringCurPos != 0) { // // If not move back beyond string beginning, move all characters behind // the current position one character forward // StringCurPos--; Update = StringCurPos; Delete = 1; CopyMem (CurrentString + StringCurPos, CurrentString + StringCurPos + 1, sizeof (CHAR16) * (StringLen - StringCurPos)); // // Adjust the current column and row // MoveCursorBackward (TotalColumn, &Column, &Row); } break; case CHAR_TAB: if (!InTabScrolling) { TabCurrent = NULL; // // Initialize a tab complete operation. // Status = CreateTabCompletionList (CurrentString, StringLen, *BufferSize, &TabCompleteList, &TabUpdatePos); if (!EFI_ERROR (Status)) { InTabScrolling = TRUE; } // // We do not set up the replacement. // The next section will do that. // } if (InTabScrolling) { // // We are in a tab complete operation. // set up the next replacement. // ASSERT (TabCompleteList != NULL); if (TabCurrent == NULL) { TabCurrent = (EFI_SHELL_FILE_INFO *)GetFirstNode (&TabCompleteList->Link); } else { TabCurrent = (EFI_SHELL_FILE_INFO *)GetNextNode (&TabCompleteList->Link, &TabCurrent->Link); } // // Skip over the empty list beginning node // if (IsNull (&TabCompleteList->Link, &TabCurrent->Link)) { TabCurrent = (EFI_SHELL_FILE_INFO *)GetNextNode (&TabCompleteList->Link, &TabCurrent->Link); } } break; default: if (Key.UnicodeChar >= ' ') { // // If we are at the buffer's end, drop the key // if ((StringLen == MaxStr - 1) && (ShellInfoObject.ViewingSettings.InsertMode || (StringCurPos == StringLen))) { break; } // // If in insert mode, make space by moving each other character 1 // space higher in the array // if (ShellInfoObject.ViewingSettings.InsertMode) { CopyMem (CurrentString + StringCurPos + 1, CurrentString + StringCurPos, (StringLen - StringCurPos)*sizeof (CurrentString[0])); } CurrentString[StringCurPos] = Key.UnicodeChar; Update = StringCurPos; StringCurPos += 1; OutputLength = 1; } break; case 0: switch (Key.ScanCode) { case SCAN_DELETE: // // Move characters behind current position one character forward // if (StringLen != 0) { Update = StringCurPos; Delete = 1; CopyMem (CurrentString + StringCurPos, CurrentString + StringCurPos + 1, sizeof (CHAR16) * (StringLen - StringCurPos)); } break; case SCAN_UP: // // Prepare to print the previous command // NewPos = (BUFFER_LIST *)GetPreviousNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link); if (IsNull (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link)) { NewPos = (BUFFER_LIST *)GetPreviousNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link); } break; case SCAN_DOWN: // // Prepare to print the next command // NewPos = (BUFFER_LIST *)GetNextNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link); if (NewPos == (BUFFER_LIST *)(&ShellInfoObject.ViewingSettings.CommandHistory)) { NewPos = (BUFFER_LIST *)GetNextNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link); } break; case SCAN_LEFT: // // Adjust current cursor position // if (StringCurPos != 0) { --StringCurPos; MoveCursorBackward (TotalColumn, &Column, &Row); } break; case SCAN_RIGHT: // // Adjust current cursor position // if (StringCurPos < StringLen) { ++StringCurPos; MoveCursorForward (TotalColumn, TotalRow, &Column, &Row); } break; case SCAN_HOME: // // Move current cursor position to the beginning of the command line // Row -= (StringCurPos + StartColumn) / TotalColumn; Column = StartColumn; StringCurPos = 0; break; case SCAN_END: // // Move current cursor position to the end of the command line // TailRow = Row + (StringLen - StringCurPos + Column) / TotalColumn; TailColumn = (StringLen - StringCurPos + Column) % TotalColumn; Row = TailRow; Column = TailColumn; StringCurPos = StringLen; break; case SCAN_ESC: // // Prepare to clear the current command line // CurrentString[0] = 0; Update = 0; Delete = StringLen; Row -= (StringCurPos + StartColumn) / TotalColumn; Column = StartColumn; OutputLength = 0; break; case SCAN_INSERT: // // Toggle the SEnvInsertMode flag // ShellInfoObject.ViewingSettings.InsertMode = (BOOLEAN) !ShellInfoObject.ViewingSettings.InsertMode; break; case SCAN_F7: // // Print command history // PrintCommandHistory (TotalColumn, TotalRow, 4); *CurrentString = CHAR_NULL; Done = TRUE; break; } } if (Done) { break; } // // If we are in auto-complete mode, we are preparing to print // the next file or directory name // if (InTabScrolling) { TabOutputStr = AllocateZeroPool (*BufferSize); if (TabOutputStr == NULL) { Status = EFI_OUT_OF_RESOURCES; } } if (InTabScrolling && (TabOutputStr != NULL)) { // // Adjust the column and row to the start of TAB-completion string. // Column = (StartColumn + TabUpdatePos) % TotalColumn; Row -= (StartColumn + StringCurPos) / TotalColumn - (StartColumn + TabUpdatePos) / TotalColumn; OutputLength = StrLen (TabCurrent->FileName); // // if the output string contains blank space, quotation marks L'\"' // should be added to the output. // if (StrStr (TabCurrent->FileName, L" ") != NULL) { TabOutputStr[0] = L'\"'; CopyMem (TabOutputStr + 1, TabCurrent->FileName, OutputLength * sizeof (CHAR16)); TabOutputStr[OutputLength + 1] = L'\"'; TabOutputStr[OutputLength + 2] = CHAR_NULL; } else { CopyMem (TabOutputStr, TabCurrent->FileName, OutputLength * sizeof (CHAR16)); TabOutputStr[OutputLength] = CHAR_NULL; } OutputLength = StrLen (TabOutputStr) < MaxStr - 1 ? StrLen (TabOutputStr) : MaxStr - 1; CopyMem (CurrentString + TabUpdatePos, TabOutputStr, OutputLength * sizeof (CHAR16)); CurrentString[TabUpdatePos + OutputLength] = CHAR_NULL; StringCurPos = TabUpdatePos + OutputLength; Update = TabUpdatePos; if (StringLen > TabUpdatePos + OutputLength) { Delete = StringLen - TabUpdatePos - OutputLength; } FreePool (TabOutputStr); } // // If we have a new position, we are preparing to print a previous or // next command. // if (NewPos != (BUFFER_LIST *)(&ShellInfoObject.ViewingSettings.CommandHistory)) { Column = StartColumn; Row -= (StringCurPos + StartColumn) / TotalColumn; LinePos = NewPos; NewPos = (BUFFER_LIST *)(&ShellInfoObject.ViewingSettings.CommandHistory); OutputLength = StrLen (LinePos->Buffer) < MaxStr - 1 ? StrLen (LinePos->Buffer) : MaxStr - 1; CopyMem (CurrentString, LinePos->Buffer, OutputLength * sizeof (CHAR16)); CurrentString[OutputLength] = CHAR_NULL; StringCurPos = OutputLength; // // Draw new input string // Update = 0; if (StringLen > OutputLength) { // // If old string was longer, blank its tail // Delete = StringLen - OutputLength; } } // // If we need to update the output do so now // if (Update != (UINTN)-1) { ShellPrintEx ((INT32)Column, (INT32)Row, L"%s%.*s", CurrentString + Update, Delete, L""); StringLen = StrLen (CurrentString); if (Delete != 0) { SetMem (CurrentString + StringLen, Delete * sizeof (CHAR16), CHAR_NULL); } if (StringCurPos > StringLen) { StringCurPos = StringLen; } Update = (UINTN)-1; // // After using print to reflect newly updates, if we're not using // BACKSPACE and DELETE, we need to move the cursor position forward, // so adjust row and column here. // if ((Key.UnicodeChar != CHAR_BACKSPACE) && !((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_DELETE))) { // // Calculate row and column of the tail of current string // TailRow = Row + (StringLen - StringCurPos + Column + OutputLength) / TotalColumn; TailColumn = (StringLen - StringCurPos + Column + OutputLength) % TotalColumn; // // If the tail of string reaches screen end, screen rolls up, so if // Row does not equal TailRow, Row should be decremented // // (if we are recalling commands using UPPER and DOWN key, and if the // old command is too long to fit the screen, TailColumn must be 79. // if ((TailColumn == 0) && (TailRow >= TotalRow) && (Row != TailRow)) { Row--; } // // Calculate the cursor position after current operation. If cursor // reaches line end, update both row and column, otherwise, only // column will be changed. // if (Column + OutputLength >= TotalColumn) { SkipLength = OutputLength - (TotalColumn - Column); Row += SkipLength / TotalColumn + 1; if (Row > TotalRow - 1) { Row = TotalRow - 1; } Column = SkipLength % TotalColumn; } else { Column += OutputLength; } } Delete = 0; } // // Set the cursor position for this key // gST->ConOut->SetCursorPosition (gST->ConOut, Column, Row); } while (!Done); if ((CurrentString != NULL) && (StrLen (CurrentString) > 0)) { // // add the line to the history buffer // AddLineToCommandHistory (CurrentString); } // // Return the data to the caller // *BufferSize = StringLen * sizeof (CHAR16); // // if this was used it should be deallocated by now... // prevent memory leaks... // if (TabCompleteList != NULL) { ShellInfoObject.NewEfiShellProtocol->FreeFileList (&TabCompleteList); } ASSERT (TabCompleteList == NULL); return Status; } // // FILE style interfaces for StdIn/StdOut/StdErr // EFI_FILE_PROTOCOL FileInterfaceStdIn = { EFI_FILE_REVISION, FileInterfaceOpenNotFound, FileInterfaceNopGeneric, FileInterfaceNopGeneric, FileInterfaceStdInRead, FileInterfaceStdInWrite, FileInterfaceNopGetPosition, FileInterfaceNopSetPosition, FileInterfaceNopGetInfo, FileInterfaceNopSetInfo, FileInterfaceNopGeneric }; EFI_FILE_PROTOCOL FileInterfaceStdOut = { EFI_FILE_REVISION, FileInterfaceOpenNotFound, FileInterfaceNopGeneric, FileInterfaceNopGeneric, FileInterfaceStdOutRead, FileInterfaceStdOutWrite, FileInterfaceNopGetPosition, FileInterfaceNopSetPosition, FileInterfaceNopGetInfo, FileInterfaceNopSetInfo, FileInterfaceNopGeneric }; EFI_FILE_PROTOCOL FileInterfaceStdErr = { EFI_FILE_REVISION, FileInterfaceOpenNotFound, FileInterfaceNopGeneric, FileInterfaceNopGeneric, FileInterfaceStdErrRead, FileInterfaceStdErrWrite, FileInterfaceNopGetPosition, FileInterfaceNopSetPosition, FileInterfaceNopGetInfo, FileInterfaceNopSetInfo, FileInterfaceNopGeneric }; EFI_FILE_PROTOCOL FileInterfaceNulFile = { EFI_FILE_REVISION, FileInterfaceOpenNotFound, FileInterfaceNopGeneric, FileInterfaceNopGeneric, FileInterfaceNulRead, FileInterfaceNulWrite, FileInterfaceNopGetPosition, FileInterfaceNopSetPosition, FileInterfaceNopGetInfo, FileInterfaceNopSetInfo, FileInterfaceNopGeneric }; // // This is identical to EFI_FILE_PROTOCOL except for the additional member // for the name. // typedef struct { UINT64 Revision; EFI_FILE_OPEN Open; EFI_FILE_CLOSE Close; EFI_FILE_DELETE Delete; EFI_FILE_READ Read; EFI_FILE_WRITE Write; EFI_FILE_GET_POSITION GetPosition; EFI_FILE_SET_POSITION SetPosition; EFI_FILE_GET_INFO GetInfo; EFI_FILE_SET_INFO SetInfo; EFI_FILE_FLUSH Flush; CHAR16 Name[1]; } EFI_FILE_PROTOCOL_ENVIRONMENT; // ANSI compliance helper to get size of the struct. #define SIZE_OF_EFI_FILE_PROTOCOL_ENVIRONMENT EFI_FIELD_OFFSET (EFI_FILE_PROTOCOL_ENVIRONMENT, Name) /** File style interface for Environment Variable (Close). Frees the memory for this object. @param[in] This The pointer to the EFI_FILE_PROTOCOL object. @retval EFI_SUCCESS **/ EFI_STATUS EFIAPI FileInterfaceEnvClose ( IN EFI_FILE_PROTOCOL *This ) { VOID *NewBuffer; UINTN NewSize; EFI_STATUS Status; BOOLEAN Volatile; UINTN TotalSize; // // Most if not all UEFI commands will have an '\r\n' at the end of any output. // Since the output was redirected to a variable, it does not make sense to // keep this. So, before closing, strip the trailing '\r\n' from the variable // if it exists. // NewBuffer = NULL; NewSize = 0; TotalSize = 0; Status = IsVolatileEnv (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &Volatile); if (EFI_ERROR (Status)) { return Status; } Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer); if (Status == EFI_BUFFER_TOO_SMALL) { TotalSize = NewSize + sizeof (CHAR16); NewBuffer = AllocateZeroPool (TotalSize); if (NewBuffer == NULL) { return EFI_OUT_OF_RESOURCES; } Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer); } if (!EFI_ERROR (Status) && (NewBuffer != NULL)) { if (TotalSize / sizeof (CHAR16) >= 3) { if ((((CHAR16 *)NewBuffer)[TotalSize / sizeof (CHAR16) - 2] == CHAR_LINEFEED) && (((CHAR16 *)NewBuffer)[TotalSize / sizeof (CHAR16) - 3] == CHAR_CARRIAGE_RETURN) ) { ((CHAR16 *)NewBuffer)[TotalSize / sizeof (CHAR16) - 3] = CHAR_NULL; // // If the NewBuffer end with \r\n\0, We will replace '\r' by '\0' and then update TotalSize. // TotalSize -= sizeof (CHAR16) * 2; } if (Volatile) { Status = SHELL_SET_ENVIRONMENT_VARIABLE_V ( ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, TotalSize - sizeof (CHAR16), NewBuffer ); if (!EFI_ERROR (Status)) { Status = ShellAddEnvVarToList ( ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, NewBuffer, TotalSize, EFI_VARIABLE_BOOTSERVICE_ACCESS ); } } else { Status = SHELL_SET_ENVIRONMENT_VARIABLE_NV ( ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, TotalSize - sizeof (CHAR16), NewBuffer ); if (!EFI_ERROR (Status)) { Status = ShellAddEnvVarToList ( ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, NewBuffer, TotalSize, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS ); } } } } SHELL_FREE_NON_NULL (NewBuffer); FreePool ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This); return (Status); } /** File style interface for Environment Variable (Delete). @param[in] This The pointer to the EFI_FILE_PROTOCOL object. @retval The return value from FileInterfaceEnvClose(). **/ EFI_STATUS EFIAPI FileInterfaceEnvDelete ( IN EFI_FILE_PROTOCOL *This ) { SHELL_DELETE_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name); return (FileInterfaceEnvClose (This)); } /** File style interface for Environment Variable (Read). @param[in] This The pointer to the EFI_FILE_PROTOCOL object. @param[in, out] BufferSize Size in bytes of Buffer. @param[out] Buffer The pointer to the buffer to fill. @retval EFI_SUCCESS The data was read. **/ EFI_STATUS EFIAPI FileInterfaceEnvRead ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { EFI_STATUS Status; *BufferSize = *BufferSize / sizeof (CHAR16) * sizeof (CHAR16); if (*BufferSize != 0) { // // Make sure the first unicode character is \xFEFF // *(CHAR16 *)Buffer = gUnicodeFileTag; Buffer = (CHAR16 *)Buffer + 1; *BufferSize -= sizeof (gUnicodeFileTag); } Status = SHELL_GET_ENVIRONMENT_VARIABLE ( ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, BufferSize, Buffer ); if (!EFI_ERROR (Status) || (Status == EFI_BUFFER_TOO_SMALL)) { // // BufferSize is valid and needs update when Status is Success or BufferTooSmall. // *BufferSize += sizeof (gUnicodeFileTag); } return Status; } /** File style interface for Volatile Environment Variable (Write). This function also caches the environment variable into gShellEnvVarList. @param[in] This The pointer to the EFI_FILE_PROTOCOL object. @param[in, out] BufferSize Size in bytes of Buffer. @param[in] Buffer The pointer to the buffer to write. @retval EFI_SUCCESS The data was successfully write to variable. @retval SHELL_OUT_OF_RESOURCES A memory allocation failed. **/ EFI_STATUS EFIAPI FileInterfaceEnvVolWrite ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, IN VOID *Buffer ) { VOID *NewBuffer; UINTN NewSize; EFI_STATUS Status; UINTN TotalSize; NewBuffer = NULL; NewSize = 0; TotalSize = 0; Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer); if (Status == EFI_BUFFER_TOO_SMALL) { TotalSize = NewSize + *BufferSize + sizeof (CHAR16); } else if (Status == EFI_NOT_FOUND) { TotalSize = *BufferSize + sizeof (CHAR16); } else { return Status; } NewBuffer = AllocateZeroPool (TotalSize); if (NewBuffer == NULL) { return EFI_OUT_OF_RESOURCES; } if (Status == EFI_BUFFER_TOO_SMALL) { Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer); } if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { FreePool (NewBuffer); return Status; } CopyMem ((UINT8 *)NewBuffer + NewSize, Buffer, *BufferSize); Status = ShellAddEnvVarToList ( ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, NewBuffer, TotalSize, EFI_VARIABLE_BOOTSERVICE_ACCESS ); if (EFI_ERROR (Status)) { FreePool (NewBuffer); return Status; } Status = SHELL_SET_ENVIRONMENT_VARIABLE_V ( ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, TotalSize - sizeof (CHAR16), NewBuffer ); if (EFI_ERROR (Status)) { ShellRemvoeEnvVarFromList (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name); } FreePool (NewBuffer); return Status; } /** File style interface for Non Volatile Environment Variable (Write). This function also caches the environment variable into gShellEnvVarList. @param[in] This The pointer to the EFI_FILE_PROTOCOL object. @param[in, out] BufferSize Size in bytes of Buffer. @param[in] Buffer The pointer to the buffer to write. @retval EFI_SUCCESS The data was successfully write to variable. @retval SHELL_OUT_OF_RESOURCES A memory allocation failed. **/ EFI_STATUS EFIAPI FileInterfaceEnvNonVolWrite ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, IN VOID *Buffer ) { VOID *NewBuffer; UINTN NewSize; EFI_STATUS Status; UINTN TotalSize; NewBuffer = NULL; NewSize = 0; TotalSize = 0; Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer); if (Status == EFI_BUFFER_TOO_SMALL) { TotalSize = NewSize + *BufferSize + sizeof (CHAR16); } else if (Status == EFI_NOT_FOUND) { TotalSize = *BufferSize + sizeof (CHAR16); } else { return Status; } NewBuffer = AllocateZeroPool (TotalSize); if (NewBuffer == NULL) { return EFI_OUT_OF_RESOURCES; } if (Status == EFI_BUFFER_TOO_SMALL) { Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer); } if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { FreePool (NewBuffer); return Status; } CopyMem ((UINT8 *)NewBuffer + NewSize, Buffer, *BufferSize); Status = ShellAddEnvVarToList ( ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, NewBuffer, TotalSize, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS ); if (EFI_ERROR (Status)) { FreePool (NewBuffer); return Status; } Status = SHELL_SET_ENVIRONMENT_VARIABLE_NV ( ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, TotalSize - sizeof (CHAR16), NewBuffer ); if (EFI_ERROR (Status)) { ShellRemvoeEnvVarFromList (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name); } FreePool (NewBuffer); return Status; } /** Creates a EFI_FILE_PROTOCOL (almost) object for using to access environment variables through file operations. @param EnvName The name of the Environment Variable to be operated on. @retval NULL Memory could not be allocated. @return other a pointer to an EFI_FILE_PROTOCOL structure **/ EFI_FILE_PROTOCOL * CreateFileInterfaceEnv ( IN CONST CHAR16 *EnvName ) { EFI_STATUS Status; EFI_FILE_PROTOCOL_ENVIRONMENT *EnvFileInterface; UINTN EnvNameSize; BOOLEAN Volatile; if (EnvName == NULL) { return (NULL); } Status = IsVolatileEnv (EnvName, &Volatile); if (EFI_ERROR (Status)) { return NULL; } // // Get some memory // EnvNameSize = StrSize (EnvName); EnvFileInterface = AllocateZeroPool (sizeof (EFI_FILE_PROTOCOL_ENVIRONMENT)+EnvNameSize); if (EnvFileInterface == NULL) { return (NULL); } // // Assign the generic members // EnvFileInterface->Revision = EFI_FILE_REVISION; EnvFileInterface->Open = FileInterfaceOpenNotFound; EnvFileInterface->Close = FileInterfaceEnvClose; EnvFileInterface->GetPosition = FileInterfaceNopGetPosition; EnvFileInterface->SetPosition = FileInterfaceNopSetPosition; EnvFileInterface->GetInfo = FileInterfaceNopGetInfo; EnvFileInterface->SetInfo = FileInterfaceNopSetInfo; EnvFileInterface->Flush = FileInterfaceNopGeneric; EnvFileInterface->Delete = FileInterfaceEnvDelete; EnvFileInterface->Read = FileInterfaceEnvRead; CopyMem (EnvFileInterface->Name, EnvName, EnvNameSize); // // Assign the different members for Volatile and Non-Volatile variables // if (Volatile) { EnvFileInterface->Write = FileInterfaceEnvVolWrite; } else { EnvFileInterface->Write = FileInterfaceEnvNonVolWrite; } return ((EFI_FILE_PROTOCOL *)EnvFileInterface); } /** Move the cursor position one character backward. @param[in] LineLength Length of a line. Get it by calling QueryMode @param[in, out] Column Current column of the cursor position @param[in, out] Row Current row of the cursor position **/ VOID MoveCursorBackward ( IN UINTN LineLength, IN OUT UINTN *Column, IN OUT UINTN *Row ) { // // If current column is 0, move to the last column of the previous line, // otherwise, just decrement column. // if (*Column == 0) { *Column = LineLength - 1; if (*Row > 0) { (*Row)--; } return; } (*Column)--; } /** Move the cursor position one character forward. @param[in] LineLength Length of a line. @param[in] TotalRow Total row of a screen @param[in, out] Column Current column of the cursor position @param[in, out] Row Current row of the cursor position **/ VOID MoveCursorForward ( IN UINTN LineLength, IN UINTN TotalRow, IN OUT UINTN *Column, IN OUT UINTN *Row ) { // // Increment Column. // If this puts column past the end of the line, move to first column // of the next row. // (*Column)++; if (*Column >= LineLength) { (*Column) = 0; if ((*Row) < TotalRow - 1) { (*Row)++; } } } /** Prints out each previously typed command in the command list history log. When each screen is full it will pause for a key before continuing. @param[in] TotalCols How many columns are on the screen @param[in] TotalRows How many rows are on the screen @param[in] StartColumn which column to start at **/ VOID PrintCommandHistory ( IN CONST UINTN TotalCols, IN CONST UINTN TotalRows, IN CONST UINTN StartColumn ) { BUFFER_LIST *Node; UINTN Index; UINTN LineNumber; UINTN LineCount; ShellPrintEx (-1, -1, L"\n"); Index = 0; LineNumber = 0; // // go through history list... // for ( Node = (BUFFER_LIST *)GetFirstNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link) ; !IsNull (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link) ; Node = (BUFFER_LIST *)GetNextNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link) ) { Index++; LineCount = ((StrLen (Node->Buffer) + StartColumn + 1) / TotalCols) + 1; if (LineNumber + LineCount >= TotalRows) { ShellPromptForResponseHii ( ShellPromptResponseTypeEnterContinue, STRING_TOKEN (STR_SHELL_ENTER_TO_CONT), ShellInfoObject.HiiHandle, NULL ); LineNumber = 0; } ShellPrintEx (-1, -1, L"%2d. %s\n", Index, Node->Buffer); LineNumber += LineCount; } } // // This is identical to EFI_FILE_PROTOCOL except for the additional members // for the buffer, size, and position. // typedef struct { UINT64 Revision; EFI_FILE_OPEN Open; EFI_FILE_CLOSE Close; EFI_FILE_DELETE Delete; EFI_FILE_READ Read; EFI_FILE_WRITE Write; EFI_FILE_GET_POSITION GetPosition; EFI_FILE_SET_POSITION SetPosition; EFI_FILE_GET_INFO GetInfo; EFI_FILE_SET_INFO SetInfo; EFI_FILE_FLUSH Flush; VOID *Buffer; UINT64 Position; UINT64 BufferSize; BOOLEAN Unicode; UINT64 FileSize; } EFI_FILE_PROTOCOL_MEM; /** File style interface for Mem (SetPosition). @param[in] This The pointer to the EFI_FILE_PROTOCOL object. @param[out] Position The position to set. @retval EFI_SUCCESS The position was successfully changed. @retval EFI_INVALID_PARAMETER The Position was invalid. **/ EFI_STATUS EFIAPI FileInterfaceMemSetPosition ( IN EFI_FILE_PROTOCOL *This, OUT UINT64 Position ) { if (Position <= ((EFI_FILE_PROTOCOL_MEM *)This)->FileSize) { ((EFI_FILE_PROTOCOL_MEM *)This)->Position = Position; return (EFI_SUCCESS); } else { return (EFI_INVALID_PARAMETER); } } /** File style interface for Mem (GetPosition). @param[in] This The pointer to the EFI_FILE_PROTOCOL object. @param[out] Position The pointer to the position. @retval EFI_SUCCESS The position was retrieved. **/ EFI_STATUS EFIAPI FileInterfaceMemGetPosition ( IN EFI_FILE_PROTOCOL *This, OUT UINT64 *Position ) { *Position = ((EFI_FILE_PROTOCOL_MEM *)This)->Position; return (EFI_SUCCESS); } /** File style interface for Mem (GetInfo). @param This Protocol instance pointer. @param InformationType Type of information to return in Buffer. @param BufferSize On input size of buffer, on output amount of data in buffer. @param Buffer The buffer to return data. @retval EFI_SUCCESS Data was returned. @retval EFI_UNSUPPORT InformationType is not supported. @retval EFI_NO_MEDIA The device has no media. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. @retval EFI_WRITE_PROTECTED The device is write protected. @retval EFI_ACCESS_DENIED The file was open for read only. @retval EFI_BUFFER_TOO_SMALL Buffer was too small; required size returned in BufferSize. **/ EFI_STATUS EFIAPI FileInterfaceMemGetInfo ( IN EFI_FILE_PROTOCOL *This, IN EFI_GUID *InformationType, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { EFI_FILE_INFO *FileInfo; if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { if (*BufferSize < sizeof (EFI_FILE_INFO)) { *BufferSize = sizeof (EFI_FILE_INFO); return EFI_BUFFER_TOO_SMALL; } if (Buffer == NULL) { return EFI_INVALID_PARAMETER; } FileInfo = (EFI_FILE_INFO *)Buffer; FileInfo->Size = sizeof (*FileInfo); ZeroMem (FileInfo, sizeof (*FileInfo)); FileInfo->FileSize = ((EFI_FILE_PROTOCOL_MEM *)This)->FileSize; FileInfo->PhysicalSize = FileInfo->FileSize; return EFI_SUCCESS; } return EFI_UNSUPPORTED; } /** File style interface for Mem (Write). @param[in] This The pointer to the EFI_FILE_PROTOCOL object. @param[in, out] BufferSize Size in bytes of Buffer. @param[in] Buffer The pointer to the buffer to write. @retval EFI_OUT_OF_RESOURCES The operation failed due to lack of resources. @retval EFI_SUCCESS The data was written. **/ EFI_STATUS EFIAPI FileInterfaceMemWrite ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, IN VOID *Buffer ) { CHAR8 *AsciiBuffer; EFI_FILE_PROTOCOL_MEM *MemFile; MemFile = (EFI_FILE_PROTOCOL_MEM *)This; if (MemFile->Unicode) { // // Unicode // if ((UINTN)(MemFile->Position + (*BufferSize)) > (UINTN)(MemFile->BufferSize)) { MemFile->Buffer = ReallocatePool ((UINTN)(MemFile->BufferSize), (UINTN)(MemFile->BufferSize) + (*BufferSize) + MEM_WRITE_REALLOC_OVERHEAD, MemFile->Buffer); if (MemFile->Buffer == NULL) { return EFI_OUT_OF_RESOURCES; } MemFile->BufferSize += (*BufferSize) + MEM_WRITE_REALLOC_OVERHEAD; } CopyMem (((UINT8 *)MemFile->Buffer) + MemFile->Position, Buffer, *BufferSize); MemFile->Position += (*BufferSize); MemFile->FileSize = MemFile->Position; return (EFI_SUCCESS); } else { // // Ascii // AsciiBuffer = AllocateZeroPool (*BufferSize); if (AsciiBuffer == NULL) { return (EFI_OUT_OF_RESOURCES); } AsciiSPrint (AsciiBuffer, *BufferSize, "%S", Buffer); if ((UINTN)(MemFile->Position + AsciiStrSize (AsciiBuffer)) > (UINTN)(MemFile->BufferSize)) { MemFile->Buffer = ReallocatePool ((UINTN)(MemFile->BufferSize), (UINTN)(MemFile->BufferSize) + AsciiStrSize (AsciiBuffer) + MEM_WRITE_REALLOC_OVERHEAD, MemFile->Buffer); if (MemFile->Buffer == NULL) { FreePool (AsciiBuffer); return EFI_OUT_OF_RESOURCES; } MemFile->BufferSize += AsciiStrSize (AsciiBuffer) + MEM_WRITE_REALLOC_OVERHEAD; } CopyMem (((UINT8 *)MemFile->Buffer) + MemFile->Position, AsciiBuffer, AsciiStrSize (AsciiBuffer)); MemFile->Position += (*BufferSize / sizeof (CHAR16)); MemFile->FileSize = MemFile->Position; FreePool (AsciiBuffer); return (EFI_SUCCESS); } } /** File style interface for Mem (Read). @param[in] This The pointer to the EFI_FILE_PROTOCOL object. @param[in, out] BufferSize Size in bytes of Buffer. @param[in] Buffer The pointer to the buffer to fill. @retval EFI_SUCCESS The data was read. **/ EFI_STATUS EFIAPI FileInterfaceMemRead ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, IN VOID *Buffer ) { EFI_FILE_PROTOCOL_MEM *MemFile; MemFile = (EFI_FILE_PROTOCOL_MEM *)This; if (*BufferSize > (UINTN)((MemFile->FileSize) - (UINTN)(MemFile->Position))) { (*BufferSize) = (UINTN)((MemFile->FileSize) - (UINTN)(MemFile->Position)); } CopyMem (Buffer, ((UINT8 *)MemFile->Buffer) + MemFile->Position, (*BufferSize)); MemFile->Position = MemFile->Position + (*BufferSize); return (EFI_SUCCESS); } /** File style interface for Mem (Close). Frees all memory associated with this object. @param[in] This The pointer to the EFI_FILE_PROTOCOL object. @retval EFI_SUCCESS The 'file' was closed. **/ EFI_STATUS EFIAPI FileInterfaceMemClose ( IN EFI_FILE_PROTOCOL *This ) { SHELL_FREE_NON_NULL (((EFI_FILE_PROTOCOL_MEM *)This)->Buffer); SHELL_FREE_NON_NULL (This); return (EFI_SUCCESS); } /** Creates a EFI_FILE_PROTOCOL (almost) object for using to access a file entirely in memory through file operations. @param[in] Unicode Boolean value with TRUE for Unicode and FALSE for Ascii. @retval NULL Memory could not be allocated. @return other A pointer to an EFI_FILE_PROTOCOL structure. **/ EFI_FILE_PROTOCOL * CreateFileInterfaceMem ( IN CONST BOOLEAN Unicode ) { EFI_FILE_PROTOCOL_MEM *FileInterface; // // Get some memory // FileInterface = AllocateZeroPool (sizeof (EFI_FILE_PROTOCOL_MEM)); if (FileInterface == NULL) { return (NULL); } // // Assign the generic members // FileInterface->Revision = EFI_FILE_REVISION; FileInterface->Open = FileInterfaceOpenNotFound; FileInterface->Close = FileInterfaceMemClose; FileInterface->GetPosition = FileInterfaceMemGetPosition; FileInterface->SetPosition = FileInterfaceMemSetPosition; FileInterface->GetInfo = FileInterfaceMemGetInfo; FileInterface->SetInfo = FileInterfaceNopSetInfo; FileInterface->Flush = FileInterfaceNopGeneric; FileInterface->Delete = FileInterfaceNopGeneric; FileInterface->Read = FileInterfaceMemRead; FileInterface->Write = FileInterfaceMemWrite; FileInterface->Unicode = Unicode; ASSERT (FileInterface->Buffer == NULL); ASSERT (FileInterface->BufferSize == 0); ASSERT (FileInterface->Position == 0); if (Unicode) { FileInterface->Buffer = AllocateZeroPool (sizeof (gUnicodeFileTag)); if (FileInterface->Buffer == NULL) { FreePool (FileInterface); return NULL; } *((CHAR16 *)(FileInterface->Buffer)) = EFI_UNICODE_BYTE_ORDER_MARK; FileInterface->BufferSize = 2; FileInterface->Position = 2; } return ((EFI_FILE_PROTOCOL *)FileInterface); } typedef struct { UINT64 Revision; EFI_FILE_OPEN Open; EFI_FILE_CLOSE Close; EFI_FILE_DELETE Delete; EFI_FILE_READ Read; EFI_FILE_WRITE Write; EFI_FILE_GET_POSITION GetPosition; EFI_FILE_SET_POSITION SetPosition; EFI_FILE_GET_INFO GetInfo; EFI_FILE_SET_INFO SetInfo; EFI_FILE_FLUSH Flush; BOOLEAN Unicode; EFI_FILE_PROTOCOL *Orig; } EFI_FILE_PROTOCOL_FILE; /** Set a files current position @param This Protocol instance pointer. @param Position Byte position from the start of the file. @retval EFI_SUCCESS Data was written. @retval EFI_UNSUPPORTED Seek request for non-zero is not valid on open. **/ EFI_STATUS EFIAPI FileInterfaceFileSetPosition ( IN EFI_FILE_PROTOCOL *This, IN UINT64 Position ) { return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->SetPosition (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, Position); } /** Get a file's current position @param This Protocol instance pointer. @param Position Byte position from the start of the file. @retval EFI_SUCCESS Data was written. @retval EFI_UNSUPPORTED Seek request for non-zero is not valid on open.. **/ EFI_STATUS EFIAPI FileInterfaceFileGetPosition ( IN EFI_FILE_PROTOCOL *This, OUT UINT64 *Position ) { return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->GetPosition (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, Position); } /** Get information about a file. @param This Protocol instance pointer. @param InformationType Type of information to return in Buffer. @param BufferSize On input size of buffer, on output amount of data in buffer. @param Buffer The buffer to return data. @retval EFI_SUCCESS Data was returned. @retval EFI_UNSUPPORT InformationType is not supported. @retval EFI_NO_MEDIA The device has no media. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. @retval EFI_WRITE_PROTECTED The device is write protected. @retval EFI_ACCESS_DENIED The file was open for read only. @retval EFI_BUFFER_TOO_SMALL Buffer was too small; required size returned in BufferSize. **/ EFI_STATUS EFIAPI FileInterfaceFileGetInfo ( IN EFI_FILE_PROTOCOL *This, IN EFI_GUID *InformationType, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->GetInfo (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, InformationType, BufferSize, Buffer); } /** Set information about a file @param This Protocol instance pointer. @param InformationType Type of information in Buffer. @param BufferSize Size of buffer. @param Buffer The data to write. @retval EFI_SUCCESS Data was returned. @retval EFI_UNSUPPORT InformationType is not supported. @retval EFI_NO_MEDIA The device has no media. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. @retval EFI_WRITE_PROTECTED The device is write protected. @retval EFI_ACCESS_DENIED The file was open for read only. **/ EFI_STATUS EFIAPI FileInterfaceFileSetInfo ( IN EFI_FILE_PROTOCOL *This, IN EFI_GUID *InformationType, IN UINTN BufferSize, IN VOID *Buffer ) { return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->SetInfo (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, InformationType, BufferSize, Buffer); } /** Flush data back for the file handle. @param This Protocol instance pointer. @retval EFI_SUCCESS Data was written. @retval EFI_UNSUPPORT Writes to Open directory are not supported. @retval EFI_NO_MEDIA The device has no media. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. @retval EFI_WRITE_PROTECTED The device is write protected. @retval EFI_ACCESS_DENIED The file was open for read only. @retval EFI_VOLUME_FULL The volume is full. **/ EFI_STATUS EFIAPI FileInterfaceFileFlush ( IN EFI_FILE_PROTOCOL *This ) { return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Flush (((EFI_FILE_PROTOCOL_FILE *)This)->Orig); } /** Read data from the file. @param This Protocol instance pointer. @param BufferSize On input size of buffer, on output amount of data in buffer. @param Buffer The buffer in which data is read. @retval EFI_SUCCESS Data was read. @retval EFI_NO_MEDIA The device has no media. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. @retval EFI_BUFFER_TO_SMALL BufferSize is too small. BufferSize contains required size. **/ EFI_STATUS EFIAPI FileInterfaceFileRead ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { EFI_STATUS Status; UINT64 Position; CHAR8 *AsciiStrBuffer; CHAR16 *UscStrBuffer; UINTN Size; if (((EFI_FILE_PROTOCOL_FILE *)This)->Unicode) { // // Unicode // There might be different file tag for the Unicode file. We cannot unconditionally insert the \xFEFF. // So we choose to leave the file content as is. // return (((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Read (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, BufferSize, Buffer)); } else { // // Ascii // *BufferSize = *BufferSize / sizeof (CHAR16) * sizeof (CHAR16); if (*BufferSize == 0) { return EFI_SUCCESS; } Status = ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->GetPosition (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, &Position); if (EFI_ERROR (Status)) { return Status; } if (Position == 0) { // // First two bytes in Buffer is for the Unicode file tag. // *(CHAR16 *)Buffer = gUnicodeFileTag; Buffer = (CHAR16 *)Buffer + 1; Size = *BufferSize / sizeof (CHAR16) - 1; } else { Size = *BufferSize / sizeof (CHAR16); } AsciiStrBuffer = AllocateZeroPool (Size + 1); if (AsciiStrBuffer == NULL) { return EFI_OUT_OF_RESOURCES; } UscStrBuffer = AllocateZeroPool ((Size + 1) * sizeof (CHAR16)); if (UscStrBuffer == NULL) { SHELL_FREE_NON_NULL (AsciiStrBuffer); return EFI_OUT_OF_RESOURCES; } Status = ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Read (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, &Size, AsciiStrBuffer); if (!EFI_ERROR (Status)) { AsciiStrToUnicodeStrS (AsciiStrBuffer, UscStrBuffer, Size + 1); *BufferSize = Size * sizeof (CHAR16); CopyMem (Buffer, UscStrBuffer, *BufferSize); } SHELL_FREE_NON_NULL (AsciiStrBuffer); SHELL_FREE_NON_NULL (UscStrBuffer); return Status; } } /** Opens a new file relative to the source file's location. @param[in] This The protocol instance pointer. @param[out] NewHandle Returns File Handle for FileName. @param[in] FileName Null terminated string. "\", ".", and ".." are supported. @param[in] OpenMode Open mode for file. @param[in] Attributes Only used for EFI_FILE_MODE_CREATE. @retval EFI_SUCCESS The device was opened. @retval EFI_NOT_FOUND The specified file could not be found on the device. @retval EFI_NO_MEDIA The device has no media. @retval EFI_MEDIA_CHANGED The media has changed. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. @retval EFI_ACCESS_DENIED The service denied access to the file. @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of resources. @retval EFI_VOLUME_FULL The volume is full. **/ EFI_STATUS EFIAPI FileInterfaceFileOpen ( IN EFI_FILE_PROTOCOL *This, OUT EFI_FILE_PROTOCOL **NewHandle, IN CHAR16 *FileName, IN UINT64 OpenMode, IN UINT64 Attributes ) { return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Open (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, NewHandle, FileName, OpenMode, Attributes); } /** Close and delete the file handle. @param This Protocol instance pointer. @retval EFI_SUCCESS The device was opened. @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not deleted. **/ EFI_STATUS EFIAPI FileInterfaceFileDelete ( IN EFI_FILE_PROTOCOL *This ) { EFI_STATUS Status; Status = ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Delete (((EFI_FILE_PROTOCOL_FILE *)This)->Orig); FreePool (This); return (Status); } /** File style interface for File (Close). @param[in] This The pointer to the EFI_FILE_PROTOCOL object. @retval EFI_SUCCESS The file was closed. **/ EFI_STATUS EFIAPI FileInterfaceFileClose ( IN EFI_FILE_PROTOCOL *This ) { EFI_STATUS Status; Status = ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Close (((EFI_FILE_PROTOCOL_FILE *)This)->Orig); FreePool (This); return (Status); } /** File style interface for File (Write). If the file was opened with ASCII mode the data will be processed through AsciiSPrint before writing. @param[in] This The pointer to the EFI_FILE_PROTOCOL object. @param[in, out] BufferSize Size in bytes of Buffer. @param[in] Buffer The pointer to the buffer to write. @retval EFI_SUCCESS The data was written. **/ EFI_STATUS EFIAPI FileInterfaceFileWrite ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, IN VOID *Buffer ) { CHAR8 *AsciiBuffer; UINTN Size; EFI_STATUS Status; if (((EFI_FILE_PROTOCOL_FILE *)This)->Unicode) { // // Unicode // return (((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Write (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, BufferSize, Buffer)); } else { // // Ascii // AsciiBuffer = AllocateZeroPool (*BufferSize); AsciiSPrint (AsciiBuffer, *BufferSize, "%S", Buffer); Size = AsciiStrSize (AsciiBuffer) - 1; // (we dont need the null terminator) Status = (((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Write (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, &Size, AsciiBuffer)); FreePool (AsciiBuffer); return (Status); } } /** Create a file interface with unicode information. This will create a new EFI_FILE_PROTOCOL identical to the Templace except that the new one has Unicode and Ascii knowledge. @param[in] Template A pointer to the EFI_FILE_PROTOCOL object. @param[in] Unicode TRUE for UCS-2, FALSE for ASCII. @return a new EFI_FILE_PROTOCOL object to be used instead of the template. **/ EFI_FILE_PROTOCOL * CreateFileInterfaceFile ( IN CONST EFI_FILE_PROTOCOL *Template, IN CONST BOOLEAN Unicode ) { EFI_FILE_PROTOCOL_FILE *NewOne; NewOne = AllocateZeroPool (sizeof (EFI_FILE_PROTOCOL_FILE)); if (NewOne == NULL) { return (NULL); } CopyMem (NewOne, Template, sizeof (EFI_FILE_PROTOCOL_FILE)); NewOne->Orig = (EFI_FILE_PROTOCOL *)Template; NewOne->Unicode = Unicode; NewOne->Open = FileInterfaceFileOpen; NewOne->Close = FileInterfaceFileClose; NewOne->Delete = FileInterfaceFileDelete; NewOne->Read = FileInterfaceFileRead; NewOne->Write = FileInterfaceFileWrite; NewOne->GetPosition = FileInterfaceFileGetPosition; NewOne->SetPosition = FileInterfaceFileSetPosition; NewOne->GetInfo = FileInterfaceFileGetInfo; NewOne->SetInfo = FileInterfaceFileSetInfo; NewOne->Flush = FileInterfaceFileFlush; return ((EFI_FILE_PROTOCOL *)NewOne); }