mirror of https://github.com/acidanthera/audk.git
1132 lines
36 KiB
C
1132 lines
36 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/UefiBootServicesTableLib.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 <stdlib.h>
|
||
#include <string.h>
|
||
#include <wchar.h>
|
||
#include <sys/poll.h>
|
||
#include <sys/fcntl.h>
|
||
#include <sys/stat.h>
|
||
#include <sys/syslimits.h>
|
||
#include <Efi/SysEfi.h>
|
||
#include <kfile.h>
|
||
#include <Device/Device.h>
|
||
#include <MainData.h>
|
||
#include <extern.h> // Library/include/extern.h: Private to implementation
|
||
#include <sys/EfiSysCall.h>
|
||
|
||
/* EFI versions of BSD system calls used in stdio */
|
||
|
||
/* 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
|
||
*/
|
||
BOOLEAN
|
||
ValidateFD( int fd, int IsOpen)
|
||
{
|
||
struct __filedes *filp;
|
||
BOOLEAN retval = FALSE;
|
||
|
||
if((fd >= 0) && (fd < OPEN_MAX)) {
|
||
filp = &gMD->fdarray[fd];
|
||
retval = TRUE;
|
||
if(IsOpen >= 0) {
|
||
retval = (BOOLEAN)((filp->f_iflags != 0) && // TRUE if OPEN
|
||
FILE_IS_USABLE(filp)); // and Usable (not Larval or Closing)
|
||
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.
|
||
*/
|
||
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].f_iflags == 0) {
|
||
Mfd[i].f_iflags = FIF_LARVAL; // Temporarily mark this fd as reserved
|
||
fd = i;
|
||
break;
|
||
}
|
||
}
|
||
return fd;
|
||
}
|
||
|
||
/* Mark that an open file is to be deleted when closed. */
|
||
int
|
||
DeleteOnClose(int fd)
|
||
{
|
||
int retval = 0;
|
||
|
||
if(ValidateFD( fd, VALID_OPEN)) {
|
||
gMD->fdarray[fd].f_iflags |= FIF_DELCLOSE;
|
||
}
|
||
else {
|
||
errno = EBADF;
|
||
retval = -1;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
/** 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 fd)
|
||
{
|
||
int retval = 0;
|
||
struct __filedes *Fp;
|
||
|
||
if(ValidateFD( fd, VALID_OPEN)) {
|
||
Fp = &gMD->fdarray[fd];
|
||
retval = Fp->f_iflags & _S_ITTY;
|
||
}
|
||
else {
|
||
errno = EBADF;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
static BOOLEAN
|
||
IsDupFd( int fd)
|
||
{
|
||
void * DevData;
|
||
const struct fileops *FileOps;
|
||
int i;
|
||
BOOLEAN Ret = FALSE;
|
||
|
||
if(ValidateFD( fd, VALID_OPEN )) {
|
||
FileOps = gMD->fdarray[fd].f_ops;
|
||
DevData = gMD->fdarray[fd].devdata;
|
||
for(i=0; i < OPEN_MAX; ++i) {
|
||
if(i == fd) continue;
|
||
if(ValidateFD( i, VALID_OPEN )) { // TRUE if fd is valid and OPEN
|
||
if((gMD->fdarray[i].f_ops == FileOps)
|
||
&&(gMD->fdarray[i].devdata == DevData )) {
|
||
Ret = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return Ret;
|
||
}
|
||
|
||
static int
|
||
_closeX (int fd, int NewState)
|
||
{
|
||
struct __filedes *Fp;
|
||
int retval = 0;
|
||
|
||
// Verify my pointers and get my FD.
|
||
if(ValidateFD( fd, VALID_OPEN )) {
|
||
Fp = &gMD->fdarray[fd];
|
||
// Check if there are other users of this FileHandle
|
||
if(Fp->RefCount == 1) { // There should be no other users
|
||
if(! IsDupFd(fd)) {
|
||
// Only do the close if no one else is using the FileHandle
|
||
if(Fp->f_iflags & FIF_DELCLOSE) {
|
||
/* Handle files marked "Delete on Close". */
|
||
if(Fp->f_ops->fo_delete != NULL) {
|
||
retval = Fp->f_ops->fo_delete(Fp);
|
||
}
|
||
}
|
||
else {
|
||
retval = Fp->f_ops->fo_close( Fp);
|
||
}
|
||
}
|
||
Fp->f_iflags = NewState; // Close this FD or reserve it
|
||
Fp->RefCount = 0; // No one using this FD
|
||
}
|
||
else {
|
||
--Fp->RefCount; /* One less user of this FD */
|
||
}
|
||
}
|
||
else {
|
||
// Bad FD
|
||
retval = -1;
|
||
errno = EBADF;
|
||
}
|
||
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);
|
||
}
|
||
|
||
/**
|
||
**/
|
||
int
|
||
unlink (const char *path)
|
||
{
|
||
struct __filedes *Fp;
|
||
int fd;
|
||
int retval = -1;
|
||
|
||
EFIerrno = RETURN_SUCCESS;
|
||
|
||
fd = open(path, O_WRONLY, 0);
|
||
if(fd >= 0) {
|
||
Fp = &gMD->fdarray[fd];
|
||
|
||
if(Fp->f_ops->fo_delete != NULL) {
|
||
retval = Fp->f_ops->fo_delete(Fp);
|
||
}
|
||
Fp->f_iflags = 0; // Close this FD
|
||
Fp->RefCount = 0; // No one using this FD
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
/** 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->f_iflags;
|
||
break;
|
||
//case F_SETOWN:
|
||
// retval = MyFd->SocProc;
|
||
// MyFd->SocProc = va_arg(p3, int);
|
||
// break;
|
||
case F_GETFD:
|
||
//retval = MyFd->Oflags;
|
||
retval = MyFd->f_iflags;
|
||
break;
|
||
case F_GETFL:
|
||
//retval = MyFd->f_iflags;
|
||
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].f_iflags = FIF_LARVAL; // Mark the file closed, but reserved
|
||
(void)memcpy(&gMD->fdarray[fildes2], // Duplicate fildes into fildes2
|
||
&gMD->fdarray[fildes], sizeof(struct __filedes));
|
||
gMD->fdarray[fildes2].MyFD = (UINT16)fildes2;
|
||
}
|
||
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 fd, __off_t offset, int how)
|
||
{
|
||
__off_t CurPos = -1;
|
||
// RETURN_STATUS Status = RETURN_SUCCESS;
|
||
struct __filedes *filp;
|
||
|
||
EFIerrno = RETURN_SUCCESS; // In case of error without an EFI call
|
||
|
||
if( how == SEEK_SET || how == SEEK_CUR || how == SEEK_END) {
|
||
if(ValidateFD( fd, VALID_OPEN)) {
|
||
filp = &gMD->fdarray[fd];
|
||
// Both of our parameters have been verified as valid
|
||
CurPos = filp->f_ops->fo_lseek( filp, offset, how);
|
||
if(CurPos >= 0) {
|
||
filp->f_offset = CurPos;
|
||
}
|
||
}
|
||
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 error codes are stored in errno and EFIerrno.
|
||
**/
|
||
int
|
||
mkdir (const char *path, __mode_t perms)
|
||
{
|
||
wchar_t *NewPath;
|
||
DeviceNode *Node;
|
||
char *GenI;
|
||
RETURN_STATUS Status;
|
||
int Instance = 0;
|
||
int retval = 0;
|
||
|
||
Status = ParsePath(path, &NewPath, &Node, &Instance);
|
||
if(Status == RETURN_SUCCESS) {
|
||
GenI = Node->InstanceList;
|
||
if(GenI == NULL) {
|
||
errno = EPERM;
|
||
retval = -1;
|
||
}
|
||
else {
|
||
GenI += (Instance * Node->InstanceSize);
|
||
retval = ((GenericInstance *)GenI)->Abstraction.fo_mkdir( path, perms);
|
||
}
|
||
free(NewPath);
|
||
}
|
||
else {
|
||
retval = -1;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
/** 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 *path, int oflags, int mode)
|
||
{
|
||
wchar_t *NewPath;
|
||
DeviceNode *Node;
|
||
char *GenI = NULL;
|
||
struct __filedes *filp;
|
||
int Instance = 0;
|
||
RETURN_STATUS Status;
|
||
UINT64 OpenMode;
|
||
int fd = -1;
|
||
int doresult;
|
||
|
||
Status = ParsePath(path, &NewPath, &Node, &Instance);
|
||
if(Status == RETURN_SUCCESS) {
|
||
if((Node != NULL) &&
|
||
((GenI = Node->InstanceList) == NULL)) {
|
||
errno = EPERM;
|
||
}
|
||
else {
|
||
// 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( VALID_CLOSED );
|
||
|
||
if( fd < 0 ) {
|
||
// All available FDs are in use
|
||
errno = EMFILE;
|
||
return -1;
|
||
}
|
||
filp = &gMD->fdarray[fd];
|
||
// Save the flags and mode in the File Descriptor
|
||
filp->Oflags = oflags;
|
||
filp->Omode = mode;
|
||
|
||
GenI += (Instance * Node->InstanceSize);
|
||
doresult = Node->OpenFunc(filp, GenI, NewPath, NULL);
|
||
if(doresult < 0) {
|
||
filp->f_iflags = 0; // Release this FD
|
||
fd = -1; // Indicate an error
|
||
}
|
||
else {
|
||
// Re-use OpenMode in order to build our final f_iflags value
|
||
OpenMode = ( mode & S_ACC_READ ) ? S_ACC_READ : 0;
|
||
OpenMode |= ( mode & S_ACC_WRITE ) ? S_ACC_WRITE : 0;
|
||
|
||
filp->f_iflags |= (UINT32)OpenMode;
|
||
++filp->RefCount;
|
||
FILE_SET_MATURE(filp);
|
||
}
|
||
}
|
||
free(NewPath);
|
||
}
|
||
// return the fd of our now open file
|
||
return fd;
|
||
}
|
||
|
||
|
||
/**
|
||
Poll a list of file descriptors.
|
||
|
||
The ::poll routine waits for up to timeout milliseconds for an event
|
||
to occur on one or more of the file descriptors listed. The event
|
||
types of interested are specified for each file descriptor in the events
|
||
field. The actual event detected is returned in the revents field of
|
||
the array. The
|
||
<a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html">POSIX</a>
|
||
documentation is available online.
|
||
|
||
@param [in] pfd Address of an array of pollfd structures.
|
||
|
||
@param [in] nfds Number of elements in the array of pollfd structures.
|
||
|
||
@param [in] timeout Length of time in milliseconds to wait for the event
|
||
|
||
@returns The number of file descriptors with detected events. Zero
|
||
indicates that the call timed out and -1 indicates an error.
|
||
|
||
**/
|
||
int
|
||
poll (
|
||
struct pollfd * pfd,
|
||
nfds_t nfds,
|
||
int timeout
|
||
)
|
||
{
|
||
struct __filedes * pDescriptor;
|
||
struct pollfd * pEnd;
|
||
struct pollfd * pPollFD;
|
||
int SelectedFDs;
|
||
EFI_STATUS Status;
|
||
EFI_EVENT Timer;
|
||
UINT64 TimerTicks;
|
||
|
||
//
|
||
// Create the timer for the timeout
|
||
//
|
||
Timer = NULL;
|
||
Status = EFI_SUCCESS;
|
||
if ( INFTIM != timeout ) {
|
||
Status = gBS->CreateEvent ( EVT_TIMER,
|
||
TPL_NOTIFY,
|
||
NULL,
|
||
NULL,
|
||
&Timer );
|
||
if ( !EFI_ERROR ( Status )) {
|
||
//
|
||
// Start the timeout timer
|
||
//
|
||
TimerTicks = timeout;
|
||
TimerTicks *= 1000 * 10;
|
||
Status = gBS->SetTimer ( Timer,
|
||
TimerRelative,
|
||
TimerTicks );
|
||
}
|
||
else {
|
||
SelectedFDs = -1;
|
||
errno = ENOMEM;
|
||
}
|
||
}
|
||
if ( !EFI_ERROR ( Status )) {
|
||
//
|
||
// Poll until an event is detected or the timer fires
|
||
//
|
||
SelectedFDs = 0;
|
||
errno = 0;
|
||
do {
|
||
//
|
||
// Poll the list of file descriptors
|
||
//
|
||
pPollFD = pfd;
|
||
pEnd = &pPollFD [ nfds ];
|
||
while ( pEnd > pPollFD ) {
|
||
//
|
||
// Validate the file descriptor
|
||
//
|
||
if ( !ValidateFD ( pPollFD->fd, VALID_OPEN )) {
|
||
errno = EINVAL;
|
||
return -1;
|
||
}
|
||
|
||
//
|
||
// Poll the device or file
|
||
//
|
||
pDescriptor = &gMD->fdarray [ pPollFD->fd ];
|
||
pPollFD->revents = pDescriptor->f_ops->fo_poll ( pDescriptor,
|
||
pPollFD->events );
|
||
|
||
//
|
||
// Determine if this file descriptor detected an event
|
||
//
|
||
if ( 0 != pPollFD->revents ) {
|
||
//
|
||
// Select this descriptor
|
||
//
|
||
SelectedFDs += 1;
|
||
}
|
||
|
||
//
|
||
// Set the next file descriptor
|
||
//
|
||
pPollFD += 1;
|
||
}
|
||
|
||
//
|
||
// Check for timeout
|
||
//
|
||
if ( NULL != Timer ) {
|
||
Status = gBS->CheckEvent ( Timer );
|
||
if ( EFI_SUCCESS == Status ) {
|
||
//
|
||
// Timeout
|
||
//
|
||
break;
|
||
}
|
||
else if ( EFI_NOT_READY == Status ) {
|
||
Status = EFI_SUCCESS;
|
||
}
|
||
}
|
||
} while (( 0 == SelectedFDs )
|
||
&& ( EFI_SUCCESS == Status ));
|
||
//
|
||
// Stop the timer
|
||
//
|
||
if ( NULL != Timer ) {
|
||
gBS->SetTimer ( Timer,
|
||
TimerCancel,
|
||
0 );
|
||
}
|
||
}
|
||
else {
|
||
SelectedFDs = -1;
|
||
errno = EAGAIN;
|
||
}
|
||
|
||
//
|
||
// Release the timer
|
||
//
|
||
if ( NULL != Timer ) {
|
||
gBS->CloseEvent ( Timer );
|
||
}
|
||
|
||
//
|
||
// Return the number of selected file system descriptors
|
||
//
|
||
return SelectedFDs;
|
||
}
|
||
|
||
|
||
|
||
/** 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
|
||
EFIAPI
|
||
rename(
|
||
const char *from,
|
||
const char *to
|
||
)
|
||
{
|
||
wchar_t *FromPath;
|
||
DeviceNode *FromNode;
|
||
char *GenI;
|
||
int Instance = 0;
|
||
RETURN_STATUS Status;
|
||
int retval = -1;
|
||
|
||
Status = ParsePath(from, &FromPath, &FromNode, &Instance);
|
||
if(Status == RETURN_SUCCESS) {
|
||
GenI = FromNode->InstanceList;
|
||
if(GenI == NULL) {
|
||
errno = EPERM;
|
||
retval = -1;
|
||
}
|
||
else {
|
||
GenI += (Instance * FromNode->InstanceSize);
|
||
retval = ((GenericInstance *)GenI)->Abstraction.fo_rename( from, to);
|
||
}
|
||
free(FromPath);
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
/**
|
||
**/
|
||
int
|
||
EFIAPI
|
||
rmdir(
|
||
const char *path
|
||
)
|
||
{
|
||
struct __filedes *filp;
|
||
int fd;
|
||
int retval = -1;
|
||
|
||
fd = open(path, O_RDWR, 0);
|
||
if(fd >= 0) {
|
||
filp = &gMD->fdarray[fd];
|
||
|
||
retval = filp->f_ops->fo_rmdir(filp);
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
/** 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] fd 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 fd, struct stat *statbuf)
|
||
{
|
||
int retval = -1;
|
||
struct __filedes *filp;
|
||
|
||
if(ValidateFD( fd, VALID_OPEN)) {
|
||
filp = &gMD->fdarray[fd];
|
||
retval = filp->f_ops->fo_stat(filp, statbuf, NULL);
|
||
}
|
||
else {
|
||
errno = EBADF;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
/** 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)
|
||
{
|
||
int fd;
|
||
int retval = -1;
|
||
struct __filedes *filp;
|
||
|
||
fd = open(path, O_RDONLY, 0);
|
||
if(fd >= 0) {
|
||
filp = &gMD->fdarray[fd];
|
||
retval = filp->f_ops->fo_stat( filp, statbuf, NULL);
|
||
close(fd);
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
/** Same as stat since EFI doesn't have symbolic links. **/
|
||
int
|
||
lstat (const char *path, struct stat *statbuf)
|
||
{
|
||
return stat(path, statbuf);
|
||
}
|
||
|
||
/** Control a device.
|
||
**/
|
||
int
|
||
ioctl(
|
||
int fd,
|
||
unsigned long request,
|
||
...
|
||
)
|
||
{
|
||
int retval = -1;
|
||
struct __filedes *filp;
|
||
va_list argp;
|
||
|
||
va_start(argp, request);
|
||
|
||
if(ValidateFD( fd, VALID_OPEN)) {
|
||
filp = &gMD->fdarray[fd];
|
||
retval = filp->f_ops->fo_ioctl(filp, request, argp);
|
||
}
|
||
else {
|
||
errno = EBADF;
|
||
}
|
||
va_end(argp);
|
||
|
||
return retval;
|
||
}
|
||
|
||
/** 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)
|
||
{
|
||
struct __filedes *filp;
|
||
ssize_t BufSize;
|
||
|
||
BufSize = (ssize_t)nbyte;
|
||
if(ValidateFD( fildes, VALID_OPEN)) {
|
||
filp = &gMD->fdarray[fildes];
|
||
|
||
BufSize = filp->f_ops->fo_read(filp, &filp->f_offset, nbyte, buf);
|
||
}
|
||
else {
|
||
errno = EBADF;
|
||
BufSize = -EBADF;
|
||
}
|
||
return BufSize;
|
||
}
|
||
|
||
/** 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 fd, const void *buf, size_t nbyte)
|
||
{
|
||
struct __filedes *filp;
|
||
ssize_t BufSize;
|
||
// EFI_FILE_HANDLE FileHandle;
|
||
// RETURN_STATUS Status = RETURN_SUCCESS;
|
||
|
||
BufSize = (ssize_t)nbyte;
|
||
|
||
if(ValidateFD( fd, VALID_OPEN)) {
|
||
filp = &gMD->fdarray[fd];
|
||
|
||
BufSize = filp->f_ops->fo_write(filp, &filp->f_offset, nbyte, buf);
|
||
}
|
||
else {
|
||
errno = EBADF;
|
||
BufSize = -EBADF;
|
||
}
|
||
return BufSize;
|
||
}
|
||
|
||
/** Gets the current working directory.
|
||
|
||
The getcwd() function shall place an absolute pathname of the current
|
||
working directory in the array pointed to by buf, and return buf. The
|
||
pathname copied to the array shall contain no components that are
|
||
symbolic links. The size argument is the size in bytes of the character
|
||
array pointed to by the buf argument.
|
||
|
||
@param[in,out] buf The buffer to fill.
|
||
@param[in] size The number of bytes in buffer.
|
||
|
||
@retval NULL The function failed.
|
||
@retval NULL Buf was NULL.
|
||
@retval NULL Size was 0.
|
||
@return buf The function completed successfully. See errno for info.
|
||
**/
|
||
char
|
||
*getcwd (char *buf, size_t size)
|
||
{
|
||
CONST CHAR16 *Cwd;
|
||
|
||
if (size == 0 || buf == NULL) {
|
||
errno = EINVAL;
|
||
return NULL;
|
||
}
|
||
|
||
Cwd = ShellGetCurrentDir(NULL);
|
||
if (Cwd == NULL) {
|
||
errno = EACCES;
|
||
return NULL;
|
||
}
|
||
if (size < ((StrLen (Cwd) + 1) * sizeof (CHAR8))) {
|
||
errno = ERANGE;
|
||
return (NULL);
|
||
}
|
||
|
||
return (UnicodeStrToAsciiStr(Cwd, buf));
|
||
}
|