]> git.proxmox.com Git - mirror_edk2.git/blob - StdLib/LibC/Uefi/Devices/UefiShell/daShell.c
StdLib: Improve robustness of stat() and make basename() a public function.
[mirror_edk2.git] / StdLib / LibC / Uefi / Devices / UefiShell / daShell.c
1 /** @file
2 Abstract device driver for the UEFI Shell-hosted environment.
3
4 In a Shell-hosted environment, this is the driver that is called
5 when no other driver matches.
6
7 Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>
8 This program and the accompanying materials are licensed and made available under
9 the terms and conditions of the BSD License that accompanies this distribution.
10 The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.
12
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15
16 **/
17 #include <Uefi.h>
18 #include <Library/BaseLib.h>
19 #include <Library/MemoryAllocationLib.h>
20 #include <Library/UefiBootServicesTableLib.h>
21 #include <Library/ShellLib.h>
22
23 #include <LibConfig.h>
24 #include <sys/EfiSysCall.h>
25
26 #include <errno.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <wctype.h>
31 #include <wchar.h>
32 #include <sys/fcntl.h>
33 #include <sys/syslimits.h>
34 #include <kfile.h>
35 #include <Device/Device.h>
36 #include <MainData.h>
37 #include <Efi/SysEfi.h>
38
39 static
40 int
41 EFIAPI
42 da_ShellClose(
43 IN struct __filedes *Fp
44 )
45 {
46 EFIerrno = ShellCloseFile( (SHELL_FILE_HANDLE *)&Fp->devdata);
47 if(RETURN_ERROR(EFIerrno)) {
48 return -1;
49 }
50 return 0;
51 }
52
53 static
54 int
55 EFIAPI
56 da_ShellDelete(
57 struct __filedes *filp
58 )
59 {
60 RETURN_STATUS Status;
61
62 Status = ShellDeleteFile( (SHELL_FILE_HANDLE *)&filp->devdata);
63 if(Status != RETURN_SUCCESS) {
64 errno = EFI2errno(Status);
65 EFIerrno = Status;
66 return -1;
67 }
68 return 0;
69 }
70
71 static
72 off_t
73 EFIAPI
74 da_ShellSeek(
75 struct __filedes *filp,
76 off_t offset,
77 int whence
78 )
79 {
80 __off_t CurPos = -1;
81 RETURN_STATUS Status = RETURN_SUCCESS;
82 SHELL_FILE_HANDLE FileHandle;
83
84 FileHandle = (SHELL_FILE_HANDLE)filp->devdata;
85
86 if(whence != SEEK_SET) {
87 // We are doing a relative seek
88 if(whence == SEEK_END) {
89 // seeking relative to EOF, so position there first.
90 Status = ShellSetFilePosition( FileHandle, 0xFFFFFFFFFFFFFFFFULL);
91 }
92 if(Status == RETURN_SUCCESS) {
93 // Now, determine our current position.
94 Status = ShellGetFilePosition( FileHandle, (UINT64 *)&CurPos);
95 }
96 }
97 else {
98 CurPos = 0; // offset is an absolute position for SEEK_SET
99 if(offset < 0) {
100 Status = RETURN_INVALID_PARAMETER;
101 }
102 }
103 if(Status == RETURN_SUCCESS) {
104 /* CurPos now indicates the point we are seeking from, so seek... */
105 Status = ShellSetFilePosition( FileHandle, (UINT64)(CurPos + offset));
106 if(Status == RETURN_SUCCESS) {
107 // Now, determine our final position.
108 Status = ShellGetFilePosition( FileHandle, (UINT64 *)&CurPos);
109 }
110 }
111 if(Status != RETURN_SUCCESS) {
112 if(Status == EFI_UNSUPPORTED) {
113 errno = EISDIR;
114 }
115 else {
116 errno = EFI2errno(Status);
117 }
118 EFIerrno = Status;
119 CurPos = EOF;
120 }
121 return CurPos;
122 }
123
124 /** The directory path is created with the access permissions specified by
125 perms.
126
127 The directory is closed after it is created.
128
129 @retval 0 The directory was created successfully.
130 @retval -1 An error occurred and an error code is stored in errno.
131 **/
132 static
133 int
134 EFIAPI
135 da_ShellMkdir(
136 const char *path,
137 __mode_t perms
138 )
139 {
140 SHELL_FILE_HANDLE FileHandle;
141 RETURN_STATUS Status;
142 EFI_FILE_INFO *FileInfo;
143 wchar_t *NewPath;
144 int retval = -1;
145
146 // Convert name from MBCS to WCS and change '/' to '\\'
147 NewPath = NormalizePath( path);
148
149 if(NewPath != NULL) {
150 Status = ShellCreateDirectory( NewPath, &FileHandle);
151 if(Status == RETURN_SUCCESS) {
152 FileInfo = ShellGetFileInfo( FileHandle);
153 Status = RETURN_ABORTED; // In case ShellGetFileInfo() failed
154 if(FileInfo != NULL) {
155 FileInfo->Attribute = Omode2EFI(perms);
156 Status = ShellSetFileInfo( FileHandle, FileInfo);
157 FreePool(FileInfo);
158 if(Status == RETURN_SUCCESS) {
159 (void)ShellCloseFile(&FileHandle);
160 retval = 0;
161 }
162 }
163 }
164 errno = EFI2errno(Status);
165 EFIerrno = Status;
166 free(NewPath);
167 }
168 return retval;
169 }
170
171 static
172 ssize_t
173 EFIAPI
174 da_ShellRead(
175 IN OUT struct __filedes *filp,
176 IN OUT off_t *offset,
177 IN size_t BufferSize,
178 OUT VOID *Buffer
179 )
180 {
181 ssize_t BufSize;
182 SHELL_FILE_HANDLE FileHandle;
183 RETURN_STATUS Status;
184
185 if(offset != NULL) {
186 BufSize = (ssize_t)da_ShellSeek(filp, *offset, SEEK_SET);
187 if(BufSize >= 0) {
188 filp->f_offset = BufSize;
189 }
190 }
191
192 BufSize = (ssize_t)BufferSize;
193 FileHandle = (SHELL_FILE_HANDLE)filp->devdata;
194
195 Status = ShellReadFile( FileHandle, (UINTN *)&BufSize, Buffer);
196 if(Status != RETURN_SUCCESS) {
197 EFIerrno = Status;
198 errno = EFI2errno(Status);
199 if(Status == RETURN_BUFFER_TOO_SMALL) {
200 BufSize = -BufSize;
201 }
202 else {
203 BufSize = -1;
204 }
205 }
206 else {
207 filp->f_offset += BufSize; // Advance to where we want to read next.
208 }
209 return BufSize;
210 }
211
212 static
213 ssize_t
214 EFIAPI
215 da_ShellWrite(
216 IN struct __filedes *filp,
217 IN off_t *offset,
218 IN size_t BufferSize,
219 IN const void *Buffer
220 )
221 {
222 ssize_t BufSize;
223 SHELL_FILE_HANDLE FileHandle;
224 RETURN_STATUS Status;
225 off_t Position = 0;
226 int How = SEEK_SET;
227
228
229 if((offset != NULL) || (filp->Oflags & O_APPEND)) {
230 if(filp->Oflags & O_APPEND) {
231 Position = 0;
232 How = SEEK_END;
233 }
234 else {
235 Position = *offset;
236 How = SEEK_SET;
237 }
238 BufSize = (ssize_t)da_ShellSeek(filp, Position, How);
239 if(BufSize >= 0) {
240 filp->f_offset = BufSize;
241 }
242 }
243
244 BufSize = (ssize_t)BufferSize;
245 FileHandle = (SHELL_FILE_HANDLE)filp->devdata;
246
247 Status = ShellWriteFile( FileHandle, (UINTN *)&BufSize, (void *)Buffer);
248
249 if(Status != RETURN_SUCCESS) {
250 EFIerrno = Status;
251 errno = EFI2errno(Status);
252 if(Status == EFI_UNSUPPORTED) {
253 errno = EISDIR;
254 }
255 BufSize = -1;
256 }
257 else {
258 filp->f_offset += BufSize; // Advance to where we want to write next.
259 }
260
261 return BufSize;
262 }
263
264 static
265 int
266 EFIAPI
267 da_ShellStat(
268 struct __filedes *filp,
269 struct stat *statbuf,
270 void *Something
271 )
272 {
273 SHELL_FILE_HANDLE FileHandle;
274 EFI_FILE_INFO *FileInfo = NULL;
275 UINT64 Attributes;
276 RETURN_STATUS Status;
277 mode_t newmode;
278
279 FileHandle = (SHELL_FILE_HANDLE)filp->devdata;
280
281 FileInfo = ShellGetFileInfo( FileHandle);
282
283 if(FileInfo != NULL) {
284 // Got the info, now populate statbuf with it
285 statbuf->st_blksize = S_BLKSIZE;
286 statbuf->st_size = FileInfo->FileSize;
287 statbuf->st_physsize = FileInfo->PhysicalSize;
288 statbuf->st_birthtime = Efi2Time( &FileInfo->CreateTime);
289 statbuf->st_atime = Efi2Time( &FileInfo->LastAccessTime);
290 statbuf->st_mtime = Efi2Time( &FileInfo->ModificationTime);
291 Attributes = FileInfo->Attribute;
292 newmode = (mode_t)(Attributes << S_EFISHIFT) | S_ACC_READ;
293 if((Attributes & EFI_FILE_DIRECTORY) == 0) {
294 newmode |= _S_IFREG;
295 if((Attributes & EFI_FILE_READ_ONLY) == 0) {
296 newmode |= S_ACC_WRITE;
297 }
298 }
299 else {
300 newmode |= _S_IFDIR;
301 }
302 statbuf->st_mode = newmode;
303 Status = RETURN_SUCCESS;
304 }
305 else {
306 Status = RETURN_DEVICE_ERROR;
307 errno = EIO;
308 }
309 EFIerrno = Status;
310
311 if(FileInfo != NULL) {
312 FreePool(FileInfo); // Release the buffer allocated by the GetInfo function
313 }
314 return (Status == RETURN_SUCCESS)? 0 : -1;
315 }
316
317 static
318 int
319 EFIAPI
320 da_ShellIoctl(
321 struct __filedes *filp,
322 ULONGN cmd,
323 va_list argp
324 )
325 {
326 return -EPERM;
327 }
328
329 /** Open an abstract Shell File.
330 **/
331 int
332 EFIAPI
333 da_ShellOpen(
334 DeviceNode *DevNode,
335 struct __filedes *filp,
336 int DevInstance, /* Not used by Shell */
337 wchar_t *Path,
338 wchar_t *MPath
339 )
340 {
341 UINT64 OpenMode;
342 UINT64 Attributes;
343 SHELL_FILE_HANDLE FileHandle;
344 GenericInstance *Gip;
345 char *NPath;
346 wchar_t *WPath;
347 RETURN_STATUS Status;
348 int oflags;
349 int retval;
350
351 EFIerrno = RETURN_SUCCESS;
352
353 //Attributes = Omode2EFI(mode);
354 Attributes = 0;
355
356 // Convert oflags to Attributes
357 oflags = filp->Oflags;
358 OpenMode = Oflags2EFI(oflags);
359 if(OpenMode == 0) {
360 errno = EINVAL;
361 return -1;
362 }
363
364 /* Re-create the full mapped path for the shell. */
365 if(MPath != NULL) {
366 WPath = AllocateZeroPool(PATH_MAX * sizeof(wchar_t) + 1);
367 if(WPath == NULL) {
368 errno = ENOMEM;
369 EFIerrno = RETURN_OUT_OF_RESOURCES;
370 return -1;
371 }
372 wcsncpy(WPath, MPath, NAME_MAX); /* Get the Map Name */
373 wcsncat(WPath, Path, (PATH_MAX - NAME_MAX)); /* Append the path */
374 }
375 else {
376 WPath = Path;
377 }
378
379 retval = -1; /* Initially assume failure. */
380
381 /* Do we care if the file already exists?
382 If O_TRUNC, then delete the file. It will be created anew subsequently.
383 If O_EXCL, then error if the file exists and O_CREAT is set.
384
385 !!!!!!!!! Change this to use ShellSetFileInfo() to actually truncate the file
386 !!!!!!!!! instead of deleting and re-creating it.
387 */
388 do { /* Do fake exception handling */
389 if((oflags & O_TRUNC) || ((oflags & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT))) {
390 Status = ShellIsFile( WPath );
391 if(Status == RETURN_SUCCESS) {
392 // The file exists
393 if(oflags & O_TRUNC) {
394 NPath = AllocateZeroPool(PATH_MAX);
395 if(NPath == NULL) {
396 errno = ENOMEM;
397 EFIerrno = RETURN_OUT_OF_RESOURCES;
398 break;
399 }
400 wcstombs(NPath, WPath, PATH_MAX);
401 // We do a truncate by deleting the existing file and creating a new one.
402 if(unlink(NPath) != 0) {
403 filp->f_iflags = 0; // Release our reservation on this FD
404 FreePool(NPath);
405 break;
406 }
407 FreePool(NPath);
408 }
409 else if((oflags & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT)) {
410 errno = EEXIST;
411 EFIerrno = RETURN_ACCESS_DENIED;
412 filp->f_iflags = 0; // Release our reservation on this FD
413 break;
414 }
415 }
416 }
417
418 // Call the EFI Shell's Open function
419 Status = ShellOpenFileByName( WPath, &FileHandle, OpenMode, Attributes);
420 if(RETURN_ERROR(Status)) {
421 filp->f_iflags = 0; // Release our reservation on this FD
422 // Set errno based upon Status
423 errno = EFI2errno(Status);
424 EFIerrno = Status;
425 break;
426 }
427 retval = 0;
428 // Successfully got a regular File
429 filp->f_iflags |= S_IFREG;
430
431 // Update the info in the fd
432 filp->devdata = (void *)FileHandle;
433
434 Gip = (GenericInstance *)DevNode->InstanceList;
435 filp->f_offset = 0;
436 filp->f_ops = &Gip->Abstraction;
437 // filp->devdata = FileHandle;
438 } while(FALSE);
439
440 /* If we get this far, WPath is not NULL.
441 If MPath is not NULL, then WPath was allocated so we need to free it.
442 */
443 if(MPath != NULL) {
444 FreePool(WPath);
445 }
446 return retval;
447 }
448
449 #include <sys/poll.h>
450 /* Returns a bit mask describing which operations could be completed immediately.
451
452 For now, assume the file system, via the shell, is always ready.
453
454 (POLLIN | POLLRDNORM) The file system is ready to be read.
455 (POLLOUT) The file system is ready for output.
456
457 */
458 static
459 short
460 EFIAPI
461 da_ShellPoll(
462 struct __filedes *filp,
463 short events
464 )
465 {
466 UINT32 RdyMask;
467 short retval = 0;
468
469 RdyMask = (UINT32)filp->Oflags;
470
471 switch(RdyMask & O_ACCMODE) {
472 case O_RDONLY:
473 retval = (POLLIN | POLLRDNORM);
474 break;
475
476 case O_WRONLY:
477 retval = POLLOUT;
478 break;
479
480 case O_RDWR:
481 retval = (POLLIN | POLLRDNORM | POLLOUT);
482 break;
483
484 default:
485 retval = POLLERR;
486 break;
487 }
488 return (retval & (events | POLL_RETONLY));
489 }
490
491 static
492 int
493 EFIAPI
494 da_ShellRename(
495 const char *from,
496 const char *to
497 )
498 {
499 RETURN_STATUS Status;
500 EFI_FILE_INFO *NewFileInfo;
501 EFI_FILE_INFO *OldFileInfo;
502 wchar_t *NewFn;
503 int OldFd;
504 SHELL_FILE_HANDLE FileHandle;
505 wchar_t *NormalizedPath;
506
507 // Open old file
508 OldFd = open(from, O_RDWR, 0);
509 if(OldFd >= 0) {
510 FileHandle = (SHELL_FILE_HANDLE)gMD->fdarray[OldFd].devdata;
511
512 NewFileInfo = malloc(sizeof(EFI_FILE_INFO) + PATH_MAX);
513 if(NewFileInfo != NULL) {
514 OldFileInfo = ShellGetFileInfo( FileHandle);
515 if(OldFileInfo != NULL) {
516 // Copy the Old file info into our new buffer, and free the old.
517 memcpy(NewFileInfo, OldFileInfo, sizeof(EFI_FILE_INFO));
518 FreePool(OldFileInfo);
519 // Normalize path and convert to WCS.
520 NormalizedPath = NormalizePath(to);
521 if (NormalizedPath != NULL) {
522 // Strip off all but the file name portion of new
523 NewFn = GetFileNameFromPath(NormalizedPath);
524 // Copy the new file name into our new file info buffer
525 wcsncpy(NewFileInfo->FileName, NewFn, wcslen(NewFn) + 1);
526 // Update the size of the structure.
527 NewFileInfo->Size = sizeof(EFI_FILE_INFO) + StrSize(NewFn);
528 // Apply the new file name
529 Status = ShellSetFileInfo(FileHandle, NewFileInfo);
530 free(NormalizedPath);
531 free(NewFileInfo);
532 if(Status == EFI_SUCCESS) {
533 // File has been successfully renamed. We are DONE!
534 return 0;
535 }
536 errno = EFI2errno( Status );
537 EFIerrno = Status;
538 }
539 else {
540 free(NewFileInfo);
541 errno = ENOMEM;
542 }
543 }
544 else {
545 free(NewFileInfo);
546 errno = EIO;
547 }
548 }
549 else {
550 errno = ENOMEM;
551 }
552 }
553 return -1;
554 }
555
556 static
557 int
558 EFIAPI
559 da_ShellRmdir(
560 struct __filedes *filp
561 )
562 {
563 SHELL_FILE_HANDLE FileHandle;
564 RETURN_STATUS Status = RETURN_SUCCESS;
565 EFI_FILE_INFO *FileInfo = NULL;
566 int Count = 0;
567 BOOLEAN NoFile = FALSE;
568
569 errno = 0; // Make it easier to see if we have an error later
570
571 FileHandle = (SHELL_FILE_HANDLE)filp->devdata;
572
573 FileInfo = ShellGetFileInfo(FileHandle);
574 if(FileInfo != NULL) {
575 if((FileInfo->Attribute & EFI_FILE_DIRECTORY) == 0) {
576 errno = ENOTDIR;
577 }
578 else {
579 // See if the directory has any entries other than ".." and ".".
580 FreePool(FileInfo); // Free up the buffer from ShellGetFileInfo()
581 Status = ShellFindFirstFile( FileHandle, &FileInfo);
582 if(Status == RETURN_SUCCESS) {
583 ++Count;
584 while(Count < 3) {
585 Status = ShellFindNextFile( FileHandle, FileInfo, &NoFile);
586 if(Status == RETURN_SUCCESS) {
587 if(NoFile) {
588 break;
589 }
590 ++Count;
591 }
592 else {
593 Count = 99;
594 }
595 }
596 FreePool(FileInfo); // Free buffer from ShellFindFirstFile()
597 if(Count < 3) {
598 // Directory is empty
599 Status = ShellDeleteFile( &FileHandle);
600 if(Status == RETURN_SUCCESS) {
601 EFIerrno = RETURN_SUCCESS;
602 return 0;
603 /* ######## SUCCESSFUL RETURN ######## */
604 }
605 }
606 else {
607 if(Count == 99) {
608 errno = EIO;
609 }
610 else {
611 errno = ENOTEMPTY;
612 }
613 }
614 }
615 }
616 }
617 else {
618 errno = EIO;
619 }
620 EFIerrno = Status;
621 if(errno == 0) {
622 errno = EFI2errno( Status );
623 }
624 return -1;
625 }
626
627 /** Construct an instance of the abstract Shell device.
628
629 Allocate the instance structure and populate it with the information for
630 the device.
631 **/
632 RETURN_STATUS
633 EFIAPI
634 __ctor_DevShell(
635 IN EFI_HANDLE ImageHandle,
636 IN EFI_SYSTEM_TABLE *SystemTable
637 )
638 {
639 GenericInstance *Stream;
640 DeviceNode *Node;
641 RETURN_STATUS Status;
642
643 Stream = (GenericInstance *)AllocateZeroPool(sizeof(GenericInstance));
644 if(Stream == NULL) {
645 return RETURN_OUT_OF_RESOURCES;
646 }
647
648 Stream->Cookie = CON_COOKIE;
649 Stream->InstanceNum = 1;
650 Stream->Dev = NULL;
651 Stream->Abstraction.fo_close = &da_ShellClose;
652 Stream->Abstraction.fo_read = &da_ShellRead;
653 Stream->Abstraction.fo_write = &da_ShellWrite;
654 Stream->Abstraction.fo_fcntl = &fnullop_fcntl;
655 Stream->Abstraction.fo_poll = &da_ShellPoll;
656 Stream->Abstraction.fo_flush = &fnullop_flush;
657 Stream->Abstraction.fo_stat = &da_ShellStat;
658 Stream->Abstraction.fo_ioctl = &da_ShellIoctl;
659 Stream->Abstraction.fo_delete = &da_ShellDelete;
660 Stream->Abstraction.fo_rmdir = &da_ShellRmdir;
661 Stream->Abstraction.fo_mkdir = &da_ShellMkdir;
662 Stream->Abstraction.fo_rename = &da_ShellRename;
663 Stream->Abstraction.fo_lseek = &da_ShellSeek;
664
665 Node = __DevRegister(NULL, NULL, &da_ShellOpen, Stream, 1, sizeof(GenericInstance), O_RDWR);
666 Status = EFIerrno;
667 Stream->Parent = Node;
668
669 return Status;
670 }
671
672 RETURN_STATUS
673 EFIAPI
674 __dtor_DevShell(
675 IN EFI_HANDLE ImageHandle,
676 IN EFI_SYSTEM_TABLE *SystemTable
677 )
678 {
679 if(daDefaultDevice != NULL) {
680 if(daDefaultDevice->InstanceList != NULL) {
681 FreePool(daDefaultDevice->InstanceList);
682 }
683 FreePool(daDefaultDevice);
684 }
685 return RETURN_SUCCESS;
686 }