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