mirror of https://github.com/acidanthera/audk.git
1199 lines
41 KiB
C
1199 lines
41 KiB
C
/** @file
|
||
EFI versions of NetBSD system calls.
|
||
|
||
Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
|
||
This program and the accompanying materials are licensed and made available under
|
||
the terms and conditions of the BSD License that accompanies this distribution.
|
||
The full text of the license may be found at
|
||
http://opensource.org/licenses/bsd-license.
|
||
|
||
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 <Uefi.h>
|
||
#include <Library/UefiLib.h>
|
||
#include <Library/BaseLib.h>
|
||
#include <Library/MemoryAllocationLib.h>
|
||
#include <Library/ShellLib.h>
|
||
|
||
#include <LibConfig.h>
|
||
#include <sys/EfiCdefs.h>
|
||
|
||
#include <sys/ansi.h>
|
||
#include <errno.h>
|
||
#include <stdarg.h>
|
||
#include <string.h>
|
||
#include <wchar.h>
|
||
#include <sys/fcntl.h>
|
||
#include <sys/stat.h>
|
||
#include <sys/syslimits.h>
|
||
#include "SysEfi.h"
|
||
#include <MainData.h>
|
||
#include <extern.h> // Library/include/extern.h: Private to implementation
|
||
#include <Efi/Console.h>
|
||
|
||
/* Macros only used in this file. */
|
||
// Parameters for the ValidateFD function.
|
||
#define VALID_OPEN 1
|
||
#define VALID_CLOSED 0
|
||
#define VALID_DONT_CARE -1
|
||
|
||
|
||
/* EFI versions of BSD system calls used in stdio */
|
||
|
||
/* Normalize path so that forward slashes are replaced with backslashes.
|
||
Backslashes are required for UEFI.
|
||
*/
|
||
static void
|
||
NormalizePath( const CHAR16 *path)
|
||
{
|
||
CHAR16 *temp;
|
||
|
||
for( temp = (CHAR16 *)path; *temp; ++temp) {
|
||
if(*temp == L'/') {
|
||
*temp = L'\\';
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Validate that fd refers to a valid file descriptor.
|
||
IsOpen is interpreted as follows:
|
||
- Positive fd must be OPEN
|
||
- Zero fd must be CLOSED
|
||
- Negative fd may be OPEN or CLOSED
|
||
|
||
@retval TRUE fd is VALID
|
||
@retval FALSE fd is INVALID
|
||
*/
|
||
static BOOLEAN
|
||
ValidateFD( int fd, int IsOpen)
|
||
{
|
||
BOOLEAN retval = FALSE;
|
||
|
||
if((fd >= 0) && (fd < OPEN_MAX)) {
|
||
retval = TRUE;
|
||
if(IsOpen >= 0) {
|
||
retval = (BOOLEAN)(gMD->fdarray[fd].State != 0); // TRUE if OPEN
|
||
if(IsOpen == VALID_CLOSED) {
|
||
retval = (BOOLEAN)!retval; // We want TRUE if CLOSED
|
||
}
|
||
}
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
/* Find and reserve a free File Descriptor.
|
||
|
||
Returns the first free File Descriptor greater than or equal to the,
|
||
already validated, fd specified by Minfd.
|
||
|
||
@return Returns -1 if there are no free FDs. Otherwise returns the
|
||
found fd.
|
||
*/
|
||
static int
|
||
FindFreeFD( int MinFd )
|
||
{
|
||
struct __filedes *Mfd;
|
||
int i;
|
||
int fd = -1;
|
||
|
||
Mfd = gMD->fdarray;
|
||
|
||
// Get an available fd
|
||
for(i=MinFd; i < OPEN_MAX; ++i) {
|
||
if(Mfd[i].State == 0) {
|
||
Mfd[i].State = S_ISYSTEM; // Temporarily mark this fd as reserved
|
||
fd = i;
|
||
break;
|
||
}
|
||
}
|
||
return fd;
|
||
}
|
||
|
||
/** The isatty() function tests whether fildes, an open file descriptor,
|
||
is associated with a terminal device.
|
||
|
||
@retval 1 fildes is associated with a terminal.
|
||
@retval 0 fildes is not associated with a terminal. errno is set to
|
||
EBADF if fildes is not a valid open FD.
|
||
**/
|
||
int
|
||
isatty (int fildes)
|
||
{
|
||
int retval = 0;
|
||
EFI_FILE_HANDLE FileHandle;
|
||
|
||
if(ValidateFD( fildes, VALID_OPEN)) {
|
||
FileHandle = gMD->fdarray[fildes].FileHandle;
|
||
retval = (FileHandle >= &gMD->StdIo[0].Abstraction) &&
|
||
(FileHandle <= &gMD->StdIo[2].Abstraction);
|
||
}
|
||
else {
|
||
errno = EBADF;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
static BOOLEAN
|
||
IsDupFd( int fd)
|
||
{
|
||
EFI_FILE_HANDLE FileHandle;
|
||
int i;
|
||
BOOLEAN Ret = FALSE;
|
||
|
||
if(ValidateFD( fd, VALID_OPEN )) {
|
||
FileHandle = gMD->fdarray[fd].FileHandle;
|
||
for(i=0; i < OPEN_MAX; ++i) {
|
||
if(i == fd) continue;
|
||
if(gMD->fdarray[i].State != 0) { // TRUE if fd is OPEN
|
||
if(gMD->fdarray[i].FileHandle == FileHandle) {
|
||
Ret = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return Ret;
|
||
}
|
||
|
||
static int
|
||
_closeX (int fd, int NewState)
|
||
{
|
||
struct __filedes *Mfd;
|
||
RETURN_STATUS Status;
|
||
int retval = 0;
|
||
|
||
Status = EFIerrno = RETURN_SUCCESS; // In case of error before the EFI call.
|
||
|
||
// Verify my pointers and get my FD.
|
||
if(ValidateFD( fd, VALID_OPEN )) {
|
||
Mfd = &gMD->fdarray[fd];
|
||
// Check if there are duplicates using this FileHandle
|
||
if(! IsDupFd(fd)) {
|
||
// Only do the close if no one else is using the FileHandle
|
||
if(isatty(fd)) {
|
||
Status = Mfd->FileHandle->Close( Mfd->FileHandle);
|
||
}
|
||
else {
|
||
Status = ShellCloseFile( (SHELL_FILE_HANDLE *)&Mfd->FileHandle);
|
||
}
|
||
}
|
||
Mfd->State = NewState; // Close this FD or reserve it
|
||
if(Status != RETURN_SUCCESS) {
|
||
errno = EFI2errno(Status);
|
||
EFIerrno = Status;
|
||
retval = -1;
|
||
}
|
||
}
|
||
else {
|
||
// Bad FD
|
||
errno = EBADF;
|
||
retval = -1;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
/** The close() function shall deallocate the file descriptor indicated by fd.
|
||
To deallocate means to make the file descriptor available for return by
|
||
subsequent calls to open() or other functions that allocate file
|
||
descriptors. All outstanding record locks owned by the process on the file
|
||
associated with the file descriptor shall be removed (that is, unlocked).
|
||
|
||
@return Upon successful completion, 0 shall be returned; otherwise,
|
||
-1 shall be returned and errno set to indicate the error.
|
||
**/
|
||
int
|
||
close (int fd)
|
||
{
|
||
//Print(L"Closing fd %d\n", fd);
|
||
return _closeX(fd, 0);
|
||
}
|
||
|
||
/* Wide character version of unlink */
|
||
int
|
||
Uunlink (const wchar_t *Path)
|
||
{
|
||
EFI_FILE_HANDLE FileHandle;
|
||
RETURN_STATUS Status;
|
||
|
||
EFIerrno = RETURN_SUCCESS;
|
||
|
||
NormalizePath( Path);
|
||
// We can only delete open files.
|
||
Status = ShellOpenFileByName( Path, (SHELL_FILE_HANDLE *)&FileHandle, 3, 0);
|
||
if(Status != RETURN_SUCCESS) {
|
||
errno = EFI2errno(Status);
|
||
EFIerrno = Status;
|
||
return -1;
|
||
}
|
||
Status = ShellDeleteFile( (SHELL_FILE_HANDLE *)&FileHandle);
|
||
if(Status != RETURN_SUCCESS) {
|
||
errno = EFI2errno(Status);
|
||
EFIerrno = Status;
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
**/
|
||
int
|
||
unlink (const char *path)
|
||
{
|
||
// Convert path from MBCS to WCS
|
||
(void)AsciiStrToUnicodeStr( path, gMD->UString);
|
||
|
||
return Uunlink(gMD->UString);
|
||
}
|
||
|
||
/** The fcntl() function shall perform the operations described below on open
|
||
files. The fildes argument is a file descriptor.
|
||
|
||
The available values for cmd are defined in <fcntl.h> and are as follows:
|
||
- F_DUPFD - Return a new file descriptor which shall be the lowest
|
||
numbered available (that is, not already open) file
|
||
descriptor greater than or equal to the third argument, arg,
|
||
taken as an integer of type int. The new file descriptor
|
||
shall refer to the same open file description as the original
|
||
file descriptor, and shall share any locks. The FD_CLOEXEC
|
||
flag associated with the new file descriptor shall be cleared
|
||
to keep the file open across calls to one of the exec functions.
|
||
- F_GETFD - Get the file descriptor flags defined in <fcntl.h> that are
|
||
associated with the file descriptor fildes. File descriptor
|
||
flags are associated with a single file descriptor and do not
|
||
affect other file descriptors that refer to the same file.
|
||
- F_SETFD - Set the file descriptor flags defined in <fcntl.h>, that are
|
||
associated with fildes, to the third argument, arg, taken
|
||
as type int. If the FD_CLOEXEC flag in the third argument
|
||
is 0, the file shall remain open across the exec
|
||
functions; otherwise, the file shall be closed upon
|
||
successful execution of one of the exec functions.
|
||
- F_GETFL - Get the file status flags and file access modes, defined in
|
||
<fcntl.h>, for the file description associated with fildes.
|
||
The file access modes can be extracted from the return
|
||
value using the mask O_ACCMODE, which is defined in
|
||
<fcntl.h>. File status flags and file access modes are
|
||
associated with the file description and do not affect
|
||
other file descriptors that refer to the same file with
|
||
different open file descriptions.
|
||
- F_SETFL - Set the file status flags, defined in <fcntl.h>, for the file
|
||
description associated with fildes from the corresponding
|
||
bits in the third argument, arg, taken as type int. Bits
|
||
corresponding to the file access mode and the file creation
|
||
flags, as defined in <fcntl.h>, that are set in arg shall
|
||
be ignored. If any bits in arg other than those mentioned
|
||
here are changed by the application, the result is unspecified.
|
||
- F_GETOWN - If fildes refers to a socket, get the process or process group
|
||
ID specified to receive SIGURG signals when out-of-band
|
||
data is available. Positive values indicate a process ID;
|
||
negative values, other than -1, indicate a process group
|
||
ID. If fildes does not refer to a socket, the results are
|
||
unspecified.
|
||
- F_SETOWN - If fildes refers to a socket, set the process or process
|
||
group ID specified to receive SIGURG signals when
|
||
out-of-band data is available, using the value of the third
|
||
argument, arg, taken as type int. Positive values indicate
|
||
a process ID; negative values, other than -1, indicate a
|
||
process group ID. If fildes does not refer to a socket, the
|
||
results are unspecified.
|
||
|
||
The fcntl() function shall fail if:
|
||
|
||
[EBADF] The fildes argument is not a valid open file descriptor.
|
||
[EINVAL] The cmd argument is invalid, or the cmd argument is F_DUPFD
|
||
and arg is negative or greater than or equal to {OPEN_MAX}.
|
||
[EMFILE] The argument cmd is F_DUPFD and {OPEN_MAX} file descriptors
|
||
are currently open in the calling process, or no file
|
||
descriptors greater than or equal to arg are available.
|
||
[EOVERFLOW] One of the values to be returned cannot be represented correctly.
|
||
|
||
@return Upon successful completion, the value returned shall depend on
|
||
cmd as follows:
|
||
- F_DUPFD - A new file descriptor.
|
||
- F_GETFD - Value of flags defined in <fcntl.h>. The return value
|
||
shall not be negative.
|
||
- F_SETFD - Value other than -1.
|
||
- F_GETFL - Value of file status flags and access modes. The return
|
||
value is not negative.
|
||
- F_SETFL - Value other than -1.
|
||
- F_GETOWN - Value of the socket owner process or process group;
|
||
this will not be -1.
|
||
- F_SETOWN - Value other than -1.
|
||
Otherwise, -1 shall be returned and errno set to indicate the error.
|
||
|
||
**/
|
||
int
|
||
fcntl (int fildes, int cmd, ...)
|
||
{
|
||
va_list p3;
|
||
struct __filedes *MyFd;
|
||
int retval = -1;
|
||
int temp;
|
||
|
||
//Print(L"%a( %d, %d, ...)\n", __func__, fildes, cmd);
|
||
va_start(p3, cmd);
|
||
|
||
if(ValidateFD( fildes, VALID_OPEN )) {
|
||
MyFd = &gMD->fdarray[fildes];
|
||
|
||
switch(cmd) {
|
||
case F_DUPFD:
|
||
temp = va_arg(p3, int);
|
||
if(ValidateFD( temp, VALID_DONT_CARE )) {
|
||
temp = FindFreeFD( temp );
|
||
if(temp < 0) {
|
||
errno = EMFILE;
|
||
break;
|
||
}
|
||
/* temp is now a valid fd reserved for further use
|
||
so copy fd into temp.
|
||
*/
|
||
(void)memcpy(&gMD->fdarray[temp], MyFd, sizeof(struct __filedes));
|
||
retval = temp;
|
||
}
|
||
else {
|
||
errno = EINVAL;
|
||
}
|
||
break;
|
||
//case F_SETFD:
|
||
case F_SETFL:
|
||
retval = MyFd->Oflags; // Get original value
|
||
temp = va_arg(p3, int);
|
||
temp &= O_SETMASK; // Only certain bits can be set
|
||
temp |= retval & O_SETMASK;
|
||
MyFd->Oflags = temp; // Set new value
|
||
break;
|
||
//case F_SETFL:
|
||
case F_SETFD:
|
||
retval = MyFd->State;
|
||
break;
|
||
case F_SETOWN:
|
||
retval = MyFd->SocProc;
|
||
MyFd->SocProc = va_arg(p3, int);
|
||
break;
|
||
case F_GETFD:
|
||
//retval = MyFd->Oflags;
|
||
retval = MyFd->State;
|
||
break;
|
||
case F_GETFL:
|
||
//retval = MyFd->State;
|
||
retval = MyFd->Oflags;
|
||
break;
|
||
case F_GETOWN:
|
||
retval = MyFd->SocProc;
|
||
break;
|
||
default:
|
||
errno = EINVAL;
|
||
break;
|
||
}
|
||
}
|
||
else {
|
||
// Bad FD
|
||
errno = EBADF;
|
||
}
|
||
va_end(p3);
|
||
return retval;;
|
||
}
|
||
|
||
/** The dup() function provides an alternative interface to the
|
||
service provided by fcntl() using the F_DUPFD command. The call:
|
||
- fid = dup(fildes);
|
||
shall be equivalent to:
|
||
- fid = fcntl(fildes, F_DUPFD, 0);
|
||
|
||
@return Upon successful completion a non-negative integer, namely the
|
||
file descriptor, shall be returned; otherwise, -1 shall be
|
||
returned and errno set to indicate the error.
|
||
**/
|
||
int
|
||
dup (int fildes)
|
||
{
|
||
return fcntl(fildes, F_DUPFD, 0);
|
||
}
|
||
|
||
/** The dup2() function provides an alternative interface to the
|
||
service provided by fcntl() using the F_DUPFD command. The call:
|
||
- fid = dup2(fildes, fildes2);
|
||
shall be equivalent to:
|
||
- close(fildes2);
|
||
- fid = fcntl(fildes, F_DUPFD, fildes2);
|
||
except for the following:
|
||
- If fildes2 is less than 0 or greater than or equal to {OPEN_MAX},
|
||
dup2() shall return -1 with errno set to [EBADF].
|
||
- If fildes is a valid file descriptor and is equal to fildes2, dup2()
|
||
shall return fildes2 without closing it.
|
||
- If fildes is not a valid file descriptor, dup2() shall return -1 and
|
||
shall not close fildes2.
|
||
- The value returned shall be equal to the value of fildes2 upon
|
||
successful completion, or -1 upon failure.
|
||
|
||
@return Upon successful completion a non-negative integer, namely
|
||
fildes2, shall be returned; otherwise, -1 shall be
|
||
returned and errno set to EBADF indicate the error.
|
||
**/
|
||
int
|
||
dup2 (int fildes, int fildes2)
|
||
{
|
||
int retval = -1;
|
||
|
||
if(ValidateFD( fildes, VALID_OPEN)) {
|
||
retval = fildes2;
|
||
if( fildes != fildes2) {
|
||
if(ValidateFD( fildes2, VALID_DONT_CARE)) {
|
||
gMD->fdarray[fildes2].State = S_ISYSTEM; // Mark the file closed, but reserved
|
||
(void)memcpy(&gMD->fdarray[fildes2], // Duplicate fildes into fildes2
|
||
&gMD->fdarray[fildes], sizeof(struct __filedes));
|
||
}
|
||
else {
|
||
errno = EBADF;
|
||
retval = -1;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
errno = EBADF;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
/** Reposition a file's read/write offset.
|
||
|
||
The lseek() function repositions the offset of the file descriptor fildes
|
||
to the argument offset according to the directive how. The argument
|
||
fildes must be an open file descriptor. lseek() repositions the file
|
||
pointer fildes as follows:
|
||
|
||
If how is SEEK_SET, the offset is set to offset bytes.
|
||
|
||
If how is SEEK_CUR, the offset is set to its current location
|
||
plus offset bytes.
|
||
|
||
If how is SEEK_END, the offset is set to the size of the file
|
||
plus offset bytes.
|
||
|
||
The lseek() function allows the file offset to be set beyond the end of
|
||
the existing end-of-file of the file. If data is later written at this
|
||
point, subsequent reads of the data in the gap return bytes of zeros
|
||
(until data is actually written into the gap).
|
||
|
||
Some devices are incapable of seeking. The value of the pointer associ-
|
||
ated with such a device is undefined.
|
||
|
||
@return Upon successful completion, lseek() returns the resulting offset
|
||
location as measured in bytes from the beginning of the file.
|
||
Otherwise, a value of -1 is returned and errno is set to
|
||
indicate the error.
|
||
**/
|
||
__off_t
|
||
lseek (int fildes, __off_t offset, int how)
|
||
{
|
||
__off_t CurPos = -1;
|
||
RETURN_STATUS Status = RETURN_SUCCESS;
|
||
EFI_FILE_HANDLE FileHandle;
|
||
|
||
EFIerrno = RETURN_SUCCESS; // In case of error without an EFI call
|
||
|
||
if( how == SEEK_SET || how == SEEK_CUR || how == SEEK_END) {
|
||
if(ValidateFD( fildes, VALID_OPEN)) {
|
||
// Both of our parameters have been verified as valid
|
||
FileHandle = gMD->fdarray[fildes].FileHandle;
|
||
CurPos = 0;
|
||
if(isatty(fildes)) {
|
||
Status = FileHandle->SetPosition( FileHandle, offset);
|
||
CurPos = offset;
|
||
}
|
||
else {
|
||
if(how != SEEK_SET) {
|
||
// We are doing a relative seek
|
||
if(how == SEEK_END) {
|
||
// seeking relative to EOF, so position there first.
|
||
Status = ShellSetFilePosition( (SHELL_FILE_HANDLE)FileHandle, 0xFFFFFFFFFFFFFFFFULL);
|
||
}
|
||
if(Status == RETURN_SUCCESS) {
|
||
// Now, determine our current position.
|
||
Status = ShellGetFilePosition( (SHELL_FILE_HANDLE)FileHandle, (UINT64 *)&CurPos);
|
||
}
|
||
}
|
||
if(Status == RETURN_SUCCESS) {
|
||
/* CurPos now indicates the point we are seeking from, so seek... */
|
||
Status = ShellSetFilePosition( (SHELL_FILE_HANDLE)FileHandle, (UINT64)(CurPos + offset));
|
||
if(Status == RETURN_SUCCESS) {
|
||
// Now, determine our final position.
|
||
Status = ShellGetFilePosition( (SHELL_FILE_HANDLE)FileHandle, (UINT64 *)&CurPos);
|
||
}
|
||
}
|
||
if(Status != RETURN_SUCCESS) {
|
||
EFIerrno = Status;
|
||
CurPos = -1;
|
||
if(Status == EFI_UNSUPPORTED) {
|
||
errno = EISDIR;
|
||
}
|
||
else {
|
||
errno = EFI2errno(Status);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
errno = EBADF; // Bad File Descriptor
|
||
}
|
||
}
|
||
else {
|
||
errno = EINVAL; // Invalid how argument
|
||
}
|
||
return CurPos;
|
||
}
|
||
|
||
/** The directory path is created with the access permissions specified by
|
||
perms.
|
||
|
||
The directory is closed after it is created.
|
||
|
||
@retval 0 The directory was created successfully.
|
||
@retval -1 An error occurred and an error code is stored in errno.
|
||
**/
|
||
int
|
||
mkdir (const char *path, __mode_t perms)
|
||
{
|
||
EFI_FILE_HANDLE FileHandle;
|
||
RETURN_STATUS Status;
|
||
EFI_FILE_INFO *FileInfo;
|
||
|
||
// Convert name from MBCS to WCS
|
||
(void)AsciiStrToUnicodeStr( path, gMD->UString);
|
||
NormalizePath( gMD->UString);
|
||
|
||
//Print(L"%a( \"%s\", 0x%8X)\n", __func__, gMD->UString, perms);
|
||
Status = ShellCreateDirectory( gMD->UString, (SHELL_FILE_HANDLE *)&FileHandle);
|
||
if(Status == RETURN_SUCCESS) {
|
||
FileInfo = ShellGetFileInfo( FileHandle);
|
||
if(FileInfo != NULL) {
|
||
FileInfo->Attribute = Omode2EFI(perms);
|
||
Status = ShellSetFileInfo( FileHandle, FileInfo);
|
||
FreePool(FileInfo);
|
||
if(Status == RETURN_SUCCESS) {
|
||
(void)ShellCloseFile((SHELL_FILE_HANDLE *)&FileHandle);
|
||
return 0;
|
||
}
|
||
}
|
||
}
|
||
errno = EFI2errno(Status);
|
||
EFIerrno = Status;
|
||
|
||
return -1;
|
||
}
|
||
|
||
/** Open a file.
|
||
|
||
The EFI ShellOpenFileByName() function is used to perform the low-level
|
||
file open operation. The primary task of open() is to translate from the
|
||
flags used in the <stdio.h> environment to those used by the EFI function.
|
||
|
||
The only valid flag combinations for ShellOpenFileByName() are:
|
||
- Read
|
||
- Read/Write
|
||
- Create/Read/Write
|
||
|
||
The mode value is saved in the FD to indicate permissions for further operations.
|
||
|
||
O_RDONLY -- flags = EFI_FILE_MODE_READ -- this is always done
|
||
O_WRONLY -- flags |= EFI_FILE_MODE_WRITE
|
||
O_RDWR -- flags |= EFI_FILE_MODE_WRITE -- READ is already set
|
||
|
||
O_NONBLOCK -- ignored
|
||
O_APPEND -- Seek to EOF before every write
|
||
O_CREAT -- flags |= EFI_FILE_MODE_CREATE
|
||
O_TRUNC -- delete first then create new
|
||
O_EXCL -- if O_CREAT is also set, open will fail if the file already exists.
|
||
**/
|
||
int
|
||
open (const char *name, int oflags, int mode)
|
||
{
|
||
EFI_FILE_HANDLE FileHandle;
|
||
struct __filedes *Mfd;
|
||
RETURN_STATUS Status;
|
||
UINT64 OpenMode;
|
||
UINT64 Attributes;
|
||
int fd = -1;
|
||
UINT32 NewState;
|
||
|
||
EFIerrno = RETURN_SUCCESS;
|
||
Mfd = gMD->fdarray;
|
||
|
||
// Convert name from MBCS to WCS
|
||
(void)AsciiStrToUnicodeStr( name, gMD->UString);
|
||
NormalizePath( gMD->UString);
|
||
|
||
// Convert oflags to Attributes
|
||
OpenMode = Oflags2EFI(oflags);
|
||
if(OpenMode == 0) {
|
||
errno = EINVAL;
|
||
return -1;
|
||
}
|
||
|
||
//Attributes = Omode2EFI(mode);
|
||
Attributes = 0;
|
||
|
||
// Could add a test to see if the file name begins with a period.
|
||
// If it does, then add the HIDDEN flag to Attributes.
|
||
|
||
// Get an available fd
|
||
fd = FindFreeFD( 0 );
|
||
|
||
if( fd < 0 ) {
|
||
// All available FDs are in use
|
||
errno = EMFILE;
|
||
return -1;
|
||
}
|
||
|
||
Status = ConOpen( NULL, &FileHandle, gMD->UString, OpenMode, Attributes);
|
||
if(Status == RETURN_NO_MAPPING) {
|
||
// Not a console device, how about a regular file device?
|
||
|
||
/* Do we care if the file already exists?
|
||
If O_TRUNC, then delete the file. It will be created anew subsequently.
|
||
If O_EXCL, then error if the file exists and O_CREAT is set.
|
||
|
||
!!!!!!!!! Change this to use ShellSetFileInfo() to actually truncate the file
|
||
!!!!!!!!! instead of deleting and re-creating it.
|
||
*/
|
||
if((oflags & O_TRUNC) || ((oflags & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT))) {
|
||
Status = ShellIsFile( gMD->UString );
|
||
if(Status == RETURN_SUCCESS) {
|
||
// The file exists
|
||
if(oflags & O_TRUNC) {
|
||
// We do a truncate by deleting the existing file and creating a new one.
|
||
if(Uunlink(gMD->UString) != 0) {
|
||
Mfd[fd].State = 0; // Release our reservation on this FD
|
||
return -1; // errno and EFIerrno are already set.
|
||
}
|
||
}
|
||
else if(oflags & (O_EXCL | O_CREAT)) {
|
||
errno = EEXIST;
|
||
EFIerrno = Status;
|
||
Mfd[fd].State = 0; // Release our reservation on this FD
|
||
return -1;
|
||
}
|
||
}
|
||
}
|
||
// Call the EFI Shell's Open function
|
||
Status = ShellOpenFileByName( gMD->UString, (SHELL_FILE_HANDLE *)&FileHandle, OpenMode, Attributes);
|
||
if(RETURN_ERROR(Status)) {
|
||
Mfd[fd].State = 0; // Release our reservation on this FD
|
||
// Set errno based upon Status
|
||
errno = EFI2errno(Status);
|
||
EFIerrno = Status;
|
||
return -1;
|
||
}
|
||
// Successfully got a regular File
|
||
NewState = S_IFREG;
|
||
}
|
||
else if(Status != RETURN_SUCCESS) {
|
||
// Set errno based upon Status
|
||
errno = EFI2errno(Status);
|
||
EFIerrno = Status;
|
||
return -1;
|
||
}
|
||
else {
|
||
// Succesfully got a Console stream
|
||
NewState = S_IFREG | _S_ITTY | _S_IFCHR;
|
||
}
|
||
|
||
// Update the info in the fd
|
||
Mfd[fd].FileHandle = FileHandle;
|
||
Mfd[fd].Oflags = oflags;
|
||
Mfd[fd].Omode = mode;
|
||
|
||
// Re-use OpenMode in order to build our final State value
|
||
OpenMode = ( mode & S_ACC_READ ) ? S_ACC_READ : 0;
|
||
OpenMode |= ( mode & S_ACC_WRITE ) ? S_ACC_WRITE : 0;
|
||
|
||
Mfd[fd].State = NewState | (UINT32)OpenMode;
|
||
|
||
// return the fd of our now open file
|
||
return fd;
|
||
}
|
||
|
||
/** The rename() function changes the name of a file.
|
||
The old argument points to the pathname of the file to be renamed. The new
|
||
argument points to the new pathname of the file.
|
||
|
||
If the old argument points to the pathname of a file that is not a
|
||
directory, the new argument shall not point to the pathname of a
|
||
directory. If the file named by the new argument exists, it shall be
|
||
removed and old renamed to new. Write access permission is required for
|
||
both the directory containing old and the directory containing new.
|
||
|
||
If the old argument points to the pathname of a directory, the new
|
||
argument shall not point to the pathname of a file that is not a
|
||
directory. If the directory named by the new argument exists, it shall be
|
||
removed and old renamed to new.
|
||
|
||
The new pathname shall not contain a path prefix that names old. Write
|
||
access permission is required for the directory containing old and the
|
||
directory containing new. If the old argument points to the pathname of a
|
||
directory, write access permission may be required for the directory named
|
||
by old, and, if it exists, the directory named by new.
|
||
|
||
If the rename() function fails for any reason other than [EIO], any file
|
||
named by new shall be unaffected.
|
||
|
||
@return Upon successful completion, rename() shall return 0; otherwise,
|
||
-1 shall be returned, errno shall be set to indicate the error,
|
||
and neither the file named by old nor the file named by new
|
||
shall be changed or created.
|
||
**/
|
||
int
|
||
rename (const char *old, const char *new)
|
||
{
|
||
// UINT64 InfoSize;
|
||
// RETURN_STATUS Status;
|
||
// EFI_FILE_INFO *NewFileInfo = NULL;
|
||
// EFI_FILE_INFO *OldFileInfo;
|
||
// char *Newfn;
|
||
// int OldFd;
|
||
|
||
//// Open old file
|
||
// OldFd = open(old, O_RDONLY, 0);
|
||
// if(OldFd >= 0) {
|
||
// NewFileInfo = malloc(sizeof(EFI_FILE_INFO) + PATH_MAX);
|
||
// if(NewFileInfo != NULL) {
|
||
// OldFileInfo = ShellGetFileInfo( FileHandle);
|
||
// if(OldFileInfo != NULL) {
|
||
// // Copy the Old file info into our new buffer, and free the old.
|
||
// memcpy(OldFileInfo, NewFileInfo, sizeof(EFI_FILE_INFO));
|
||
// FreePool(OldFileInfo);
|
||
// // Strip off all but the file name portion of new
|
||
// NewFn = strrchr(new, '/');
|
||
// if(NewFn == NULL) {
|
||
// NewFn = strrchr(new '\\');
|
||
// if(NewFn == NULL) {
|
||
// NewFn = new;
|
||
// }
|
||
// }
|
||
// // Convert new name from MBCS to WCS
|
||
// (void)AsciiStrToUnicodeStr( NewFn, gMD->UString);
|
||
// // Copy the new file name into our new file info buffer
|
||
// wcsncpy(NewFileInfo->FileName, gMD->UString, wcslen(gMD->UString)+1);
|
||
// // Apply the new file name
|
||
// Status = ShellSetFileInfo(FileHandle);
|
||
// if(Status == EFI_SUCCESS) {
|
||
// // File has been successfully renamed. We are DONE!
|
||
// return 0;
|
||
// }
|
||
// errno = EFI2errno( Status );
|
||
// EFIerrno = Status;
|
||
// }
|
||
// else {
|
||
// errno = EIO;
|
||
// }
|
||
// }
|
||
// else {
|
||
// errno = ENOMEM;
|
||
// }
|
||
// }
|
||
return -1;
|
||
}
|
||
|
||
/**
|
||
**/
|
||
int
|
||
rmdir (const char *path)
|
||
{
|
||
EFI_FILE_HANDLE FileHandle;
|
||
RETURN_STATUS Status;
|
||
EFI_FILE_INFO *FileInfo = NULL;
|
||
int Count = 0;
|
||
BOOLEAN NoFile = FALSE;
|
||
|
||
errno = 0; // Make it easier to see if we have an error later
|
||
|
||
// Convert name from MBCS to WCS
|
||
(void)AsciiStrToUnicodeStr( path, gMD->UString);
|
||
NormalizePath( gMD->UString);
|
||
|
||
//Print(L"%a( \"%s\")\n", __func__, gMD->UString);
|
||
Status = ShellOpenFileByName( gMD->UString, (SHELL_FILE_HANDLE *)&FileHandle,
|
||
(EFI_FILE_MODE_READ || EFI_FILE_MODE_WRITE), 0);
|
||
if(Status == RETURN_SUCCESS) {
|
||
FileInfo = ShellGetFileInfo( (SHELL_FILE_HANDLE)FileHandle);
|
||
if(FileInfo != NULL) {
|
||
if((FileInfo->Attribute & EFI_FILE_DIRECTORY) == 0) {
|
||
errno = ENOTDIR;
|
||
}
|
||
else {
|
||
// See if the directory has any entries other than ".." and ".".
|
||
FreePool(FileInfo); // Free up the buffer from ShellGetFileInfo()
|
||
Status = ShellFindFirstFile( (SHELL_FILE_HANDLE)FileHandle, &FileInfo);
|
||
if(Status == RETURN_SUCCESS) {
|
||
++Count;
|
||
while(Count < 3) {
|
||
Status = ShellFindNextFile( (SHELL_FILE_HANDLE)FileHandle, FileInfo, &NoFile);
|
||
if(Status == RETURN_SUCCESS) {
|
||
if(NoFile) {
|
||
break;
|
||
}
|
||
++Count;
|
||
}
|
||
else {
|
||
Count = 99;
|
||
}
|
||
}
|
||
FreePool(FileInfo); // Free buffer from ShellFindFirstFile()
|
||
if(Count < 3) {
|
||
// Directory is empty
|
||
Status = ShellDeleteFile( (SHELL_FILE_HANDLE *)&FileHandle);
|
||
if(Status == RETURN_SUCCESS) {
|
||
EFIerrno = RETURN_SUCCESS;
|
||
return 0;
|
||
/* ######## SUCCESSFUL RETURN ######## */
|
||
}
|
||
}
|
||
else {
|
||
if(Count == 99) {
|
||
errno = EIO;
|
||
}
|
||
else {
|
||
errno = ENOTEMPTY;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
errno = EIO;
|
||
}
|
||
}
|
||
EFIerrno = Status;
|
||
if(errno == 0) {
|
||
errno = EFI2errno( Status );
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/* Internal File Info. worker function for stat and fstat. */
|
||
static
|
||
EFI_STATUS
|
||
_EFI_FileInfo( EFI_FILE_INFO *FileInfo, struct stat *statbuf)
|
||
{
|
||
UINT64 Attributes;
|
||
RETURN_STATUS Status;
|
||
mode_t newmode;
|
||
|
||
if(FileInfo != NULL) {
|
||
// Got the info, now populate statbuf with it
|
||
statbuf->st_blksize = S_BLKSIZE;
|
||
statbuf->st_size = FileInfo->Size;
|
||
statbuf->st_physsize = FileInfo->PhysicalSize;
|
||
statbuf->st_birthtime = Efi2Time( &FileInfo->CreateTime);
|
||
statbuf->st_atime = Efi2Time( &FileInfo->LastAccessTime);
|
||
statbuf->st_mtime = Efi2Time( &FileInfo->ModificationTime);
|
||
Attributes = FileInfo->Attribute;
|
||
newmode = (mode_t)(Attributes << S_EFISHIFT) | S_ACC_READ;
|
||
if((Attributes & EFI_FILE_DIRECTORY) == 0) {
|
||
newmode |= _S_IFREG;
|
||
if((Attributes & EFI_FILE_READ_ONLY) == 0) {
|
||
statbuf->st_mode |= S_ACC_WRITE;
|
||
}
|
||
}
|
||
else {
|
||
newmode |= _S_IFDIR;
|
||
}
|
||
statbuf->st_mode = newmode;
|
||
Status = RETURN_SUCCESS;
|
||
}
|
||
else {
|
||
Status = RETURN_DEVICE_ERROR;
|
||
}
|
||
return Status;
|
||
}
|
||
|
||
/** The fstat() function obtains information about an open file associated
|
||
with the file descriptor fildes, and shall write it to the area pointed to
|
||
by buf.
|
||
|
||
The buf argument is a pointer to a stat structure, as defined
|
||
in <sys/stat.h>, into which information is placed concerning the file.
|
||
|
||
The structure members st_mode, st_ino, st_dev, st_uid, st_gid, st_atime,
|
||
st_ctime, and st_mtime shall have meaningful values. The value of the
|
||
member st_nlink shall be set to the number of links to the file.
|
||
|
||
The fstat() function shall update any time-related fields before writing
|
||
into the stat structure.
|
||
|
||
The fstat() function is implemented using the ShellGetFileInfo()
|
||
function.
|
||
|
||
The stat structure members which don't have direct analogs to EFI file
|
||
information are filled in as follows:
|
||
- st_mode Populated with information from fildes
|
||
- st_ino Set to zero. (inode)
|
||
- st_dev Set to zero.
|
||
- st_uid Set to zero.
|
||
- st_gid Set to zero.
|
||
- st_nlink Set to one.
|
||
|
||
@param[in] fildes File descriptor as returned from open().
|
||
@param[out] statbuf Buffer in which the file status is put.
|
||
|
||
@retval 0 Successful Completion.
|
||
@retval -1 An error has occurred and errno has been set to
|
||
identify the error.
|
||
**/
|
||
int
|
||
fstat (int fildes, struct stat *statbuf)
|
||
{
|
||
EFI_FILE_HANDLE FileHandle;
|
||
RETURN_STATUS Status = RETURN_SUCCESS;
|
||
EFI_FILE_INFO *FileInfo = NULL;
|
||
UINTN FinfoSize = sizeof(EFI_FILE_INFO);
|
||
|
||
if(ValidateFD( fildes, VALID_OPEN)) {
|
||
FileHandle = gMD->fdarray[fildes].FileHandle;
|
||
if(isatty(fildes)) {
|
||
FileInfo = AllocateZeroPool(FinfoSize);
|
||
if(FileInfo != NULL) {
|
||
Status = FileHandle->GetInfo( FileHandle, 0, &FinfoSize, FileInfo);
|
||
}
|
||
else {
|
||
Status = RETURN_OUT_OF_RESOURCES;
|
||
}
|
||
}
|
||
else {
|
||
FileInfo = ShellGetFileInfo( FileHandle);
|
||
}
|
||
Status = _EFI_FileInfo( FileInfo, statbuf);
|
||
}
|
||
errno = EFI2errno(Status);
|
||
EFIerrno = Status;
|
||
|
||
if(FileInfo != NULL) {
|
||
FreePool(FileInfo); // Release the buffer allocated by the GetInfo function
|
||
}
|
||
|
||
return errno? -1 : 0;
|
||
}
|
||
|
||
/** Obtains information about the file pointed to by path.
|
||
|
||
Opens the file pointed to by path, calls _EFI_FileInfo with the file's handle,
|
||
then closes the file.
|
||
|
||
@retval 0 Successful Completion.
|
||
@retval -1 An error has occurred and errno has been set to
|
||
identify the error.
|
||
**/
|
||
int
|
||
stat (const char *path, void *statbuf)
|
||
{
|
||
EFI_FILE_HANDLE FileHandle;
|
||
RETURN_STATUS Status;
|
||
EFI_FILE_INFO *FileInfo;
|
||
|
||
errno = 0; // Make it easier to see if we have an error later
|
||
|
||
// Convert name from MBCS to WCS
|
||
(void)AsciiStrToUnicodeStr( path, gMD->UString);
|
||
NormalizePath( gMD->UString);
|
||
|
||
Status = ShellOpenFileByName( gMD->UString, (SHELL_FILE_HANDLE *)&FileHandle, EFI_FILE_MODE_READ, 0ULL);
|
||
if(Status == RETURN_SUCCESS) {
|
||
FileInfo = ShellGetFileInfo( FileHandle);
|
||
Status = _EFI_FileInfo( FileInfo, (struct stat *)statbuf);
|
||
(void)ShellCloseFile( (SHELL_FILE_HANDLE *)&FileHandle);
|
||
}
|
||
errno = EFI2errno(Status);
|
||
EFIerrno = Status;
|
||
|
||
return errno? -1 : 0;
|
||
}
|
||
|
||
/** Same as stat since EFI doesn't have symbolic links. **/
|
||
int
|
||
lstat (const char *path, struct stat *statbuf)
|
||
{
|
||
return stat(path, statbuf);
|
||
}
|
||
|
||
/** Read from a file.
|
||
|
||
The read() function shall attempt to read nbyte bytes from the file
|
||
associated with the open file descriptor, fildes, into the buffer pointed
|
||
to by buf.
|
||
|
||
Before any action described below is taken, and if nbyte is zero, the
|
||
read() function may detect and return errors as described below. In the
|
||
absence of errors, or if error detection is not performed, the read()
|
||
function shall return zero and have no other results.
|
||
|
||
On files that support seeking (for example, a regular file), the read()
|
||
shall start at a position in the file given by the file offset associated
|
||
with fildes. The file offset shall be incremented by the number of bytes
|
||
actually read.
|
||
|
||
Files that do not support seeking - for example, terminals - always read
|
||
from the current position. The value of a file offset associated with
|
||
such a file is undefined.
|
||
|
||
No data transfer shall occur past the current end-of-file. If the
|
||
starting position is at or after the end-of-file, 0 shall be returned.
|
||
|
||
The read() function reads data previously written to a file. If any
|
||
portion of a regular file prior to the end-of-file has not been written,
|
||
read() shall return bytes with value 0. For example, lseek() allows the
|
||
file offset to be set beyond the end of existing data in the file. If data
|
||
is later written at this point, subsequent reads in the gap between the
|
||
previous end of data and the newly written data shall return bytes with
|
||
value 0 until data is written into the gap.
|
||
|
||
Upon successful completion, where nbyte is greater than 0, read() shall
|
||
mark for update the st_atime field of the file, and shall return the
|
||
number of bytes read. This number shall never be greater than nbyte. The
|
||
value returned may be less than nbyte if the number of bytes left in the
|
||
file is less than nbyte, if the read() request was interrupted by a
|
||
signal, or if the file is a pipe or FIFO or special file and has fewer
|
||
than nbyte bytes immediately available for reading. For example, a read()
|
||
from a file associated with a terminal may return one typed line of data.
|
||
|
||
If fildes does not refer to a directory, the function reads the requested
|
||
number of bytes from the file at the file<6C>s current position and returns
|
||
them in buf. If the read goes beyond the end of the file, the read
|
||
length is truncated to the end of the file. The file<6C>s current position is
|
||
increased by the number of bytes returned.
|
||
|
||
If fildes refers to a directory, the function reads the directory entry at
|
||
the file<6C>s current position and returns the entry in buf. If buf
|
||
is not large enough to hold the current directory entry, then
|
||
errno is set to EBUFSIZE, EFIerrno is set to EFI_BUFFER_TOO_SMALL, and the
|
||
current file position is not updated. The size of the buffer needed to read
|
||
the entry will be returned as a negative number. On success, the current
|
||
position is updated to the next directory entry. If there are no more
|
||
directory entries, the read returns a zero-length buffer.
|
||
EFI_FILE_INFO is the structure returned as the directory entry.
|
||
|
||
@return Upon successful completion, read() returns a non-negative integer
|
||
indicating the number of bytes actually read. Otherwise, the
|
||
functions return a negative value and sets errno to indicate the
|
||
error. If errno is EBUFSIZE, the absolute value of the
|
||
return value indicates the size of the buffer needed to read
|
||
the directory entry.
|
||
**/
|
||
ssize_t
|
||
read (int fildes, void *buf, size_t nbyte)
|
||
{
|
||
ssize_t BufSize;
|
||
EFI_FILE_HANDLE FileHandle;
|
||
RETURN_STATUS Status;
|
||
|
||
BufSize = (ssize_t)nbyte;
|
||
if(ValidateFD( fildes, VALID_OPEN)) {
|
||
FileHandle = gMD->fdarray[fildes].FileHandle;
|
||
if(isatty(fildes)) {
|
||
Status = FileHandle->Read( FileHandle, (UINTN *)&BufSize, buf);
|
||
}
|
||
else {
|
||
Status = ShellReadFile( FileHandle, (UINTN *)&BufSize, buf);
|
||
}
|
||
if(Status != RETURN_SUCCESS) {
|
||
EFIerrno = Status;
|
||
errno = EFI2errno(Status);
|
||
if(Status == RETURN_BUFFER_TOO_SMALL) {
|
||
BufSize = -BufSize;
|
||
}
|
||
else {
|
||
BufSize = -1;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
errno = EBADF;
|
||
}
|
||
return BufSize;
|
||
}
|
||
|
||
ssize_t
|
||
WideTtyCvt( CHAR16 *dest, const char *buf, size_t n)
|
||
{
|
||
UINTN i;
|
||
wint_t wc;
|
||
|
||
for(i = 0; i < n; ++i) {
|
||
wc = btowc(*buf++);
|
||
if( wc == 0) {
|
||
break;
|
||
};
|
||
if(wc < 0) {
|
||
wc = BLOCKELEMENT_LIGHT_SHADE;
|
||
}
|
||
if(wc == L'\n') {
|
||
*dest++ = L'\r';
|
||
}
|
||
*dest++ = (CHAR16)wc;
|
||
}
|
||
*dest = 0;
|
||
return (ssize_t)i;
|
||
}
|
||
|
||
/** Write data to a file.
|
||
|
||
This function writes the specified number of bytes to the file at the current
|
||
file position. The current file position is advanced the actual number of bytes
|
||
written, which is returned in BufferSize. Partial writes only occur when there
|
||
has been a data error during the write attempt (such as "volume space full").
|
||
The file is automatically grown to hold the data if required. Direct writes to
|
||
opened directories are not supported.
|
||
|
||
If fildes refers to a terminal device, isatty() returns TRUE, a partial write
|
||
will occur if a NULL or EOF character is encountered before n characters have
|
||
been written. Characters inserted due to line-end translations will not be
|
||
counted. Unconvertable characters are translated into the UEFI character
|
||
BLOCKELEMENT_LIGHT_SHADE.
|
||
|
||
Since the UEFI console device works on wide characters, the buffer is assumed
|
||
to contain a single-byte character stream which is then translated to wide
|
||
characters using the btowc() functions. The resulting wide character stream
|
||
is what is actually sent to the UEFI console.
|
||
|
||
QUESTION: Should writes to stdout or stderr always succeed?
|
||
**/
|
||
ssize_t
|
||
write (int fildes, const void *buf, size_t n)
|
||
{
|
||
ssize_t BufSize;
|
||
EFI_FILE_HANDLE FileHandle;
|
||
RETURN_STATUS Status = RETURN_SUCCESS;
|
||
ssize_t UniBufSz;
|
||
|
||
BufSize = (ssize_t)n;
|
||
|
||
if(ValidateFD( fildes, VALID_OPEN)) {
|
||
FileHandle = gMD->fdarray[fildes].FileHandle;
|
||
if(isatty(fildes)) {
|
||
// Convert string from MBCS to WCS and translate \n to \r\n.
|
||
UniBufSz = WideTtyCvt(gMD->UString, (const char *)buf, n);
|
||
if(UniBufSz > 0) {
|
||
BufSize = (ssize_t)(UniBufSz * sizeof(CHAR16));
|
||
Status = FileHandle->Write( FileHandle, (UINTN *)&BufSize, (void *)gMD->UString);
|
||
BufSize = (ssize_t)n; // Always pretend all was output
|
||
}
|
||
}
|
||
else {
|
||
Status = ShellWriteFile( FileHandle, (UINTN *)&BufSize, (void *)buf);
|
||
}
|
||
if(Status != RETURN_SUCCESS) {
|
||
EFIerrno = Status;
|
||
errno = EFI2errno(Status);
|
||
if(Status == EFI_UNSUPPORTED) {
|
||
errno = EISDIR;
|
||
}
|
||
BufSize = -1;
|
||
}
|
||
}
|
||
else {
|
||
errno = EBADF;
|
||
}
|
||
return BufSize;
|
||
}
|