diff options
Diffstat (limited to 'EmulatorPkg/Unix/Host/PosixFileSystem.c')
-rw-r--r-- | EmulatorPkg/Unix/Host/PosixFileSystem.c | 1556 |
1 files changed, 1556 insertions, 0 deletions
diff --git a/EmulatorPkg/Unix/Host/PosixFileSystem.c b/EmulatorPkg/Unix/Host/PosixFileSystem.c new file mode 100644 index 0000000000..3141a3cf1a --- /dev/null +++ b/EmulatorPkg/Unix/Host/PosixFileSystem.c @@ -0,0 +1,1556 @@ +/*++ @file + POSIX Pthreads to emulate APs and implement threads + +Copyright (c) 2011, Apple Inc. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +**/ + +#include "SecMain.h" + + +#define EMU_SIMPLE_FILE_SYSTEM_PRIVATE_SIGNATURE SIGNATURE_32 ('E', 'P', 'f', 's') + +typedef struct { + UINTN Signature; + EMU_IO_THUNK_PROTOCOL *Thunk; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL SimpleFileSystem; + CHAR8 *FilePath; + CHAR16 *VolumeLabel; + BOOLEAN FileHandlesOpen; +} EMU_SIMPLE_FILE_SYSTEM_PRIVATE; + +#define EMU_SIMPLE_FILE_SYSTEM_PRIVATE_DATA_FROM_THIS(a) \ + CR (a, \ + EMU_SIMPLE_FILE_SYSTEM_PRIVATE, \ + SimpleFileSystem, \ + EMU_SIMPLE_FILE_SYSTEM_PRIVATE_SIGNATURE \ + ) + + +#define EMU_EFI_FILE_PRIVATE_SIGNATURE SIGNATURE_32 ('E', 'P', 'f', 'i') + +typedef struct { + UINTN Signature; + EMU_IO_THUNK_PROTOCOL *Thunk; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem; + EFI_FILE_PROTOCOL EfiFile; + int fd; + DIR *Dir; + BOOLEAN IsRootDirectory; + BOOLEAN IsDirectoryPath; + BOOLEAN IsOpenedByRead; + char *FileName; + struct dirent *Dirent; +} EMU_EFI_FILE_PRIVATE; + +#define EMU_EFI_FILE_PRIVATE_DATA_FROM_THIS(a) \ + CR (a, \ + EMU_EFI_FILE_PRIVATE, \ + EfiFile, \ + EMU_EFI_FILE_PRIVATE_SIGNATURE \ + ) + +EFI_STATUS +PosixFileGetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +EFI_STATUS +PosixFileSetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN UINTN BufferSize, + IN VOID *Buffer + ); + + +EFI_FILE_PROTOCOL gPosixFileProtocol = { + EFI_FILE_REVISION, + GasketPosixFileOpen, + GasketPosixFileCLose, + GasketPosixFileDelete, + GasketPosixFileRead, + GasketPosixFileWrite, + GasketPosixFileGetPossition, + GasketPosixFileSetPossition, + GasketPosixFileGetInfo, + GasketPosixFileSetInfo, + GasketPosixFileFlush +}; + +EFI_SIMPLE_FILE_SYSTEM_PROTOCOL gPosixFileSystemProtocol = { + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION, + GasketPosixOpenVolume, +}; + + +/** + Open the root directory on a volume. + + @param This Protocol instance pointer. + @param Root Returns an Open file handle for the root directory + + @retval EFI_SUCCESS The device was opened. + @retval EFI_UNSUPPORTED This volume does not support the file system. + @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_ACCESS_DENIED The service denied access to the file. + @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of resources. + +**/ +EFI_STATUS +PosixOpenVolume ( + IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **Root + ) +{ + EFI_STATUS Status; + EMU_SIMPLE_FILE_SYSTEM_PRIVATE *Private; + EMU_EFI_FILE_PRIVATE *PrivateFile; + + Private = EMU_SIMPLE_FILE_SYSTEM_PRIVATE_DATA_FROM_THIS (This); + + Status = EFI_OUT_OF_RESOURCES; + PrivateFile = malloc (sizeof (EMU_EFI_FILE_PRIVATE)); + if (PrivateFile == NULL) { + goto Done; + } + + PrivateFile->FileName = malloc (AsciiStrSize (Private->FilePath)); + if (PrivateFile->FileName == NULL) { + goto Done; + } + AsciiStrCpy (PrivateFile->FileName, Private->FilePath); + + PrivateFile->Signature = EMU_EFI_FILE_PRIVATE_SIGNATURE; + PrivateFile->Thunk = Private->Thunk; + PrivateFile->SimpleFileSystem = This; + PrivateFile->IsRootDirectory = TRUE; + PrivateFile->IsDirectoryPath = TRUE; + PrivateFile->IsOpenedByRead = TRUE; + + CopyMem (&PrivateFile->EfiFile, &gPosixFileProtocol, sizeof (EFI_FILE_PROTOCOL)); + + PrivateFile->fd = -1; + PrivateFile->Dir = NULL; + PrivateFile->Dirent = NULL; + + *Root = &PrivateFile->EfiFile; + + PrivateFile->Dir = opendir (PrivateFile->FileName); + if (PrivateFile->Dir == NULL) { + Status = EFI_ACCESS_DENIED; + } else { + Status = EFI_SUCCESS; + } + +Done: + if (EFI_ERROR (Status)) { + if (PrivateFile != NULL) { + if (PrivateFile->FileName != NULL) { + free (PrivateFile->FileName); + } + + free (PrivateFile); + } + + *Root = NULL; + } + + return Status; +} + + +EFI_STATUS +ErrnoToEfiStatus () +{ + switch (errno) { + case EACCES: + return EFI_ACCESS_DENIED; + + case EDQUOT: + case ENOSPC: + return EFI_VOLUME_FULL; + + default: + return EFI_DEVICE_ERROR; + } +} + +VOID +CutPrefix ( + IN CHAR8 *Str, + IN UINTN Count + ) +{ + CHAR8 *Pointer; + + if (AsciiStrLen (Str) < Count) { + ASSERT (0); + } + + for (Pointer = Str; *(Pointer + Count); Pointer++) { + *Pointer = *(Pointer + Count); + } + + *Pointer = *(Pointer + Count); +} + + +VOID +PosixSystemTimeToEfiTime ( + IN time_t SystemTime, + OUT EFI_TIME *Time + ) +{ + struct tm *tm; + + tm = gmtime (&SystemTime); + Time->Year = tm->tm_year; + Time->Month = tm->tm_mon + 1; + Time->Day = tm->tm_mday; + Time->Hour = tm->tm_hour; + Time->Minute = tm->tm_min; + Time->Second = tm->tm_sec; + Time->Nanosecond = 0; + + Time->TimeZone = timezone; + Time->Daylight = (daylight ? EFI_TIME_ADJUST_DAYLIGHT : 0) | (tm->tm_isdst > 0 ? EFI_TIME_IN_DAYLIGHT : 0); +} + + +EFI_STATUS +UnixSimpleFileSystemFileInfo ( + EMU_EFI_FILE_PRIVATE *PrivateFile, + IN CHAR8 *FileName, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINTN Size; + UINTN NameSize; + UINTN ResultSize; + EFI_FILE_INFO *Info; + CHAR8 *RealFileName; + CHAR8 *TempPointer; + CHAR16 *BufferFileName; + struct stat buf; + + if (FileName != NULL) { + RealFileName = FileName; + } else if (PrivateFile->IsRootDirectory) { + RealFileName = ""; + } else { + RealFileName = PrivateFile->FileName; + } + + TempPointer = RealFileName; + while (*TempPointer) { + if (*TempPointer == '/') { + RealFileName = TempPointer + 1; + } + + TempPointer++; + } + + Size = SIZE_OF_EFI_FILE_INFO; + NameSize = AsciiStrSize (RealFileName) * 2; + ResultSize = Size + NameSize; + + if (*BufferSize < ResultSize) { + *BufferSize = ResultSize; + return EFI_BUFFER_TOO_SMALL; + } + if (stat (FileName == NULL ? PrivateFile->FileName : FileName, &buf) < 0) { + return EFI_DEVICE_ERROR; + } + + Status = EFI_SUCCESS; + + Info = Buffer; + ZeroMem (Info, ResultSize); + + Info->Size = ResultSize; + Info->FileSize = buf.st_size; + Info->PhysicalSize = MultU64x32 (buf.st_blocks, buf.st_blksize); + + PosixSystemTimeToEfiTime (buf.st_ctime, &Info->CreateTime); + PosixSystemTimeToEfiTime (buf.st_atime, &Info->LastAccessTime); + PosixSystemTimeToEfiTime (buf.st_mtime, &Info->ModificationTime); + + if (!(buf.st_mode & S_IWUSR)) { + Info->Attribute |= EFI_FILE_READ_ONLY; + } + + if (S_ISDIR(buf.st_mode)) { + Info->Attribute |= EFI_FILE_DIRECTORY; + } + + + BufferFileName = (CHAR16 *)((CHAR8 *) Buffer + Size); + while (*RealFileName) { + *BufferFileName++ = *RealFileName++; + } + *BufferFileName = 0; + + *BufferSize = ResultSize; + return Status; +} + +BOOLEAN +IsZero ( + IN VOID *Buffer, + IN UINTN Length + ) +{ + if (Buffer == NULL || Length == 0) { + return FALSE; + } + + if (*(UINT8 *) Buffer != 0) { + return FALSE; + } + + if (Length > 1) { + if (!CompareMem (Buffer, (UINT8 *) Buffer + 1, Length - 1)) { + return FALSE; + } + } + + return TRUE; +} + + + +/** + Opens a new file relative to the source file's location. + + @param This The protocol instance pointer. + @param NewHandle Returns File Handle for FileName. + @param FileName Null terminated string. "\", ".", and ".." are supported. + @param OpenMode Open mode for file. + @param 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 +PosixFileOpen ( + IN EFI_FILE_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes + ) +{ + EFI_FILE_PROTOCOL *Root; + EMU_EFI_FILE_PRIVATE *PrivateFile; + EMU_EFI_FILE_PRIVATE *NewPrivateFile; + EMU_SIMPLE_FILE_SYSTEM_PRIVATE *PrivateRoot; + EFI_STATUS Status; + CHAR16 *Src; + char *Dst; + CHAR8 *RealFileName; + char *ParseFileName; + char *GuardPointer; + CHAR8 TempChar; + UINTN Count; + BOOLEAN TrailingDash; + BOOLEAN LoopFinish; + UINTN InfoSize; + EFI_FILE_INFO *Info; + struct stat finfo; + int res; + + + PrivateFile = EMU_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); + PrivateRoot = EMU_SIMPLE_FILE_SYSTEM_PRIVATE_DATA_FROM_THIS (PrivateFile->SimpleFileSystem); + NewPrivateFile = NULL; + Status = EFI_OUT_OF_RESOURCES; + + // + // BUGBUG: assume an open of root + // if current location, return current data + // + if ((StrCmp (FileName, L"\\") == 0) || + (StrCmp (FileName, L".") == 0 && PrivateFile->IsRootDirectory)) { +OpenRoot: + Status = PosixOpenVolume (PrivateFile->SimpleFileSystem, &Root); + NewPrivateFile = EMU_EFI_FILE_PRIVATE_DATA_FROM_THIS (Root); + goto Done; + } + + TrailingDash = FALSE; + if (FileName[StrLen (FileName) - 1] == L'\\') { + TrailingDash = TRUE; + FileName[StrLen (FileName) - 1] = 0; + } + + // + // Attempt to open the file + // + NewPrivateFile = malloc (sizeof (EMU_EFI_FILE_PRIVATE)); + if (NewPrivateFile == NULL) { + goto Done; + } + + CopyMem (NewPrivateFile, PrivateFile, sizeof (EMU_EFI_FILE_PRIVATE)); + + NewPrivateFile->FileName = malloc (AsciiStrSize (PrivateFile->FileName) + 1 + StrLen (FileName) + 1); + if (NewPrivateFile->FileName == NULL) { + goto Done; + } + + if (*FileName == L'\\') { + AsciiStrCpy (NewPrivateFile->FileName, PrivateRoot->FilePath); + // Skip first '\'. + Src = FileName + 1; + } else { + AsciiStrCpy (NewPrivateFile->FileName, PrivateFile->FileName); + Src = FileName; + } + Dst = NewPrivateFile->FileName + AsciiStrLen (NewPrivateFile->FileName); + GuardPointer = NewPrivateFile->FileName + AsciiStrLen (PrivateRoot->FilePath); + *Dst++ = '/'; + // Convert unicode to ascii and '\' to '/' + while (*Src) { + if (*Src == '\\') { + *Dst++ = '/'; + } else { + *Dst++ = *Src; + } + Src++; + } + *Dst = 0; + + + // + // Get rid of . and .., except leading . or .. + // + + // + // GuardPointer protect simplefilesystem root path not be destroyed + // + + LoopFinish = FALSE; + while (!LoopFinish) { + LoopFinish = TRUE; + + for (ParseFileName = GuardPointer; *ParseFileName; ParseFileName++) { + if (*ParseFileName == '.' && + (*(ParseFileName + 1) == 0 || *(ParseFileName + 1) == '/') && + *(ParseFileName - 1) == '/' + ) { + + // + // cut /. + // + CutPrefix (ParseFileName - 1, 2); + LoopFinish = FALSE; + break; + } + + if (*ParseFileName == '.' && + *(ParseFileName + 1) == '.' && + (*(ParseFileName + 2) == 0 || *(ParseFileName + 2) == '/') && + *(ParseFileName - 1) == '/' + ) { + + ParseFileName--; + Count = 3; + + while (ParseFileName != GuardPointer) { + ParseFileName--; + Count++; + if (*ParseFileName == '/') { + break; + } + } + + // + // cut /.. and its left directory + // + CutPrefix (ParseFileName, Count); + LoopFinish = FALSE; + break; + } + } + } + + if (AsciiStrCmp (NewPrivateFile->FileName, PrivateRoot->FilePath) == 0) { + NewPrivateFile->IsRootDirectory = TRUE; + free (NewPrivateFile->FileName); + free (NewPrivateFile); + goto OpenRoot; + } + + RealFileName = NewPrivateFile->FileName + AsciiStrLen(NewPrivateFile->FileName) - 1; + while (RealFileName > NewPrivateFile->FileName && *RealFileName != '/') { + RealFileName--; + } + + TempChar = *(RealFileName - 1); + *(RealFileName - 1) = 0; + *(RealFileName - 1) = TempChar; + + + // + // Test whether file or directory + // + NewPrivateFile->IsRootDirectory = FALSE; + NewPrivateFile->fd = -1; + NewPrivateFile->Dir = NULL; + if (OpenMode & EFI_FILE_MODE_CREATE) { + if (Attributes & EFI_FILE_DIRECTORY) { + NewPrivateFile->IsDirectoryPath = TRUE; + } else { + NewPrivateFile->IsDirectoryPath = FALSE; + } + } else { + res = stat (NewPrivateFile->FileName, &finfo); + if (res == 0 && S_ISDIR(finfo.st_mode)) { + NewPrivateFile->IsDirectoryPath = TRUE; + } else { + NewPrivateFile->IsDirectoryPath = FALSE; + } + } + + if (OpenMode & EFI_FILE_MODE_WRITE) { + NewPrivateFile->IsOpenedByRead = FALSE; + } else { + NewPrivateFile->IsOpenedByRead = TRUE; + } + + Status = EFI_SUCCESS; + + // + // deal with directory + // + if (NewPrivateFile->IsDirectoryPath) { + if ((OpenMode & EFI_FILE_MODE_CREATE)) { + // + // Create a directory + // + if (mkdir (NewPrivateFile->FileName, 0777) != 0) { + if (errno != EEXIST) { + //free (TempFileName); + Status = EFI_ACCESS_DENIED; + goto Done; + } + } + } + + NewPrivateFile->Dir = opendir (NewPrivateFile->FileName); + if (NewPrivateFile->Dir == NULL) { + if (errno == EACCES) { + Status = EFI_ACCESS_DENIED; + } else { + Status = EFI_NOT_FOUND; + } + + goto Done; + } + + } else { + // + // deal with file + // + NewPrivateFile->fd = open ( + NewPrivateFile->FileName, + ((OpenMode & EFI_FILE_MODE_CREATE) ? O_CREAT : 0) | (NewPrivateFile->IsOpenedByRead ? O_RDONLY : O_RDWR), + 0666 + ); + if (NewPrivateFile->fd < 0) { + if (errno == ENOENT) { + Status = EFI_NOT_FOUND; + } else { + Status = EFI_ACCESS_DENIED; + } + } + } + + if ((OpenMode & EFI_FILE_MODE_CREATE) && Status == EFI_SUCCESS) { + // + // Set the attribute + // + InfoSize = 0; + Info = NULL; + Status = PosixFileGetInfo (&NewPrivateFile->EfiFile, &gEfiFileInfoGuid, &InfoSize, Info); + if (Status != EFI_BUFFER_TOO_SMALL) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + Info = malloc (InfoSize); + if (Info == NULL) { + goto Done; + } + + Status = PosixFileGetInfo (&NewPrivateFile->EfiFile, &gEfiFileInfoGuid, &InfoSize, Info); + if (EFI_ERROR (Status)) { + goto Done; + } + + Info->Attribute = Attributes; + PosixFileSetInfo (&NewPrivateFile->EfiFile, &gEfiFileInfoGuid, InfoSize, Info); + + free (Info); + } + +Done: ; + if (TrailingDash) { + FileName[StrLen (FileName) + 1] = 0; + FileName[StrLen (FileName)] = L'\\'; + } + + if (EFI_ERROR (Status)) { + if (NewPrivateFile) { + if (NewPrivateFile->FileName) { + free (NewPrivateFile->FileName); + } + + free (NewPrivateFile); + } + } else { + *NewHandle = &NewPrivateFile->EfiFile; + } + + return Status; +} + + + +/** + Close the file handle + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The device was opened. + +**/ +EFI_STATUS +PosixFileCLose ( + IN EFI_FILE_PROTOCOL *This + ) +{ + EMU_EFI_FILE_PRIVATE *PrivateFile; + + PrivateFile = EMU_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); + + if (PrivateFile->fd >= 0) { + close (PrivateFile->fd); + } + if (PrivateFile->Dir != NULL) { + closedir (PrivateFile->Dir); + } + + PrivateFile->fd = -1; + PrivateFile->Dir = NULL; + + if (PrivateFile->FileName) { + free (PrivateFile->FileName); + } + + free (PrivateFile); + + return EFI_SUCCESS; +} + + +/** + 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 +PosixFileDelete ( + IN EFI_FILE_PROTOCOL *This + ) +{ + EFI_STATUS Status; + EMU_EFI_FILE_PRIVATE *PrivateFile; + + PrivateFile = EMU_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); + Status = EFI_WARN_DELETE_FAILURE; + + if (PrivateFile->IsDirectoryPath) { + if (PrivateFile->Dir != NULL) { + closedir (PrivateFile->Dir); + PrivateFile->Dir = NULL; + } + + if (rmdir (PrivateFile->FileName) == 0) { + Status = EFI_SUCCESS; + } + } else { + close (PrivateFile->fd); + PrivateFile->fd = -1; + + if (!PrivateFile->IsOpenedByRead) { + if (!unlink (PrivateFile->FileName)) { + Status = EFI_SUCCESS; + } + } + } + + free (PrivateFile->FileName); + free (PrivateFile); + + return Status; +} + + +/** + 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 +PosixFileRead ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EMU_EFI_FILE_PRIVATE *PrivateFile; + EFI_STATUS Status; + int Res; + UINTN Size; + UINTN NameSize; + UINTN ResultSize; + CHAR8 *FullFileName; + + + PrivateFile = EMU_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); + + if (!PrivateFile->IsDirectoryPath) { + if (PrivateFile->fd < 0) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + Res = read (PrivateFile->fd, Buffer, *BufferSize); + if (Res < 0) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + *BufferSize = Res; + Status = EFI_SUCCESS; + goto Done; + } + + // + // Read on a directory. + // + if (PrivateFile->Dir == NULL) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + if (PrivateFile->Dirent == NULL) { + PrivateFile->Dirent = readdir (PrivateFile->Dir); + if (PrivateFile->Dirent == NULL) { + *BufferSize = 0; + Status = EFI_SUCCESS; + goto Done; + } + } + + Size = SIZE_OF_EFI_FILE_INFO; + NameSize = AsciiStrLen (PrivateFile->Dirent->d_name) + 1; + ResultSize = Size + 2 * NameSize; + + if (*BufferSize < ResultSize) { + *BufferSize = ResultSize; + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + Status = EFI_SUCCESS; + + *BufferSize = ResultSize; + + FullFileName = malloc (AsciiStrLen(PrivateFile->FileName) + 1 + NameSize); + if (FullFileName == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + AsciiStrCpy (FullFileName, PrivateFile->FileName); + AsciiStrCat (FullFileName, "/"); + AsciiStrCat (FullFileName, PrivateFile->Dirent->d_name); + Status = UnixSimpleFileSystemFileInfo ( + PrivateFile, + FullFileName, + BufferSize, + Buffer + ); + free (FullFileName); + + PrivateFile->Dirent = NULL; + +Done: + return Status; +} + + + +/** + Write data to a 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 to write. + + @retval EFI_SUCCESS Data was written. + @retval EFI_UNSUPPORTED 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_DEVICE_ERROR An attempt was made to write to a deleted file. + @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 +PosixFileWrite ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + EMU_EFI_FILE_PRIVATE *PrivateFile; + int Res; + + + PrivateFile = EMU_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); + + if (PrivateFile->fd < 0) { + return EFI_DEVICE_ERROR; + } + + if (PrivateFile->IsDirectoryPath) { + return EFI_UNSUPPORTED; + } + + if (PrivateFile->IsOpenedByRead) { + return EFI_ACCESS_DENIED; + } + + Res = write (PrivateFile->fd, Buffer, *BufferSize); + if (Res == (UINTN)-1) { + return ErrnoToEfiStatus (); + } + + *BufferSize = Res; + return EFI_SUCCESS; +} + + + +/** + 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 +PosixFileSetPossition ( + IN EFI_FILE_PROTOCOL *This, + IN UINT64 Position + ) +{ + EMU_EFI_FILE_PRIVATE *PrivateFile; + off_t Pos; + + PrivateFile = EMU_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); + + if (PrivateFile->IsDirectoryPath) { + if (Position != 0) { + return EFI_UNSUPPORTED; + } + + if (PrivateFile->Dir == NULL) { + return EFI_DEVICE_ERROR; + } + rewinddir (PrivateFile->Dir); + return EFI_SUCCESS; + } else { + if (Position == (UINT64) -1) { + Pos = lseek (PrivateFile->fd, 0, SEEK_END); + } else { + Pos = lseek (PrivateFile->fd, Position, SEEK_SET); + } + if (Pos == (off_t)-1) { + return ErrnoToEfiStatus (); + } + return EFI_SUCCESS; + } +} + + + +/** + 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 +PosixFileGetPossition ( + IN EFI_FILE_PROTOCOL *This, + OUT UINT64 *Position + ) +{ + EFI_STATUS Status; + EMU_EFI_FILE_PRIVATE *PrivateFile; + + PrivateFile = EMU_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); + + if (PrivateFile->IsDirectoryPath) { + Status = EFI_UNSUPPORTED; + } else { + *Position = (UINT64)lseek (PrivateFile->fd, 0, SEEK_CUR); + Status = (*Position == (UINT64) -1) ? ErrnoToEfiStatus () : EFI_SUCCESS; + } + + return Status; +} + + +/** + 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_UNSUPPORTED 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 +PosixFileGetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + EMU_EFI_FILE_PRIVATE *PrivateFile; + EFI_FILE_SYSTEM_INFO *FileSystemInfoBuffer; + int UnixStatus; + EMU_SIMPLE_FILE_SYSTEM_PRIVATE *PrivateRoot; + struct statfs buf; + + PrivateFile = EMU_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); + PrivateRoot = EMU_SIMPLE_FILE_SYSTEM_PRIVATE_DATA_FROM_THIS (PrivateFile->SimpleFileSystem); + + Status = EFI_SUCCESS; + if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { + Status = UnixSimpleFileSystemFileInfo (PrivateFile, NULL, BufferSize, Buffer); + } else if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { + if (*BufferSize < SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (PrivateRoot->VolumeLabel)) { + *BufferSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (PrivateRoot->VolumeLabel); + return EFI_BUFFER_TOO_SMALL; + } + + UnixStatus = statfs (PrivateFile->FileName, &buf); + if (UnixStatus < 0) { + return EFI_DEVICE_ERROR; + } + + FileSystemInfoBuffer = (EFI_FILE_SYSTEM_INFO *) Buffer; + FileSystemInfoBuffer->Size = SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (PrivateRoot->VolumeLabel); + FileSystemInfoBuffer->ReadOnly = FALSE; + + // + // Succeeded + // + FileSystemInfoBuffer->VolumeSize = MultU64x32 (buf.f_blocks, buf.f_bsize); + FileSystemInfoBuffer->FreeSpace = MultU64x32 (buf.f_bavail, buf.f_bsize); + FileSystemInfoBuffer->BlockSize = buf.f_bsize; + + + StrCpy ((CHAR16 *) FileSystemInfoBuffer->VolumeLabel, PrivateRoot->VolumeLabel); + *BufferSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (PrivateRoot->VolumeLabel); + + } else if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { + if (*BufferSize < StrSize (PrivateRoot->VolumeLabel)) { + *BufferSize = StrSize (PrivateRoot->VolumeLabel); + return EFI_BUFFER_TOO_SMALL; + } + + StrCpy ((CHAR16 *) Buffer, PrivateRoot->VolumeLabel); + *BufferSize = StrSize (PrivateRoot->VolumeLabel); + + } + + return Status; +} + + +/** + Set information about a file + + @param File 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_UNSUPPORTED 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 +PosixFileSetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + EMU_SIMPLE_FILE_SYSTEM_PRIVATE *PrivateRoot; + EMU_EFI_FILE_PRIVATE *PrivateFile; + EFI_FILE_INFO *OldFileInfo; + EFI_FILE_INFO *NewFileInfo; + EFI_STATUS Status; + UINTN OldInfoSize; + mode_t NewAttr; + struct stat OldAttr; + CHAR8 *OldFileName; + CHAR8 *NewFileName; + CHAR8 *CharPointer; + BOOLEAN AttrChangeFlag; + BOOLEAN NameChangeFlag; + BOOLEAN SizeChangeFlag; + BOOLEAN TimeChangeFlag; + struct tm NewLastAccessSystemTime; + struct tm NewLastWriteSystemTime; + EFI_FILE_SYSTEM_INFO *NewFileSystemInfo; + CHAR8 *AsciiFilePtr; + CHAR16 *UnicodeFilePtr; + int UnixStatus; + struct utimbuf Utime; + + + PrivateFile = EMU_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); + PrivateRoot = EMU_SIMPLE_FILE_SYSTEM_PRIVATE_DATA_FROM_THIS (PrivateFile->SimpleFileSystem); + errno = 0; + Status = EFI_UNSUPPORTED; + OldFileInfo = NewFileInfo = NULL; + OldFileName = NewFileName = NULL; + AttrChangeFlag = NameChangeFlag = SizeChangeFlag = TimeChangeFlag = FALSE; + + // + // Set file system information. + // + if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { + if (BufferSize < (SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (PrivateRoot->VolumeLabel))) { + Status = EFI_BAD_BUFFER_SIZE; + goto Done; + } + + NewFileSystemInfo = (EFI_FILE_SYSTEM_INFO *) Buffer; + + free (PrivateRoot->VolumeLabel); + + PrivateRoot->VolumeLabel = malloc (StrSize (NewFileSystemInfo->VolumeLabel)); + if (PrivateRoot->VolumeLabel == NULL) { + goto Done; + } + + StrCpy (PrivateRoot->VolumeLabel, NewFileSystemInfo->VolumeLabel); + + Status = EFI_SUCCESS; + goto Done; + } + + // + // Set volume label information. + // + if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { + if (BufferSize < StrSize (PrivateRoot->VolumeLabel)) { + Status = EFI_BAD_BUFFER_SIZE; + goto Done; + } + + StrCpy (PrivateRoot->VolumeLabel, (CHAR16 *) Buffer); + + Status = EFI_SUCCESS; + goto Done; + } + + if (!CompareGuid (InformationType, &gEfiFileInfoGuid)) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + if (BufferSize < SIZE_OF_EFI_FILE_INFO) { + Status = EFI_BAD_BUFFER_SIZE; + goto Done; + } + + // + // Set file/directory information. + // + + // + // Check for invalid set file information parameters. + // + NewFileInfo = (EFI_FILE_INFO *) Buffer; + if (NewFileInfo->Size <= sizeof (EFI_FILE_INFO) || + (NewFileInfo->Attribute &~(EFI_FILE_VALID_ATTR)) || + (sizeof (UINTN) == 4 && NewFileInfo->Size > 0xFFFFFFFF) + ) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + // + // Get current file information so we can determine what kind + // of change request this is. + // + OldInfoSize = 0; + Status = UnixSimpleFileSystemFileInfo (PrivateFile, NULL, &OldInfoSize, NULL); + if (Status != EFI_BUFFER_TOO_SMALL) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + OldFileInfo = malloc (OldInfoSize); + if (OldFileInfo == NULL) { + goto Done; + } + + Status = UnixSimpleFileSystemFileInfo (PrivateFile, NULL, &OldInfoSize, OldFileInfo); + if (EFI_ERROR (Status)) { + goto Done; + } + + OldFileName = malloc (AsciiStrSize (PrivateFile->FileName)); + if (OldFileInfo == NULL) { + goto Done; + } + + AsciiStrCpy (OldFileName, PrivateFile->FileName); + + // + // Make full pathname from new filename and rootpath. + // + if (NewFileInfo->FileName[0] == '\\') { + NewFileName = malloc (AsciiStrLen (PrivateRoot->FilePath) + 1 + StrLen (NewFileInfo->FileName) + 1); + if (NewFileName == NULL) { + goto Done; + } + + AsciiStrCpy (NewFileName, PrivateRoot->FilePath); + AsciiFilePtr = NewFileName + AsciiStrLen(NewFileName); + UnicodeFilePtr = NewFileInfo->FileName + 1; + *AsciiFilePtr++ ='/'; + } else { + NewFileName = malloc (AsciiStrLen (PrivateFile->FileName) + 2 + StrLen (NewFileInfo->FileName) + 1); + if (NewFileName == NULL) { + goto Done; + } + + AsciiStrCpy (NewFileName, PrivateRoot->FilePath); + AsciiFilePtr = NewFileName + AsciiStrLen(NewFileName); + if ((AsciiFilePtr[-1] != '/') && (NewFileInfo->FileName[0] != '/')) { + // make sure there is a / between Root FilePath and NewFileInfo Filename + AsciiFilePtr[0] = '/'; + AsciiFilePtr[1] = '\0'; + AsciiFilePtr++; + } + UnicodeFilePtr = NewFileInfo->FileName; + } + // Convert to ascii. + while (*UnicodeFilePtr) { + *AsciiFilePtr++ = *UnicodeFilePtr++; + } + *AsciiFilePtr = 0; + + // + // Is there an attribute change request? + // + if (NewFileInfo->Attribute != OldFileInfo->Attribute) { + if ((NewFileInfo->Attribute & EFI_FILE_DIRECTORY) != (OldFileInfo->Attribute & EFI_FILE_DIRECTORY)) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + AttrChangeFlag = TRUE; + } + + // + // Is there a name change request? + // bugbug: - Should really use EFI_UNICODE_COLLATION_PROTOCOL + // + if (StrCmp (NewFileInfo->FileName, OldFileInfo->FileName)) { + NameChangeFlag = TRUE; + } + + // + // Is there a size change request? + // + if (NewFileInfo->FileSize != OldFileInfo->FileSize) { + SizeChangeFlag = TRUE; + } + + // + // Is there a time stamp change request? + // + if (!IsZero (&NewFileInfo->CreateTime, sizeof (EFI_TIME)) && + CompareMem (&NewFileInfo->CreateTime, &OldFileInfo->CreateTime, sizeof (EFI_TIME)) + ) { + TimeChangeFlag = TRUE; + } else if (!IsZero (&NewFileInfo->LastAccessTime, sizeof (EFI_TIME)) && + CompareMem (&NewFileInfo->LastAccessTime, &OldFileInfo->LastAccessTime, sizeof (EFI_TIME)) + ) { + TimeChangeFlag = TRUE; + } else if (!IsZero (&NewFileInfo->ModificationTime, sizeof (EFI_TIME)) && + CompareMem (&NewFileInfo->ModificationTime, &OldFileInfo->ModificationTime, sizeof (EFI_TIME)) + ) { + TimeChangeFlag = TRUE; + } + + // + // All done if there are no change requests being made. + // + if (!(AttrChangeFlag || NameChangeFlag || SizeChangeFlag || TimeChangeFlag)) { + Status = EFI_SUCCESS; + goto Done; + } + + // + // Set file or directory information. + // + if (stat (OldFileName, &OldAttr) != 0) { + Status = ErrnoToEfiStatus (); + goto Done; + } + + // + // Name change. + // + if (NameChangeFlag) { + // + // Close the handles first + // + if (PrivateFile->IsOpenedByRead) { + Status = EFI_ACCESS_DENIED; + goto Done; + } + + for (CharPointer = NewFileName; *CharPointer != 0 && *CharPointer != L'/'; CharPointer++) { + } + + if (*CharPointer != 0) { + Status = EFI_ACCESS_DENIED; + goto Done; + } + + UnixStatus = rename (OldFileName, NewFileName); + if (UnixStatus == 0) { + // + // modify file name + // + free (PrivateFile->FileName); + + PrivateFile->FileName = malloc (AsciiStrSize (NewFileName)); + if (PrivateFile->FileName == NULL) { + goto Done; + } + + AsciiStrCpy (PrivateFile->FileName, NewFileName); + } else { + Status = EFI_DEVICE_ERROR; + goto Done; + } + } + + // + // Size change + // + if (SizeChangeFlag) { + if (PrivateFile->IsDirectoryPath) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + if (PrivateFile->IsOpenedByRead || OldFileInfo->Attribute & EFI_FILE_READ_ONLY) { + Status = EFI_ACCESS_DENIED; + goto Done; + } + + if (ftruncate (PrivateFile->fd, NewFileInfo->FileSize) != 0) { + Status = ErrnoToEfiStatus (); + goto Done; + } + + } + + // + // Time change + // + if (TimeChangeFlag) { + NewLastAccessSystemTime.tm_year = NewFileInfo->LastAccessTime.Year; + NewLastAccessSystemTime.tm_mon = NewFileInfo->LastAccessTime.Month; + NewLastAccessSystemTime.tm_mday = NewFileInfo->LastAccessTime.Day; + NewLastAccessSystemTime.tm_hour = NewFileInfo->LastAccessTime.Hour; + NewLastAccessSystemTime.tm_min = NewFileInfo->LastAccessTime.Minute; + NewLastAccessSystemTime.tm_sec = NewFileInfo->LastAccessTime.Second; + NewLastAccessSystemTime.tm_isdst = 0; + + Utime.actime = mktime (&NewLastAccessSystemTime); + + NewLastWriteSystemTime.tm_year = NewFileInfo->ModificationTime.Year; + NewLastWriteSystemTime.tm_mon = NewFileInfo->ModificationTime.Month; + NewLastWriteSystemTime.tm_mday = NewFileInfo->ModificationTime.Day; + NewLastWriteSystemTime.tm_hour = NewFileInfo->ModificationTime.Hour; + NewLastWriteSystemTime.tm_min = NewFileInfo->ModificationTime.Minute; + NewLastWriteSystemTime.tm_sec = NewFileInfo->ModificationTime.Second; + NewLastWriteSystemTime.tm_isdst = 0; + + Utime.modtime = mktime (&NewLastWriteSystemTime); + + if (Utime.actime == (time_t)-1 || Utime.modtime == (time_t)-1) { + goto Done; + } + + if (utime (PrivateFile->FileName, &Utime) == -1) { + Status = ErrnoToEfiStatus (); + goto Done; + } + } + + // + // No matter about AttrChangeFlag, Attribute must be set. + // Because operation before may cause attribute change. + // + NewAttr = OldAttr.st_mode; + + if (NewFileInfo->Attribute & EFI_FILE_READ_ONLY) { + NewAttr &= ~(S_IRUSR | S_IRGRP | S_IROTH); + } else { + NewAttr |= S_IRUSR; + } + + if (chmod (NewFileName, NewAttr) != 0) { + Status = ErrnoToEfiStatus (); + } + +Done: + if (OldFileInfo != NULL) { + free (OldFileInfo); + } + + if (OldFileName != NULL) { + free (OldFileName); + } + + if (NewFileName != NULL) { + free (NewFileName); + } + + return Status; +} + + +/** + Flush data back for the file handle. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS Data was written. + @retval EFI_UNSUPPORTED 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 +PosixFileFlush ( + IN EFI_FILE_PROTOCOL *This + ) +{ + EMU_EFI_FILE_PRIVATE *PrivateFile; + + + PrivateFile = EMU_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); + + if (PrivateFile->IsDirectoryPath) { + return EFI_UNSUPPORTED; + } + + if (PrivateFile->IsOpenedByRead) { + return EFI_ACCESS_DENIED; + } + + if (PrivateFile->fd < 0) { + return EFI_DEVICE_ERROR; + } + + if (fsync (PrivateFile->fd) != 0) { + return ErrnoToEfiStatus (); + } + + return EFI_SUCCESS; +} + + + +EFI_STATUS +PosixFileSystmeThunkOpen ( + IN EMU_IO_THUNK_PROTOCOL *This + ) +{ + EMU_SIMPLE_FILE_SYSTEM_PRIVATE *Private; + UINTN i; + + if (This->Private != NULL) { + return EFI_ALREADY_STARTED; + } + + if (!CompareGuid (This->Protocol, &gEfiSimpleFileSystemProtocolGuid)) { + return EFI_UNSUPPORTED; + } + + Private = malloc (sizeof (EMU_SIMPLE_FILE_SYSTEM_PRIVATE)); + if (Private == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Private->FilePath = malloc (StrLen (This->ConfigString) + 1); + if (Private->FilePath == NULL) { + free (Private); + return EFI_OUT_OF_RESOURCES; + } + + // Convert Unicode to Ascii + for (i = 0; This->ConfigString[i] != 0; i++) { + Private->FilePath[i] = This->ConfigString[i]; + } + Private->FilePath[i] = 0; + + + Private->VolumeLabel = malloc (StrSize (L"EFI_EMULATED")); + if (Private->VolumeLabel == NULL) { + free (Private->FilePath); + free (Private); + return EFI_OUT_OF_RESOURCES; + } + StrCpy (Private->VolumeLabel, L"EFI_EMULATED"); + + Private->Signature = EMU_SIMPLE_FILE_SYSTEM_PRIVATE_SIGNATURE; + Private->Thunk = This; + CopyMem (&Private->SimpleFileSystem, &gPosixFileSystemProtocol, sizeof (Private->SimpleFileSystem)); + Private->FileHandlesOpen = FALSE; + + This->Interface = &Private->SimpleFileSystem; + This->Private = Private; + return EFI_SUCCESS; +} + + +EFI_STATUS +PosixFileSystmeThunkClose ( + IN EMU_IO_THUNK_PROTOCOL *This + ) +{ + EMU_SIMPLE_FILE_SYSTEM_PRIVATE *Private; + + if (!CompareGuid (This->Protocol, &gEfiSimpleFileSystemProtocolGuid)) { + return EFI_UNSUPPORTED; + } + + Private = This->Private; + + if (Private->FileHandlesOpen) { + // + // Close only supported if all the EFI_FILE_HANDLEs have been closed. + // + return EFI_NOT_READY; + } + + if (This->Private != NULL) { + if (Private->VolumeLabel != NULL) { + free (Private->VolumeLabel); + } + free (This->Private); + This->Private = NULL; + } + + return EFI_SUCCESS; +} + + +EMU_IO_THUNK_PROTOCOL gPosixFileSystemThunkIo = { + &gEfiSimpleFileSystemProtocolGuid, + NULL, + NULL, + 0, + GasketPosixFileSystmeThunkOpen, + GasketPosixFileSystmeThunkClose, + NULL +}; + + |