| /*++ @file | |
| POSIX Pthreads to emulate APs and implement threads | |
| Copyright (c) 2011, Apple Inc. All rights reserved. | |
| Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "Host.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; | |
| } | |
| AsciiStrCpyS ( | |
| PrivateFile->FileName, | |
| AsciiStrSize (Private->FilePath), | |
| 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 / 60; | |
| 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; | |
| UINTN Size; | |
| 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 | |
| // | |
| TrailingDash = FALSE; | |
| 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; | |
| } | |
| 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)); | |
| Size = AsciiStrSize (PrivateFile->FileName) + 1 + StrLen (FileName) + 1; | |
| NewPrivateFile->FileName = malloc (Size); | |
| if (NewPrivateFile->FileName == NULL) { | |
| goto Done; | |
| } | |
| if (*FileName == L'\\') { | |
| AsciiStrCpyS (NewPrivateFile->FileName, Size, PrivateRoot->FilePath); | |
| // Skip first '\'. | |
| Src = FileName + 1; | |
| } else { | |
| AsciiStrCpyS (NewPrivateFile->FileName, Size, 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; | |
| UINTN FullFileNameSize; | |
| 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; | |
| FullFileNameSize = AsciiStrLen (PrivateFile->FileName) + 1 + NameSize; | |
| FullFileName = malloc (FullFileNameSize); | |
| if (FullFileName == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto Done; | |
| } | |
| AsciiStrCpyS (FullFileName, FullFileNameSize, PrivateFile->FileName); | |
| AsciiStrCatS (FullFileName, FullFileNameSize, "/"); | |
| AsciiStrCatS (FullFileName, FullFileNameSize, 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; | |
| StrCpyS ( | |
| (CHAR16 *)FileSystemInfoBuffer->VolumeLabel, | |
| (*BufferSize - SIZE_OF_EFI_FILE_SYSTEM_INFO) / sizeof (CHAR16), | |
| 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; | |
| } | |
| StrCpyS ( | |
| (CHAR16 *)Buffer, | |
| *BufferSize / sizeof (CHAR16), | |
| 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; | |
| UINTN Size; | |
| 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; | |
| } | |
| StrCpyS ( | |
| PrivateRoot->VolumeLabel, | |
| StrSize (NewFileSystemInfo->VolumeLabel) / sizeof (CHAR16), | |
| 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; | |
| } | |
| StrCpyS ( | |
| PrivateRoot->VolumeLabel, | |
| StrSize (PrivateRoot->VolumeLabel) / sizeof (CHAR16), | |
| (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 (OldFileName == NULL) { | |
| goto Done; | |
| } | |
| AsciiStrCpyS ( | |
| OldFileName, | |
| AsciiStrSize (PrivateFile->FileName), | |
| PrivateFile->FileName | |
| ); | |
| // | |
| // Make full pathname from new filename and rootpath. | |
| // | |
| if (NewFileInfo->FileName[0] == '\\') { | |
| Size = AsciiStrLen (PrivateRoot->FilePath) + 1 + StrLen (NewFileInfo->FileName) + 1; | |
| NewFileName = malloc (Size); | |
| if (NewFileName == NULL) { | |
| goto Done; | |
| } | |
| AsciiStrCpyS (NewFileName, Size, PrivateRoot->FilePath); | |
| AsciiFilePtr = NewFileName + AsciiStrLen (NewFileName); | |
| UnicodeFilePtr = NewFileInfo->FileName + 1; | |
| *AsciiFilePtr++ = '/'; | |
| } else { | |
| Size = AsciiStrLen (PrivateFile->FileName) + 2 + StrLen (NewFileInfo->FileName) + 1; | |
| NewFileName = malloc (Size); | |
| if (NewFileName == NULL) { | |
| goto Done; | |
| } | |
| AsciiStrCpyS (NewFileName, Size, 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; | |
| } | |
| AsciiStrCpyS ( | |
| PrivateFile->FileName, | |
| AsciiStrSize (NewFileName), | |
| 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; | |
| } | |
| StrCpyS ( | |
| Private->VolumeLabel, | |
| StrSize (L"EFI_EMULATED") / sizeof (CHAR16), | |
| 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 | |
| }; |