+/** @file\r
+ Abstract device driver for the UEFI Shell-hosted environment.\r
+\r
+ In a Shell-hosted environment, this is the driver that is called\r
+ when no other driver matches.\r
+\r
+ Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
+ This program and the accompanying materials are licensed and made available under\r
+ the terms and conditions of the BSD License that accompanies this distribution.\r
+ The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+#include <Uefi.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/ShellLib.h>\r
+\r
+#include <LibConfig.h>\r
+#include <sys/EfiSysCall.h>\r
+\r
+#include <errno.h>\r
+#include <string.h>\r
+#include <stdlib.h>\r
+#include <wctype.h>\r
+#include <wchar.h>\r
+#include <sys/fcntl.h>\r
+#include <kfile.h>\r
+#include <Device/Device.h>\r
+#include <MainData.h>\r
+#include <Efi/SysEfi.h>\r
+\r
+static\r
+int\r
+EFIAPI\r
+da_ShellClose(\r
+ IN struct __filedes *Fp\r
+)\r
+{\r
+ EFIerrno = ShellCloseFile( (SHELL_FILE_HANDLE *)&Fp->devdata);\r
+ if(RETURN_ERROR(EFIerrno)) {\r
+ return -1;\r
+ }\r
+ return 0;\r
+}\r
+\r
+static\r
+int\r
+EFIAPI\r
+da_ShellDelete(\r
+ struct __filedes *filp\r
+ )\r
+{\r
+ RETURN_STATUS Status;\r
+\r
+ Status = ShellDeleteFile( (SHELL_FILE_HANDLE *)&filp->devdata);\r
+ if(Status != RETURN_SUCCESS) {\r
+ errno = EFI2errno(Status);\r
+ EFIerrno = Status;\r
+ return -1;\r
+ }\r
+ return 0;\r
+}\r
+\r
+static\r
+off_t\r
+EFIAPI\r
+da_ShellSeek(\r
+ struct __filedes *filp,\r
+ off_t offset,\r
+ int whence\r
+)\r
+{\r
+ __off_t CurPos = -1;\r
+ RETURN_STATUS Status = RETURN_SUCCESS;\r
+ SHELL_FILE_HANDLE FileHandle;\r
+\r
+ FileHandle = (SHELL_FILE_HANDLE)filp->devdata;\r
+\r
+ if(whence != SEEK_SET) {\r
+ // We are doing a relative seek\r
+ if(whence == SEEK_END) {\r
+ // seeking relative to EOF, so position there first.\r
+ Status = ShellSetFilePosition( FileHandle, 0xFFFFFFFFFFFFFFFFULL);\r
+ }\r
+ if(Status == RETURN_SUCCESS) {\r
+ // Now, determine our current position.\r
+ Status = ShellGetFilePosition( FileHandle, (UINT64 *)&CurPos);\r
+ }\r
+ }\r
+ else {\r
+ CurPos = 0; // offset is an absolute position for SEEK_SET\r
+ if(offset < 0) {\r
+ Status = RETURN_INVALID_PARAMETER;\r
+ }\r
+ }\r
+ if(Status == RETURN_SUCCESS) {\r
+ /* CurPos now indicates the point we are seeking from, so seek... */\r
+ Status = ShellSetFilePosition( FileHandle, (UINT64)(CurPos + offset));\r
+ if(Status == RETURN_SUCCESS) {\r
+ // Now, determine our final position.\r
+ Status = ShellGetFilePosition( FileHandle, (UINT64 *)&CurPos);\r
+ }\r
+ }\r
+ if(Status != RETURN_SUCCESS) {\r
+ if(Status == EFI_UNSUPPORTED) {\r
+ errno = EISDIR;\r
+ }\r
+ else {\r
+ errno = EFI2errno(Status);\r
+ }\r
+ EFIerrno = Status;\r
+ CurPos = EOF;\r
+ }\r
+ return CurPos;\r
+}\r
+\r
+/** The directory path is created with the access permissions specified by\r
+ perms.\r
+\r
+ The directory is closed after it is created.\r
+\r
+ @retval 0 The directory was created successfully.\r
+ @retval -1 An error occurred and an error code is stored in errno.\r
+**/\r
+static\r
+int\r
+EFIAPI\r
+da_ShellMkdir(\r
+ const char *path,\r
+ __mode_t perms\r
+ )\r
+{\r
+ SHELL_FILE_HANDLE FileHandle;\r
+ RETURN_STATUS Status;\r
+ EFI_FILE_INFO *FileInfo;\r
+ wchar_t *NewPath;\r
+ int retval = -1;\r
+\r
+ // Convert name from MBCS to WCS and change '/' to '\\'\r
+ NewPath = NormalizePath( path);\r
+\r
+ if(NewPath != NULL) {\r
+ Status = ShellCreateDirectory( NewPath, &FileHandle);\r
+ if(Status == RETURN_SUCCESS) {\r
+ FileInfo = ShellGetFileInfo( FileHandle);\r
+ Status = RETURN_ABORTED; // In case ShellGetFileInfo() failed\r
+ if(FileInfo != NULL) {\r
+ FileInfo->Attribute = Omode2EFI(perms);\r
+ Status = ShellSetFileInfo( FileHandle, FileInfo);\r
+ FreePool(FileInfo);\r
+ if(Status == RETURN_SUCCESS) {\r
+ (void)ShellCloseFile(&FileHandle);\r
+ retval = 0;\r
+ }\r
+ }\r
+ }\r
+ errno = EFI2errno(Status);\r
+ EFIerrno = Status;\r
+ free(NewPath);\r
+ }\r
+ return retval;\r
+}\r
+\r
+static\r
+ssize_t\r
+EFIAPI\r
+da_ShellRead(\r
+ IN OUT struct __filedes *filp,\r
+ IN OUT off_t *offset,\r
+ IN size_t BufferSize,\r
+ OUT VOID *Buffer\r
+)\r
+{\r
+ ssize_t BufSize;\r
+ SHELL_FILE_HANDLE FileHandle;\r
+ RETURN_STATUS Status;\r
+\r
+ if(offset != NULL) {\r
+ BufSize = (ssize_t)da_ShellSeek(filp, *offset, SEEK_SET);\r
+ if(BufSize >= 0) {\r
+ filp->f_offset = BufSize;\r
+ }\r
+ }\r
+\r
+ BufSize = (ssize_t)BufferSize;\r
+ FileHandle = (SHELL_FILE_HANDLE)filp->devdata;\r
+\r
+ Status = ShellReadFile( FileHandle, (UINTN *)&BufSize, Buffer);\r
+ if(Status != RETURN_SUCCESS) {\r
+ EFIerrno = Status;\r
+ errno = EFI2errno(Status);\r
+ if(Status == RETURN_BUFFER_TOO_SMALL) {\r
+ BufSize = -BufSize;\r
+ }\r
+ else {\r
+ BufSize = -1;\r
+ }\r
+ }\r
+ else {\r
+ filp->f_offset += BufSize; // Advance to where we want to read next.\r
+ }\r
+ return BufSize;\r
+}\r
+\r
+static\r
+ssize_t\r
+EFIAPI\r
+da_ShellWrite(\r
+ IN struct __filedes *filp,\r
+ IN off_t *offset,\r
+ IN size_t BufferSize,\r
+ IN const void *Buffer\r
+ )\r
+{\r
+ ssize_t BufSize;\r
+ SHELL_FILE_HANDLE FileHandle;\r
+ RETURN_STATUS Status;\r
+ off_t Position = 0;\r
+ int How = SEEK_SET;\r
+\r
+\r
+ if((offset != NULL) || (filp->Oflags & O_APPEND)) {\r
+ if(filp->Oflags & O_APPEND) {\r
+ Position = 0;\r
+ How = SEEK_END;\r
+ }\r
+ else {\r
+ Position = *offset;\r
+ How = SEEK_SET;\r
+ }\r
+ BufSize = (ssize_t)da_ShellSeek(filp, Position, How);\r
+ if(BufSize >= 0) {\r
+ filp->f_offset = BufSize;\r
+ }\r
+ }\r
+\r
+ BufSize = (ssize_t)BufferSize;\r
+ FileHandle = (SHELL_FILE_HANDLE)filp->devdata;\r
+\r
+ Status = ShellWriteFile( FileHandle, (UINTN *)&BufSize, (void *)Buffer);\r
+\r
+ if(Status != RETURN_SUCCESS) {\r
+ EFIerrno = Status;\r
+ errno = EFI2errno(Status);\r
+ if(Status == EFI_UNSUPPORTED) {\r
+ errno = EISDIR;\r
+ }\r
+ BufSize = -1;\r
+ }\r
+ else {\r
+ filp->f_offset += BufSize; // Advance to where we want to write next.\r
+ }\r
+\r
+ return BufSize;\r
+}\r
+\r
+static\r
+int\r
+EFIAPI\r
+da_ShellStat(\r
+ struct __filedes *filp,\r
+ struct stat *statbuf,\r
+ void *Something\r
+ )\r
+{\r
+ SHELL_FILE_HANDLE FileHandle;\r
+ EFI_FILE_INFO *FileInfo = NULL;\r
+ UINT64 Attributes;\r
+ RETURN_STATUS Status;\r
+ mode_t newmode;\r
+\r
+ FileHandle = (SHELL_FILE_HANDLE)filp->devdata;\r
+\r
+ FileInfo = ShellGetFileInfo( FileHandle);\r
+\r
+ if(FileInfo != NULL) {\r
+ // Got the info, now populate statbuf with it\r
+ statbuf->st_blksize = S_BLKSIZE;\r
+ statbuf->st_size = FileInfo->FileSize;\r
+ statbuf->st_physsize = FileInfo->PhysicalSize;\r
+ statbuf->st_birthtime = Efi2Time( &FileInfo->CreateTime);\r
+ statbuf->st_atime = Efi2Time( &FileInfo->LastAccessTime);\r
+ statbuf->st_mtime = Efi2Time( &FileInfo->ModificationTime);\r
+ Attributes = FileInfo->Attribute;\r
+ newmode = (mode_t)(Attributes << S_EFISHIFT) | S_ACC_READ;\r
+ if((Attributes & EFI_FILE_DIRECTORY) == 0) {\r
+ newmode |= _S_IFREG;\r
+ if((Attributes & EFI_FILE_READ_ONLY) == 0) {\r
+ newmode |= S_ACC_WRITE;\r
+ }\r
+ }\r
+ else {\r
+ newmode |= _S_IFDIR;\r
+ }\r
+ statbuf->st_mode = newmode;\r
+ Status = RETURN_SUCCESS;\r
+ }\r
+ else {\r
+ Status = RETURN_DEVICE_ERROR;\r
+ }\r
+ errno = EFI2errno(Status);\r
+ EFIerrno = Status;\r
+\r
+ if(FileInfo != NULL) {\r
+ FreePool(FileInfo); // Release the buffer allocated by the GetInfo function\r
+ }\r
+ return errno? -1 : 0;\r
+}\r
+\r
+static\r
+int\r
+EFIAPI\r
+da_ShellIoctl(\r
+ struct __filedes *filp,\r
+ ULONGN cmd,\r
+ void *argp ///< May be a pointer or a value\r
+ )\r
+{\r
+ return 0;\r
+}\r
+\r
+/** Open an abstract Shell File.\r
+**/\r
+int\r
+EFIAPI\r
+da_ShellOpen(\r
+ struct __filedes *filp,\r
+ void *DevInstance,\r
+ wchar_t *Path,\r
+ wchar_t *Flags\r
+ )\r
+{\r
+ UINT64 OpenMode;\r
+ UINT64 Attributes;\r
+ SHELL_FILE_HANDLE FileHandle;\r
+ GenericInstance *Gip;\r
+ char *NPath;\r
+ RETURN_STATUS Status;\r
+ int oflags;\r
+\r
+ EFIerrno = RETURN_SUCCESS;\r
+\r
+ //Attributes = Omode2EFI(mode);\r
+ Attributes = 0;\r
+\r
+ // Convert oflags to Attributes\r
+ oflags = filp->Oflags;\r
+ OpenMode = Oflags2EFI(oflags);\r
+ if(OpenMode == 0) {\r
+ errno = EINVAL;\r
+ return -1;\r
+ }\r
+\r
+ /* Do we care if the file already exists?\r
+ If O_TRUNC, then delete the file. It will be created anew subsequently.\r
+ If O_EXCL, then error if the file exists and O_CREAT is set.\r
+\r
+ !!!!!!!!! Change this to use ShellSetFileInfo() to actually truncate the file\r
+ !!!!!!!!! instead of deleting and re-creating it.\r
+ */\r
+ if((oflags & O_TRUNC) || ((oflags & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT))) {\r
+ Status = ShellIsFile( Path );\r
+ if(Status == RETURN_SUCCESS) {\r
+ // The file exists\r
+ if(oflags & O_TRUNC) {\r
+ NPath = AllocateZeroPool(1024);\r
+ if(NPath == NULL) {\r
+ errno = ENOMEM;\r
+ EFIerrno = RETURN_OUT_OF_RESOURCES;\r
+ return -1;\r
+ }\r
+ wcstombs(NPath, Path, 1024);\r
+ // We do a truncate by deleting the existing file and creating a new one.\r
+ if(unlink(NPath) != 0) {\r
+ filp->f_iflags = 0; // Release our reservation on this FD\r
+ FreePool(NPath);\r
+ return -1; // errno and EFIerrno are already set.\r
+ }\r
+ FreePool(NPath);\r
+ }\r
+ else if((oflags & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT)) {\r
+ errno = EEXIST;\r
+ EFIerrno = RETURN_ACCESS_DENIED;\r
+ filp->f_iflags = 0; // Release our reservation on this FD\r
+ return -1;\r
+ }\r
+ }\r
+ }\r
+\r
+ // Call the EFI Shell's Open function\r
+ Status = ShellOpenFileByName( Path, &FileHandle, OpenMode, Attributes);\r
+ if(RETURN_ERROR(Status)) {\r
+ filp->f_iflags = 0; // Release our reservation on this FD\r
+ // Set errno based upon Status\r
+ errno = EFI2errno(Status);\r
+ EFIerrno = Status;\r
+ return -1;\r
+ }\r
+ // Successfully got a regular File\r
+ filp->f_iflags |= S_IFREG;\r
+\r
+ // Update the info in the fd\r
+ filp->devdata = (void *)FileHandle;\r
+\r
+ Gip = (GenericInstance *)DevInstance;\r
+ filp->f_offset = 0;\r
+ filp->f_ops = &Gip->Abstraction;\r
+// filp->devdata = FileHandle;\r
+\r
+ return 0;\r
+}\r
+\r
+#include <sys/poll.h>\r
+/* Returns a bit mask describing which operations could be completed immediately.\r
+\r
+ For now, assume the file system, via the shell, is always ready.\r
+\r
+ (POLLIN | POLLRDNORM) The file system is ready to be read.\r
+ (POLLOUT) The file system is ready for output.\r
+\r
+*/\r
+static\r
+short\r
+EFIAPI\r
+da_ShellPoll(\r
+ struct __filedes *filp,\r
+ short events\r
+ )\r
+{\r
+ UINT32 RdyMask;\r
+ short retval = 0;\r
+\r
+ RdyMask = (UINT32)filp->Oflags;\r
+\r
+ switch(RdyMask & O_ACCMODE) {\r
+ case O_RDONLY:\r
+ retval = (POLLIN | POLLRDNORM);\r
+ break;\r
+\r
+ case O_WRONLY:\r
+ retval = POLLOUT;\r
+ break;\r
+\r
+ case O_RDWR:\r
+ retval = (POLLIN | POLLRDNORM | POLLOUT);\r
+ break;\r
+\r
+ default:\r
+ retval = POLLERR;\r
+ break;\r
+ }\r
+ return (retval & (events | POLL_RETONLY));\r
+}\r
+\r
+static\r
+int\r
+EFIAPI\r
+da_ShellRename(\r
+ const char *from,\r
+ const char *to\r
+ )\r
+{\r
+ RETURN_STATUS Status;\r
+ EFI_FILE_INFO *NewFileInfo;\r
+ EFI_FILE_INFO *OldFileInfo;\r
+ char *NewFn;\r
+ int OldFd;\r
+ SHELL_FILE_HANDLE FileHandle;\r
+\r
+ // Open old file\r
+ OldFd = open(from, O_RDWR, 0);\r
+ if(OldFd >= 0) {\r
+ FileHandle = (SHELL_FILE_HANDLE)gMD->fdarray[OldFd].devdata;\r
+\r
+ NewFileInfo = malloc(sizeof(EFI_FILE_INFO) + PATH_MAX);\r
+ if(NewFileInfo != NULL) {\r
+ OldFileInfo = ShellGetFileInfo( FileHandle);\r
+ if(OldFileInfo != NULL) {\r
+ // Copy the Old file info into our new buffer, and free the old.\r
+ memcpy(OldFileInfo, NewFileInfo, sizeof(EFI_FILE_INFO));\r
+ FreePool(OldFileInfo);\r
+ // Strip off all but the file name portion of new\r
+ NewFn = strrchr(to, '/');\r
+ if(NewFn == NULL) {\r
+ NewFn = strrchr(to, '\\');\r
+ if(NewFn == NULL) {\r
+ NewFn = (char *)to;\r
+ }\r
+ }\r
+ // Convert new name from MBCS to WCS\r
+ (void)AsciiStrToUnicodeStr( NewFn, gMD->UString);\r
+ // Copy the new file name into our new file info buffer\r
+ wcsncpy(NewFileInfo->FileName, gMD->UString, wcslen(gMD->UString)+1);\r
+ // Apply the new file name\r
+ Status = ShellSetFileInfo(FileHandle, NewFileInfo);\r
+ free(NewFileInfo);\r
+ if(Status == EFI_SUCCESS) {\r
+ // File has been successfully renamed. We are DONE!\r
+ return 0;\r
+ }\r
+ errno = EFI2errno( Status );\r
+ EFIerrno = Status;\r
+ }\r
+ else {\r
+ errno = EIO;\r
+ }\r
+ }\r
+ else {\r
+ errno = ENOMEM;\r
+ }\r
+ }\r
+ return -1;\r
+}\r
+\r
+static\r
+int\r
+EFIAPI\r
+da_ShellRmdir(\r
+ struct __filedes *filp\r
+ )\r
+{\r
+ SHELL_FILE_HANDLE FileHandle;\r
+ RETURN_STATUS Status = RETURN_SUCCESS;\r
+ EFI_FILE_INFO *FileInfo = NULL;\r
+ int Count = 0;\r
+ BOOLEAN NoFile = FALSE;\r
+\r
+ errno = 0; // Make it easier to see if we have an error later\r
+\r
+ FileHandle = (SHELL_FILE_HANDLE)filp->devdata;\r
+\r
+ FileInfo = ShellGetFileInfo(FileHandle);\r
+ if(FileInfo != NULL) {\r
+ if((FileInfo->Attribute & EFI_FILE_DIRECTORY) == 0) {\r
+ errno = ENOTDIR;\r
+ }\r
+ else {\r
+ // See if the directory has any entries other than ".." and ".".\r
+ FreePool(FileInfo); // Free up the buffer from ShellGetFileInfo()\r
+ Status = ShellFindFirstFile( FileHandle, &FileInfo);\r
+ if(Status == RETURN_SUCCESS) {\r
+ ++Count;\r
+ while(Count < 3) {\r
+ Status = ShellFindNextFile( FileHandle, FileInfo, &NoFile);\r
+ if(Status == RETURN_SUCCESS) {\r
+ if(NoFile) {\r
+ break;\r
+ }\r
+ ++Count;\r
+ }\r
+ else {\r
+ Count = 99;\r
+ }\r
+ }\r
+ FreePool(FileInfo); // Free buffer from ShellFindFirstFile()\r
+ if(Count < 3) {\r
+ // Directory is empty\r
+ Status = ShellDeleteFile( &FileHandle);\r
+ if(Status == RETURN_SUCCESS) {\r
+ EFIerrno = RETURN_SUCCESS;\r
+ return 0;\r
+ /* ######## SUCCESSFUL RETURN ######## */\r
+ }\r
+ }\r
+ else {\r
+ if(Count == 99) {\r
+ errno = EIO;\r
+ }\r
+ else {\r
+ errno = ENOTEMPTY;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ else {\r
+ errno = EIO;\r
+ }\r
+ EFIerrno = Status;\r
+ if(errno == 0) {\r
+ errno = EFI2errno( Status );\r
+ }\r
+ return -1;\r
+}\r
+\r
+/** Construct an instance of the abstract Shell device.\r
+\r
+ Allocate the instance structure and populate it with the information for\r
+ the device.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+__ctor_DevShell(\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+)\r
+{\r
+ GenericInstance *Stream;\r
+ DeviceNode *Node;\r
+ RETURN_STATUS Status;\r
+\r
+ Stream = (GenericInstance *)AllocateZeroPool(sizeof(GenericInstance));\r
+ if(Stream == NULL) {\r
+ return RETURN_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Stream->Cookie = CON_COOKIE;\r
+ Stream->InstanceNum = 1;\r
+ Stream->Dev = NULL;\r
+ Stream->Abstraction.fo_close = &da_ShellClose;\r
+ Stream->Abstraction.fo_read = &da_ShellRead;\r
+ Stream->Abstraction.fo_write = &da_ShellWrite;\r
+ Stream->Abstraction.fo_fcntl = &fnullop_fcntl;\r
+ Stream->Abstraction.fo_poll = &da_ShellPoll;\r
+ Stream->Abstraction.fo_flush = &fnullop_flush;\r
+ Stream->Abstraction.fo_stat = &da_ShellStat;\r
+ Stream->Abstraction.fo_ioctl = &fbadop_ioctl;\r
+ Stream->Abstraction.fo_delete = &da_ShellDelete;\r
+ Stream->Abstraction.fo_rmdir = &da_ShellRmdir;\r
+ Stream->Abstraction.fo_mkdir = &da_ShellMkdir;\r
+ Stream->Abstraction.fo_rename = &da_ShellRename;\r
+ Stream->Abstraction.fo_lseek = &da_ShellSeek;\r
+\r
+ Node = __DevRegister(NULL, NULL, &da_ShellOpen, Stream, 1, sizeof(GenericInstance), O_RDWR);\r
+ Status = EFIerrno;\r
+ Stream->Parent = Node;\r
+\r
+ return Status;\r
+}\r
+\r
+RETURN_STATUS\r
+EFIAPI\r
+__dtor_DevShell(\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+)\r
+{\r
+ if(daDefaultDevice != NULL) {\r
+ if(daDefaultDevice->InstanceList != NULL) {\r
+ FreePool(daDefaultDevice->InstanceList);\r
+ }\r
+ FreePool(daDefaultDevice);\r
+ }\r
+ return RETURN_SUCCESS;\r
+}\r