]> git.proxmox.com Git - mirror_edk2.git/blob - StdLib/LibC/Uefi/SysCalls.c
624d45878e166e78aa218cf545c01f244125b18b
[mirror_edk2.git] / StdLib / LibC / Uefi / SysCalls.c
1 /** @file
2 EFI versions of NetBSD system calls.
3
4 Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials are licensed and made available under
6 the terms and conditions of the BSD License that accompanies this distribution.
7 The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14 #include <Uefi.h>
15 #include <Library/UefiLib.h>
16 #include <Library/BaseLib.h>
17 #include <Library/MemoryAllocationLib.h>
18 #include <Library/ShellLib.h>
19
20 #include <LibConfig.h>
21 #include <sys/EfiCdefs.h>
22
23 #include <sys/ansi.h>
24 #include <errno.h>
25 #include <stdarg.h>
26 #include <string.h>
27 #include <wchar.h>
28 #include <sys/fcntl.h>
29 #include <sys/stat.h>
30 #include <sys/syslimits.h>
31 #include "SysEfi.h"
32 #include <MainData.h>
33 #include <extern.h> // Library/include/extern.h: Private to implementation
34 #include <Efi/Console.h>
35
36 /* Macros only used in this file. */
37 // Parameters for the ValidateFD function.
38 #define VALID_OPEN 1
39 #define VALID_CLOSED 0
40 #define VALID_DONT_CARE -1
41
42
43 /* EFI versions of BSD system calls used in stdio */
44
45 /* Normalize path so that forward slashes are replaced with backslashes.
46 Backslashes are required for UEFI.
47 */
48 static void
49 NormalizePath( const CHAR16 *path)
50 {
51 CHAR16 *temp;
52
53 for( temp = (CHAR16 *)path; *temp; ++temp) {
54 if(*temp == L'/') {
55 *temp = L'\\';
56 }
57 }
58 }
59
60 /* Validate that fd refers to a valid file descriptor.
61 IsOpen is interpreted as follows:
62 - Positive fd must be OPEN
63 - Zero fd must be CLOSED
64 - Negative fd may be OPEN or CLOSED
65
66 @retval TRUE fd is VALID
67 @retval FALSE fd is INVALID
68 */
69 static BOOLEAN
70 ValidateFD( int fd, int IsOpen)
71 {
72 BOOLEAN retval = FALSE;
73
74 if((fd >= 0) && (fd < OPEN_MAX)) {
75 retval = TRUE;
76 if(IsOpen >= 0) {
77 retval = (BOOLEAN)(gMD->fdarray[fd].State != 0); // TRUE if OPEN
78 if(IsOpen == VALID_CLOSED) {
79 retval = (BOOLEAN)!retval; // We want TRUE if CLOSED
80 }
81 }
82 }
83 return retval;
84 }
85
86 /* Find and reserve a free File Descriptor.
87
88 Returns the first free File Descriptor greater than or equal to the,
89 already validated, fd specified by Minfd.
90
91 @return Returns -1 if there are no free FDs. Otherwise returns the
92 found fd.
93 */
94 static int
95 FindFreeFD( int MinFd )
96 {
97 struct __filedes *Mfd;
98 int i;
99 int fd = -1;
100
101 Mfd = gMD->fdarray;
102
103 // Get an available fd
104 for(i=MinFd; i < OPEN_MAX; ++i) {
105 if(Mfd[i].State == 0) {
106 Mfd[i].State = S_ISYSTEM; // Temporarily mark this fd as reserved
107 fd = i;
108 break;
109 }
110 }
111 return fd;
112 }
113
114 /** The isatty() function tests whether fildes, an open file descriptor,
115 is associated with a terminal device.
116
117 @retval 1 fildes is associated with a terminal.
118 @retval 0 fildes is not associated with a terminal. errno is set to
119 EBADF if fildes is not a valid open FD.
120 **/
121 int
122 isatty (int fildes)
123 {
124 int retval = 0;
125 EFI_FILE_HANDLE FileHandle;
126
127 if(ValidateFD( fildes, VALID_OPEN)) {
128 FileHandle = gMD->fdarray[fildes].FileHandle;
129 retval = (FileHandle >= &gMD->StdIo[0].Abstraction) &&
130 (FileHandle <= &gMD->StdIo[2].Abstraction);
131 }
132 else {
133 errno = EBADF;
134 }
135 return retval;
136 }
137
138 static BOOLEAN
139 IsDupFd( int fd)
140 {
141 EFI_FILE_HANDLE FileHandle;
142 int i;
143 BOOLEAN Ret = FALSE;
144
145 if(ValidateFD( fd, VALID_OPEN )) {
146 FileHandle = gMD->fdarray[fd].FileHandle;
147 for(i=0; i < OPEN_MAX; ++i) {
148 if(i == fd) continue;
149 if(gMD->fdarray[i].State != 0) { // TRUE if fd is OPEN
150 if(gMD->fdarray[i].FileHandle == FileHandle) {
151 Ret = TRUE;
152 break;
153 }
154 }
155 }
156 }
157 return Ret;
158 }
159
160 static int
161 _closeX (int fd, int NewState)
162 {
163 struct __filedes *Mfd;
164 RETURN_STATUS Status;
165 int retval = 0;
166
167 Status = EFIerrno = RETURN_SUCCESS; // In case of error before the EFI call.
168
169 // Verify my pointers and get my FD.
170 if(ValidateFD( fd, VALID_OPEN )) {
171 Mfd = &gMD->fdarray[fd];
172 // Check if there are duplicates using this FileHandle
173 if(! IsDupFd(fd)) {
174 // Only do the close if no one else is using the FileHandle
175 if(isatty(fd)) {
176 Status = Mfd->FileHandle->Close( Mfd->FileHandle);
177 }
178 else {
179 Status = ShellCloseFile( (SHELL_FILE_HANDLE *)&Mfd->FileHandle);
180 }
181 }
182 Mfd->State = NewState; // Close this FD or reserve it
183 if(Status != RETURN_SUCCESS) {
184 errno = EFI2errno(Status);
185 EFIerrno = Status;
186 retval = -1;
187 }
188 }
189 else {
190 // Bad FD
191 errno = EBADF;
192 retval = -1;
193 }
194 return retval;
195 }
196
197 /** The close() function shall deallocate the file descriptor indicated by fd.
198 To deallocate means to make the file descriptor available for return by
199 subsequent calls to open() or other functions that allocate file
200 descriptors. All outstanding record locks owned by the process on the file
201 associated with the file descriptor shall be removed (that is, unlocked).
202
203 @return Upon successful completion, 0 shall be returned; otherwise,
204 -1 shall be returned and errno set to indicate the error.
205 **/
206 int
207 close (int fd)
208 {
209 //Print(L"Closing fd %d\n", fd);
210 return _closeX(fd, 0);
211 }
212
213 /* Wide character version of unlink */
214 int
215 Uunlink (const wchar_t *Path)
216 {
217 EFI_FILE_HANDLE FileHandle;
218 RETURN_STATUS Status;
219
220 EFIerrno = RETURN_SUCCESS;
221
222 NormalizePath( Path);
223 // We can only delete open files.
224 Status = ShellOpenFileByName( Path, (SHELL_FILE_HANDLE *)&FileHandle, 3, 0);
225 if(Status != RETURN_SUCCESS) {
226 errno = EFI2errno(Status);
227 EFIerrno = Status;
228 return -1;
229 }
230 Status = ShellDeleteFile( (SHELL_FILE_HANDLE *)&FileHandle);
231 if(Status != RETURN_SUCCESS) {
232 errno = EFI2errno(Status);
233 EFIerrno = Status;
234 return -1;
235 }
236 return 0;
237 }
238
239 /**
240 **/
241 int
242 unlink (const char *path)
243 {
244 // Convert path from MBCS to WCS
245 (void)AsciiStrToUnicodeStr( path, gMD->UString);
246
247 return Uunlink(gMD->UString);
248 }
249
250 /** The fcntl() function shall perform the operations described below on open
251 files. The fildes argument is a file descriptor.
252
253 The available values for cmd are defined in <fcntl.h> and are as follows:
254 - F_DUPFD - Return a new file descriptor which shall be the lowest
255 numbered available (that is, not already open) file
256 descriptor greater than or equal to the third argument, arg,
257 taken as an integer of type int. The new file descriptor
258 shall refer to the same open file description as the original
259 file descriptor, and shall share any locks. The FD_CLOEXEC
260 flag associated with the new file descriptor shall be cleared
261 to keep the file open across calls to one of the exec functions.
262 - F_GETFD - Get the file descriptor flags defined in <fcntl.h> that are
263 associated with the file descriptor fildes. File descriptor
264 flags are associated with a single file descriptor and do not
265 affect other file descriptors that refer to the same file.
266 - F_SETFD - Set the file descriptor flags defined in <fcntl.h>, that are
267 associated with fildes, to the third argument, arg, taken
268 as type int. If the FD_CLOEXEC flag in the third argument
269 is 0, the file shall remain open across the exec
270 functions; otherwise, the file shall be closed upon
271 successful execution of one of the exec functions.
272 - F_GETFL - Get the file status flags and file access modes, defined in
273 <fcntl.h>, for the file description associated with fildes.
274 The file access modes can be extracted from the return
275 value using the mask O_ACCMODE, which is defined in
276 <fcntl.h>. File status flags and file access modes are
277 associated with the file description and do not affect
278 other file descriptors that refer to the same file with
279 different open file descriptions.
280 - F_SETFL - Set the file status flags, defined in <fcntl.h>, for the file
281 description associated with fildes from the corresponding
282 bits in the third argument, arg, taken as type int. Bits
283 corresponding to the file access mode and the file creation
284 flags, as defined in <fcntl.h>, that are set in arg shall
285 be ignored. If any bits in arg other than those mentioned
286 here are changed by the application, the result is unspecified.
287 - F_GETOWN - If fildes refers to a socket, get the process or process group
288 ID specified to receive SIGURG signals when out-of-band
289 data is available. Positive values indicate a process ID;
290 negative values, other than -1, indicate a process group
291 ID. If fildes does not refer to a socket, the results are
292 unspecified.
293 - F_SETOWN - If fildes refers to a socket, set the process or process
294 group ID specified to receive SIGURG signals when
295 out-of-band data is available, using the value of the third
296 argument, arg, taken as type int. Positive values indicate
297 a process ID; negative values, other than -1, indicate a
298 process group ID. If fildes does not refer to a socket, the
299 results are unspecified.
300
301 The fcntl() function shall fail if:
302
303 [EBADF] The fildes argument is not a valid open file descriptor.
304 [EINVAL] The cmd argument is invalid, or the cmd argument is F_DUPFD
305 and arg is negative or greater than or equal to {OPEN_MAX}.
306 [EMFILE] The argument cmd is F_DUPFD and {OPEN_MAX} file descriptors
307 are currently open in the calling process, or no file
308 descriptors greater than or equal to arg are available.
309 [EOVERFLOW] One of the values to be returned cannot be represented correctly.
310
311 @return Upon successful completion, the value returned shall depend on
312 cmd as follows:
313 - F_DUPFD - A new file descriptor.
314 - F_GETFD - Value of flags defined in <fcntl.h>. The return value
315 shall not be negative.
316 - F_SETFD - Value other than -1.
317 - F_GETFL - Value of file status flags and access modes. The return
318 value is not negative.
319 - F_SETFL - Value other than -1.
320 - F_GETOWN - Value of the socket owner process or process group;
321 this will not be -1.
322 - F_SETOWN - Value other than -1.
323 Otherwise, -1 shall be returned and errno set to indicate the error.
324
325 **/
326 int
327 fcntl (int fildes, int cmd, ...)
328 {
329 va_list p3;
330 struct __filedes *MyFd;
331 int retval = -1;
332 int temp;
333
334 //Print(L"%a( %d, %d, ...)\n", __func__, fildes, cmd);
335 va_start(p3, cmd);
336
337 if(ValidateFD( fildes, VALID_OPEN )) {
338 MyFd = &gMD->fdarray[fildes];
339
340 switch(cmd) {
341 case F_DUPFD:
342 temp = va_arg(p3, int);
343 if(ValidateFD( temp, VALID_DONT_CARE )) {
344 temp = FindFreeFD( temp );
345 if(temp < 0) {
346 errno = EMFILE;
347 break;
348 }
349 /* temp is now a valid fd reserved for further use
350 so copy fd into temp.
351 */
352 (void)memcpy(&gMD->fdarray[temp], MyFd, sizeof(struct __filedes));
353 retval = temp;
354 }
355 else {
356 errno = EINVAL;
357 }
358 break;
359 //case F_SETFD:
360 case F_SETFL:
361 retval = MyFd->Oflags; // Get original value
362 temp = va_arg(p3, int);
363 temp &= O_SETMASK; // Only certain bits can be set
364 temp |= retval & O_SETMASK;
365 MyFd->Oflags = temp; // Set new value
366 break;
367 //case F_SETFL:
368 case F_SETFD:
369 retval = MyFd->State;
370 break;
371 case F_SETOWN:
372 retval = MyFd->SocProc;
373 MyFd->SocProc = va_arg(p3, int);
374 break;
375 case F_GETFD:
376 //retval = MyFd->Oflags;
377 retval = MyFd->State;
378 break;
379 case F_GETFL:
380 //retval = MyFd->State;
381 retval = MyFd->Oflags;
382 break;
383 case F_GETOWN:
384 retval = MyFd->SocProc;
385 break;
386 default:
387 errno = EINVAL;
388 break;
389 }
390 }
391 else {
392 // Bad FD
393 errno = EBADF;
394 }
395 va_end(p3);
396 return retval;;
397 }
398
399 /** The dup() function provides an alternative interface to the
400 service provided by fcntl() using the F_DUPFD command. The call:
401 - fid = dup(fildes);
402 shall be equivalent to:
403 - fid = fcntl(fildes, F_DUPFD, 0);
404
405 @return Upon successful completion a non-negative integer, namely the
406 file descriptor, shall be returned; otherwise, -1 shall be
407 returned and errno set to indicate the error.
408 **/
409 int
410 dup (int fildes)
411 {
412 return fcntl(fildes, F_DUPFD, 0);
413 }
414
415 /** The dup2() function provides an alternative interface to the
416 service provided by fcntl() using the F_DUPFD command. The call:
417 - fid = dup2(fildes, fildes2);
418 shall be equivalent to:
419 - close(fildes2);
420 - fid = fcntl(fildes, F_DUPFD, fildes2);
421 except for the following:
422 - If fildes2 is less than 0 or greater than or equal to {OPEN_MAX},
423 dup2() shall return -1 with errno set to [EBADF].
424 - If fildes is a valid file descriptor and is equal to fildes2, dup2()
425 shall return fildes2 without closing it.
426 - If fildes is not a valid file descriptor, dup2() shall return -1 and
427 shall not close fildes2.
428 - The value returned shall be equal to the value of fildes2 upon
429 successful completion, or -1 upon failure.
430
431 @return Upon successful completion a non-negative integer, namely
432 fildes2, shall be returned; otherwise, -1 shall be
433 returned and errno set to EBADF indicate the error.
434 **/
435 int
436 dup2 (int fildes, int fildes2)
437 {
438 int retval = -1;
439
440 if(ValidateFD( fildes, VALID_OPEN)) {
441 retval = fildes2;
442 if( fildes != fildes2) {
443 if(ValidateFD( fildes2, VALID_DONT_CARE)) {
444 gMD->fdarray[fildes2].State = S_ISYSTEM; // Mark the file closed, but reserved
445 (void)memcpy(&gMD->fdarray[fildes2], // Duplicate fildes into fildes2
446 &gMD->fdarray[fildes], sizeof(struct __filedes));
447 }
448 else {
449 errno = EBADF;
450 retval = -1;
451 }
452 }
453 }
454 else {
455 errno = EBADF;
456 }
457 return retval;
458 }
459
460 /** Reposition a file's read/write offset.
461
462 The lseek() function repositions the offset of the file descriptor fildes
463 to the argument offset according to the directive how. The argument
464 fildes must be an open file descriptor. lseek() repositions the file
465 pointer fildes as follows:
466
467 If how is SEEK_SET, the offset is set to offset bytes.
468
469 If how is SEEK_CUR, the offset is set to its current location
470 plus offset bytes.
471
472 If how is SEEK_END, the offset is set to the size of the file
473 plus offset bytes.
474
475 The lseek() function allows the file offset to be set beyond the end of
476 the existing end-of-file of the file. If data is later written at this
477 point, subsequent reads of the data in the gap return bytes of zeros
478 (until data is actually written into the gap).
479
480 Some devices are incapable of seeking. The value of the pointer associ-
481 ated with such a device is undefined.
482
483 @return Upon successful completion, lseek() returns the resulting offset
484 location as measured in bytes from the beginning of the file.
485 Otherwise, a value of -1 is returned and errno is set to
486 indicate the error.
487 **/
488 __off_t
489 lseek (int fildes, __off_t offset, int how)
490 {
491 __off_t CurPos = -1;
492 RETURN_STATUS Status = RETURN_SUCCESS;
493 EFI_FILE_HANDLE FileHandle;
494
495 EFIerrno = RETURN_SUCCESS; // In case of error without an EFI call
496
497 if( how == SEEK_SET || how == SEEK_CUR || how == SEEK_END) {
498 if(ValidateFD( fildes, VALID_OPEN)) {
499 // Both of our parameters have been verified as valid
500 FileHandle = gMD->fdarray[fildes].FileHandle;
501 CurPos = 0;
502 if(isatty(fildes)) {
503 Status = FileHandle->SetPosition( FileHandle, offset);
504 CurPos = offset;
505 }
506 else {
507 if(how != SEEK_SET) {
508 // We are doing a relative seek
509 if(how == SEEK_END) {
510 // seeking relative to EOF, so position there first.
511 Status = ShellSetFilePosition( (SHELL_FILE_HANDLE)FileHandle, 0xFFFFFFFFFFFFFFFFULL);
512 }
513 if(Status == RETURN_SUCCESS) {
514 // Now, determine our current position.
515 Status = ShellGetFilePosition( (SHELL_FILE_HANDLE)FileHandle, (UINT64 *)&CurPos);
516 }
517 }
518 if(Status == RETURN_SUCCESS) {
519 /* CurPos now indicates the point we are seeking from, so seek... */
520 Status = ShellSetFilePosition( (SHELL_FILE_HANDLE)FileHandle, (UINT64)(CurPos + offset));
521 if(Status == RETURN_SUCCESS) {
522 // Now, determine our final position.
523 Status = ShellGetFilePosition( (SHELL_FILE_HANDLE)FileHandle, (UINT64 *)&CurPos);
524 }
525 }
526 if(Status != RETURN_SUCCESS) {
527 EFIerrno = Status;
528 CurPos = -1;
529 if(Status == EFI_UNSUPPORTED) {
530 errno = EISDIR;
531 }
532 else {
533 errno = EFI2errno(Status);
534 }
535 }
536 }
537 }
538 else {
539 errno = EBADF; // Bad File Descriptor
540 }
541 }
542 else {
543 errno = EINVAL; // Invalid how argument
544 }
545 return CurPos;
546 }
547
548 /** The directory path is created with the access permissions specified by
549 perms.
550
551 The directory is closed after it is created.
552
553 @retval 0 The directory was created successfully.
554 @retval -1 An error occurred and an error code is stored in errno.
555 **/
556 int
557 mkdir (const char *path, __mode_t perms)
558 {
559 EFI_FILE_HANDLE FileHandle;
560 RETURN_STATUS Status;
561 EFI_FILE_INFO *FileInfo;
562
563 // Convert name from MBCS to WCS
564 (void)AsciiStrToUnicodeStr( path, gMD->UString);
565 NormalizePath( gMD->UString);
566
567 //Print(L"%a( \"%s\", 0x%8X)\n", __func__, gMD->UString, perms);
568 Status = ShellCreateDirectory( gMD->UString, (SHELL_FILE_HANDLE *)&FileHandle);
569 if(Status == RETURN_SUCCESS) {
570 FileInfo = ShellGetFileInfo( FileHandle);
571 if(FileInfo != NULL) {
572 FileInfo->Attribute = Omode2EFI(perms);
573 Status = ShellSetFileInfo( FileHandle, FileInfo);
574 FreePool(FileInfo);
575 if(Status == RETURN_SUCCESS) {
576 (void)ShellCloseFile((SHELL_FILE_HANDLE *)&FileHandle);
577 return 0;
578 }
579 }
580 }
581 errno = EFI2errno(Status);
582 EFIerrno = Status;
583
584 return -1;
585 }
586
587 /** Open a file.
588
589 The EFI ShellOpenFileByName() function is used to perform the low-level
590 file open operation. The primary task of open() is to translate from the
591 flags used in the <stdio.h> environment to those used by the EFI function.
592
593 The only valid flag combinations for ShellOpenFileByName() are:
594 - Read
595 - Read/Write
596 - Create/Read/Write
597
598 The mode value is saved in the FD to indicate permissions for further operations.
599
600 O_RDONLY -- flags = EFI_FILE_MODE_READ -- this is always done
601 O_WRONLY -- flags |= EFI_FILE_MODE_WRITE
602 O_RDWR -- flags |= EFI_FILE_MODE_WRITE -- READ is already set
603
604 O_NONBLOCK -- ignored
605 O_APPEND -- Seek to EOF before every write
606 O_CREAT -- flags |= EFI_FILE_MODE_CREATE
607 O_TRUNC -- delete first then create new
608 O_EXCL -- if O_CREAT is also set, open will fail if the file already exists.
609 **/
610 int
611 open (const char *name, int oflags, int mode)
612 {
613 EFI_FILE_HANDLE FileHandle;
614 struct __filedes *Mfd;
615 RETURN_STATUS Status;
616 UINT64 OpenMode;
617 UINT64 Attributes;
618 int fd = -1;
619 UINT32 NewState;
620
621 EFIerrno = RETURN_SUCCESS;
622 Mfd = gMD->fdarray;
623
624 // Convert name from MBCS to WCS
625 (void)AsciiStrToUnicodeStr( name, gMD->UString);
626 NormalizePath( gMD->UString);
627
628 // Convert oflags to Attributes
629 OpenMode = Oflags2EFI(oflags);
630 if(OpenMode == 0) {
631 errno = EINVAL;
632 return -1;
633 }
634
635 //Attributes = Omode2EFI(mode);
636 Attributes = 0;
637
638 // Could add a test to see if the file name begins with a period.
639 // If it does, then add the HIDDEN flag to Attributes.
640
641 // Get an available fd
642 fd = FindFreeFD( 0 );
643
644 if( fd < 0 ) {
645 // All available FDs are in use
646 errno = EMFILE;
647 return -1;
648 }
649
650 Status = ConOpen( NULL, &FileHandle, gMD->UString, OpenMode, Attributes);
651 if(Status == RETURN_NO_MAPPING) {
652 // Not a console device, how about a regular file device?
653
654 /* Do we care if the file already exists?
655 If O_TRUNC, then delete the file. It will be created anew subsequently.
656 If O_EXCL, then error if the file exists and O_CREAT is set.
657
658 !!!!!!!!! Change this to use ShellSetFileInfo() to actually truncate the file
659 !!!!!!!!! instead of deleting and re-creating it.
660 */
661 if((oflags & O_TRUNC) || ((oflags & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT))) {
662 Status = ShellIsFile( gMD->UString );
663 if(Status == RETURN_SUCCESS) {
664 // The file exists
665 if(oflags & O_TRUNC) {
666 // We do a truncate by deleting the existing file and creating a new one.
667 if(Uunlink(gMD->UString) != 0) {
668 Mfd[fd].State = 0; // Release our reservation on this FD
669 return -1; // errno and EFIerrno are already set.
670 }
671 }
672 else if(oflags & (O_EXCL | O_CREAT)) {
673 errno = EEXIST;
674 EFIerrno = Status;
675 Mfd[fd].State = 0; // Release our reservation on this FD
676 return -1;
677 }
678 }
679 }
680 // Call the EFI Shell's Open function
681 Status = ShellOpenFileByName( gMD->UString, (SHELL_FILE_HANDLE *)&FileHandle, OpenMode, Attributes);
682 if(RETURN_ERROR(Status)) {
683 Mfd[fd].State = 0; // Release our reservation on this FD
684 // Set errno based upon Status
685 errno = EFI2errno(Status);
686 EFIerrno = Status;
687 return -1;
688 }
689 // Successfully got a regular File
690 NewState = S_IFREG;
691 }
692 else if(Status != RETURN_SUCCESS) {
693 // Set errno based upon Status
694 errno = EFI2errno(Status);
695 EFIerrno = Status;
696 return -1;
697 }
698 else {
699 // Succesfully got a Console stream
700 NewState = S_IFREG | _S_ITTY | _S_IFCHR;
701 }
702
703 // Update the info in the fd
704 Mfd[fd].FileHandle = FileHandle;
705 Mfd[fd].Oflags = oflags;
706 Mfd[fd].Omode = mode;
707
708 // Re-use OpenMode in order to build our final State value
709 OpenMode = ( mode & S_ACC_READ ) ? S_ACC_READ : 0;
710 OpenMode |= ( mode & S_ACC_WRITE ) ? S_ACC_WRITE : 0;
711
712 Mfd[fd].State = NewState | (UINT32)OpenMode;
713
714 // return the fd of our now open file
715 return fd;
716 }
717
718 /** The rename() function changes the name of a file.
719 The old argument points to the pathname of the file to be renamed. The new
720 argument points to the new pathname of the file.
721
722 If the old argument points to the pathname of a file that is not a
723 directory, the new argument shall not point to the pathname of a
724 directory. If the file named by the new argument exists, it shall be
725 removed and old renamed to new. Write access permission is required for
726 both the directory containing old and the directory containing new.
727
728 If the old argument points to the pathname of a directory, the new
729 argument shall not point to the pathname of a file that is not a
730 directory. If the directory named by the new argument exists, it shall be
731 removed and old renamed to new.
732
733 The new pathname shall not contain a path prefix that names old. Write
734 access permission is required for the directory containing old and the
735 directory containing new. If the old argument points to the pathname of a
736 directory, write access permission may be required for the directory named
737 by old, and, if it exists, the directory named by new.
738
739 If the rename() function fails for any reason other than [EIO], any file
740 named by new shall be unaffected.
741
742 @return Upon successful completion, rename() shall return 0; otherwise,
743 -1 shall be returned, errno shall be set to indicate the error,
744 and neither the file named by old nor the file named by new
745 shall be changed or created.
746 **/
747 int
748 rename (const char *old, const char *new)
749 {
750 // UINT64 InfoSize;
751 // RETURN_STATUS Status;
752 // EFI_FILE_INFO *NewFileInfo = NULL;
753 // EFI_FILE_INFO *OldFileInfo;
754 // char *Newfn;
755 // int OldFd;
756
757 //// Open old file
758 // OldFd = open(old, O_RDONLY, 0);
759 // if(OldFd >= 0) {
760 // NewFileInfo = malloc(sizeof(EFI_FILE_INFO) + PATH_MAX);
761 // if(NewFileInfo != NULL) {
762 // OldFileInfo = ShellGetFileInfo( FileHandle);
763 // if(OldFileInfo != NULL) {
764 // // Copy the Old file info into our new buffer, and free the old.
765 // memcpy(OldFileInfo, NewFileInfo, sizeof(EFI_FILE_INFO));
766 // FreePool(OldFileInfo);
767 // // Strip off all but the file name portion of new
768 // NewFn = strrchr(new, '/');
769 // if(NewFn == NULL) {
770 // NewFn = strrchr(new '\\');
771 // if(NewFn == NULL) {
772 // NewFn = new;
773 // }
774 // }
775 // // Convert new name from MBCS to WCS
776 // (void)AsciiStrToUnicodeStr( NewFn, gMD->UString);
777 // // Copy the new file name into our new file info buffer
778 // wcsncpy(NewFileInfo->FileName, gMD->UString, wcslen(gMD->UString)+1);
779 // // Apply the new file name
780 // Status = ShellSetFileInfo(FileHandle);
781 // if(Status == EFI_SUCCESS) {
782 // // File has been successfully renamed. We are DONE!
783 // return 0;
784 // }
785 // errno = EFI2errno( Status );
786 // EFIerrno = Status;
787 // }
788 // else {
789 // errno = EIO;
790 // }
791 // }
792 // else {
793 // errno = ENOMEM;
794 // }
795 // }
796 return -1;
797 }
798
799 /**
800 **/
801 int
802 rmdir (const char *path)
803 {
804 EFI_FILE_HANDLE FileHandle;
805 RETURN_STATUS Status;
806 EFI_FILE_INFO *FileInfo = NULL;
807 int Count = 0;
808 BOOLEAN NoFile = FALSE;
809
810 errno = 0; // Make it easier to see if we have an error later
811
812 // Convert name from MBCS to WCS
813 (void)AsciiStrToUnicodeStr( path, gMD->UString);
814 NormalizePath( gMD->UString);
815
816 //Print(L"%a( \"%s\")\n", __func__, gMD->UString);
817 Status = ShellOpenFileByName( gMD->UString, (SHELL_FILE_HANDLE *)&FileHandle,
818 (EFI_FILE_MODE_READ || EFI_FILE_MODE_WRITE), 0);
819 if(Status == RETURN_SUCCESS) {
820 FileInfo = ShellGetFileInfo( (SHELL_FILE_HANDLE)FileHandle);
821 if(FileInfo != NULL) {
822 if((FileInfo->Attribute & EFI_FILE_DIRECTORY) == 0) {
823 errno = ENOTDIR;
824 }
825 else {
826 // See if the directory has any entries other than ".." and ".".
827 FreePool(FileInfo); // Free up the buffer from ShellGetFileInfo()
828 Status = ShellFindFirstFile( (SHELL_FILE_HANDLE)FileHandle, &FileInfo);
829 if(Status == RETURN_SUCCESS) {
830 ++Count;
831 while(Count < 3) {
832 Status = ShellFindNextFile( (SHELL_FILE_HANDLE)FileHandle, FileInfo, &NoFile);
833 if(Status == RETURN_SUCCESS) {
834 if(NoFile) {
835 break;
836 }
837 ++Count;
838 }
839 else {
840 Count = 99;
841 }
842 }
843 FreePool(FileInfo); // Free buffer from ShellFindFirstFile()
844 if(Count < 3) {
845 // Directory is empty
846 Status = ShellDeleteFile( (SHELL_FILE_HANDLE *)&FileHandle);
847 if(Status == RETURN_SUCCESS) {
848 EFIerrno = RETURN_SUCCESS;
849 return 0;
850 /* ######## SUCCESSFUL RETURN ######## */
851 }
852 }
853 else {
854 if(Count == 99) {
855 errno = EIO;
856 }
857 else {
858 errno = ENOTEMPTY;
859 }
860 }
861 }
862 }
863 }
864 else {
865 errno = EIO;
866 }
867 }
868 EFIerrno = Status;
869 if(errno == 0) {
870 errno = EFI2errno( Status );
871 }
872 return -1;
873 }
874
875 /* Internal File Info. worker function for stat and fstat. */
876 static
877 EFI_STATUS
878 _EFI_FileInfo( EFI_FILE_INFO *FileInfo, struct stat *statbuf)
879 {
880 UINT64 Attributes;
881 RETURN_STATUS Status;
882 mode_t newmode;
883
884 if(FileInfo != NULL) {
885 // Got the info, now populate statbuf with it
886 statbuf->st_blksize = S_BLKSIZE;
887 statbuf->st_size = FileInfo->Size;
888 statbuf->st_physsize = FileInfo->PhysicalSize;
889 statbuf->st_birthtime = Efi2Time( &FileInfo->CreateTime);
890 statbuf->st_atime = Efi2Time( &FileInfo->LastAccessTime);
891 statbuf->st_mtime = Efi2Time( &FileInfo->ModificationTime);
892 Attributes = FileInfo->Attribute;
893 newmode = (mode_t)(Attributes << S_EFISHIFT) | S_ACC_READ;
894 if((Attributes & EFI_FILE_DIRECTORY) == 0) {
895 newmode |= _S_IFREG;
896 if((Attributes & EFI_FILE_READ_ONLY) == 0) {
897 statbuf->st_mode |= S_ACC_WRITE;
898 }
899 }
900 else {
901 newmode |= _S_IFDIR;
902 }
903 statbuf->st_mode = newmode;
904 Status = RETURN_SUCCESS;
905 }
906 else {
907 Status = RETURN_DEVICE_ERROR;
908 }
909 return Status;
910 }
911
912 /** The fstat() function obtains information about an open file associated
913 with the file descriptor fildes, and shall write it to the area pointed to
914 by buf.
915
916 The buf argument is a pointer to a stat structure, as defined
917 in <sys/stat.h>, into which information is placed concerning the file.
918
919 The structure members st_mode, st_ino, st_dev, st_uid, st_gid, st_atime,
920 st_ctime, and st_mtime shall have meaningful values. The value of the
921 member st_nlink shall be set to the number of links to the file.
922
923 The fstat() function shall update any time-related fields before writing
924 into the stat structure.
925
926 The fstat() function is implemented using the ShellGetFileInfo()
927 function.
928
929 The stat structure members which don't have direct analogs to EFI file
930 information are filled in as follows:
931 - st_mode Populated with information from fildes
932 - st_ino Set to zero. (inode)
933 - st_dev Set to zero.
934 - st_uid Set to zero.
935 - st_gid Set to zero.
936 - st_nlink Set to one.
937
938 @param[in] fildes File descriptor as returned from open().
939 @param[out] statbuf Buffer in which the file status is put.
940
941 @retval 0 Successful Completion.
942 @retval -1 An error has occurred and errno has been set to
943 identify the error.
944 **/
945 int
946 fstat (int fildes, struct stat *statbuf)
947 {
948 EFI_FILE_HANDLE FileHandle;
949 RETURN_STATUS Status = RETURN_SUCCESS;
950 EFI_FILE_INFO *FileInfo = NULL;
951 UINTN FinfoSize = sizeof(EFI_FILE_INFO);
952
953 if(ValidateFD( fildes, VALID_OPEN)) {
954 FileHandle = gMD->fdarray[fildes].FileHandle;
955 if(isatty(fildes)) {
956 FileInfo = AllocateZeroPool(FinfoSize);
957 if(FileInfo != NULL) {
958 Status = FileHandle->GetInfo( FileHandle, 0, &FinfoSize, FileInfo);
959 }
960 else {
961 Status = RETURN_OUT_OF_RESOURCES;
962 }
963 }
964 else {
965 FileInfo = ShellGetFileInfo( FileHandle);
966 }
967 Status = _EFI_FileInfo( FileInfo, statbuf);
968 }
969 errno = EFI2errno(Status);
970 EFIerrno = Status;
971
972 if(FileInfo != NULL) {
973 FreePool(FileInfo); // Release the buffer allocated by the GetInfo function
974 }
975
976 return errno? -1 : 0;
977 }
978
979 /** Obtains information about the file pointed to by path.
980
981 Opens the file pointed to by path, calls _EFI_FileInfo with the file's handle,
982 then closes the file.
983
984 @retval 0 Successful Completion.
985 @retval -1 An error has occurred and errno has been set to
986 identify the error.
987 **/
988 int
989 stat (const char *path, void *statbuf)
990 {
991 EFI_FILE_HANDLE FileHandle;
992 RETURN_STATUS Status;
993 EFI_FILE_INFO *FileInfo;
994
995 errno = 0; // Make it easier to see if we have an error later
996
997 // Convert name from MBCS to WCS
998 (void)AsciiStrToUnicodeStr( path, gMD->UString);
999 NormalizePath( gMD->UString);
1000
1001 Status = ShellOpenFileByName( gMD->UString, (SHELL_FILE_HANDLE *)&FileHandle, EFI_FILE_MODE_READ, 0ULL);
1002 if(Status == RETURN_SUCCESS) {
1003 FileInfo = ShellGetFileInfo( FileHandle);
1004 Status = _EFI_FileInfo( FileInfo, (struct stat *)statbuf);
1005 (void)ShellCloseFile( (SHELL_FILE_HANDLE *)&FileHandle);
1006 }
1007 errno = EFI2errno(Status);
1008 EFIerrno = Status;
1009
1010 return errno? -1 : 0;
1011 }
1012
1013 /** Same as stat since EFI doesn't have symbolic links. **/
1014 int
1015 lstat (const char *path, struct stat *statbuf)
1016 {
1017 return stat(path, statbuf);
1018 }
1019
1020 /** Read from a file.
1021
1022 The read() function shall attempt to read nbyte bytes from the file
1023 associated with the open file descriptor, fildes, into the buffer pointed
1024 to by buf.
1025
1026 Before any action described below is taken, and if nbyte is zero, the
1027 read() function may detect and return errors as described below. In the
1028 absence of errors, or if error detection is not performed, the read()
1029 function shall return zero and have no other results.
1030
1031 On files that support seeking (for example, a regular file), the read()
1032 shall start at a position in the file given by the file offset associated
1033 with fildes. The file offset shall be incremented by the number of bytes
1034 actually read.
1035
1036 Files that do not support seeking - for example, terminals - always read
1037 from the current position. The value of a file offset associated with
1038 such a file is undefined.
1039
1040 No data transfer shall occur past the current end-of-file. If the
1041 starting position is at or after the end-of-file, 0 shall be returned.
1042
1043 The read() function reads data previously written to a file. If any
1044 portion of a regular file prior to the end-of-file has not been written,
1045 read() shall return bytes with value 0. For example, lseek() allows the
1046 file offset to be set beyond the end of existing data in the file. If data
1047 is later written at this point, subsequent reads in the gap between the
1048 previous end of data and the newly written data shall return bytes with
1049 value 0 until data is written into the gap.
1050
1051 Upon successful completion, where nbyte is greater than 0, read() shall
1052 mark for update the st_atime field of the file, and shall return the
1053 number of bytes read. This number shall never be greater than nbyte. The
1054 value returned may be less than nbyte if the number of bytes left in the
1055 file is less than nbyte, if the read() request was interrupted by a
1056 signal, or if the file is a pipe or FIFO or special file and has fewer
1057 than nbyte bytes immediately available for reading. For example, a read()
1058 from a file associated with a terminal may return one typed line of data.
1059
1060 If fildes does not refer to a directory, the function reads the requested
1061 number of bytes from the file at the file\92s current position and returns
1062 them in buf. If the read goes beyond the end of the file, the read
1063 length is truncated to the end of the file. The file\92s current position is
1064 increased by the number of bytes returned.
1065
1066 If fildes refers to a directory, the function reads the directory entry at
1067 the file\92s current position and returns the entry in buf. If buf
1068 is not large enough to hold the current directory entry, then
1069 errno is set to EBUFSIZE, EFIerrno is set to EFI_BUFFER_TOO_SMALL, and the
1070 current file position is not updated. The size of the buffer needed to read
1071 the entry will be returned as a negative number. On success, the current
1072 position is updated to the next directory entry. If there are no more
1073 directory entries, the read returns a zero-length buffer.
1074 EFI_FILE_INFO is the structure returned as the directory entry.
1075
1076 @return Upon successful completion, read() returns a non-negative integer
1077 indicating the number of bytes actually read. Otherwise, the
1078 functions return a negative value and sets errno to indicate the
1079 error. If errno is EBUFSIZE, the absolute value of the
1080 return value indicates the size of the buffer needed to read
1081 the directory entry.
1082 **/
1083 ssize_t
1084 read (int fildes, void *buf, size_t nbyte)
1085 {
1086 ssize_t BufSize;
1087 EFI_FILE_HANDLE FileHandle;
1088 RETURN_STATUS Status;
1089
1090 BufSize = (ssize_t)nbyte;
1091 if(ValidateFD( fildes, VALID_OPEN)) {
1092 FileHandle = gMD->fdarray[fildes].FileHandle;
1093 if(isatty(fildes)) {
1094 Status = FileHandle->Read( FileHandle, (UINTN *)&BufSize, buf);
1095 }
1096 else {
1097 Status = ShellReadFile( FileHandle, (UINTN *)&BufSize, buf);
1098 }
1099 if(Status != RETURN_SUCCESS) {
1100 EFIerrno = Status;
1101 errno = EFI2errno(Status);
1102 if(Status == RETURN_BUFFER_TOO_SMALL) {
1103 BufSize = -BufSize;
1104 }
1105 else {
1106 BufSize = -1;
1107 }
1108 }
1109 }
1110 else {
1111 errno = EBADF;
1112 }
1113 return BufSize;
1114 }
1115
1116 ssize_t
1117 WideTtyCvt( CHAR16 *dest, const char *buf, size_t n)
1118 {
1119 UINTN i;
1120 wint_t wc;
1121
1122 for(i = 0; i < n; ++i) {
1123 wc = btowc(*buf++);
1124 if( wc == 0) {
1125 break;
1126 };
1127 if(wc < 0) {
1128 wc = BLOCKELEMENT_LIGHT_SHADE;
1129 }
1130 if(wc == L'\n') {
1131 *dest++ = L'\r';
1132 }
1133 *dest++ = (CHAR16)wc;
1134 }
1135 *dest = 0;
1136 return (ssize_t)i;
1137 }
1138
1139 /** Write data to a file.
1140
1141 This function writes the specified number of bytes to the file at the current
1142 file position. The current file position is advanced the actual number of bytes
1143 written, which is returned in BufferSize. Partial writes only occur when there
1144 has been a data error during the write attempt (such as "volume space full").
1145 The file is automatically grown to hold the data if required. Direct writes to
1146 opened directories are not supported.
1147
1148 If fildes refers to a terminal device, isatty() returns TRUE, a partial write
1149 will occur if a NULL or EOF character is encountered before n characters have
1150 been written. Characters inserted due to line-end translations will not be
1151 counted. Unconvertable characters are translated into the UEFI character
1152 BLOCKELEMENT_LIGHT_SHADE.
1153
1154 Since the UEFI console device works on wide characters, the buffer is assumed
1155 to contain a single-byte character stream which is then translated to wide
1156 characters using the btowc() functions. The resulting wide character stream
1157 is what is actually sent to the UEFI console.
1158
1159 QUESTION: Should writes to stdout or stderr always succeed?
1160 **/
1161 ssize_t
1162 write (int fildes, const void *buf, size_t n)
1163 {
1164 ssize_t BufSize;
1165 EFI_FILE_HANDLE FileHandle;
1166 RETURN_STATUS Status = RETURN_SUCCESS;
1167 ssize_t UniBufSz;
1168
1169 BufSize = (ssize_t)n;
1170
1171 if(ValidateFD( fildes, VALID_OPEN)) {
1172 FileHandle = gMD->fdarray[fildes].FileHandle;
1173 if(isatty(fildes)) {
1174 // Convert string from MBCS to WCS and translate \n to \r\n.
1175 UniBufSz = WideTtyCvt(gMD->UString, (const char *)buf, n);
1176 if(UniBufSz > 0) {
1177 BufSize = (ssize_t)(UniBufSz * sizeof(CHAR16));
1178 Status = FileHandle->Write( FileHandle, (UINTN *)&BufSize, (void *)gMD->UString);
1179 BufSize = (ssize_t)n; // Always pretend all was output
1180 }
1181 }
1182 else {
1183 Status = ShellWriteFile( FileHandle, (UINTN *)&BufSize, (void *)buf);
1184 }
1185 if(Status != RETURN_SUCCESS) {
1186 EFIerrno = Status;
1187 errno = EFI2errno(Status);
1188 if(Status == EFI_UNSUPPORTED) {
1189 errno = EISDIR;
1190 }
1191 BufSize = -1;
1192 }
1193 }
1194 else {
1195 errno = EBADF;
1196 }
1197 return BufSize;
1198 }