/*++ @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 | |
}; |