2 * Copyright 2012 <James.Bottomley@HansenPartnership.com>
11 #include <simple_file.h>
12 #include <efiauthenticated.h>
13 #include <execute.h> /* for generate_path() */
15 static EFI_GUID IMAGE_PROTOCOL
= LOADED_IMAGE_PROTOCOL
;
16 static EFI_GUID SIMPLE_FS_PROTOCOL
= SIMPLE_FILE_SYSTEM_PROTOCOL
;
17 static EFI_GUID FILE_INFO
= EFI_FILE_INFO_ID
;
18 static EFI_GUID FS_INFO
= EFI_FILE_SYSTEM_INFO_ID
;
21 simple_file_open_by_handle(EFI_HANDLE device
, CHAR16
*name
, EFI_FILE
**file
, UINT64 mode
)
23 EFI_STATUS efi_status
;
24 EFI_FILE_IO_INTERFACE
*drive
;
27 efi_status
= uefi_call_wrapper(BS
->HandleProtocol
, 3, device
,
28 &SIMPLE_FS_PROTOCOL
, (void **)&drive
);
30 if (efi_status
!= EFI_SUCCESS
) {
31 Print(L
"Unable to find simple file protocol (%d)\n", efi_status
);
35 efi_status
= uefi_call_wrapper(drive
->OpenVolume
, 2, drive
, &root
);
37 if (efi_status
!= EFI_SUCCESS
) {
38 Print(L
"Failed to open drive volume (%d)\n", efi_status
);
42 efi_status
= uefi_call_wrapper(root
->Open
, 5, root
, file
, name
,
50 simple_file_open(EFI_HANDLE image
, CHAR16
*name
, EFI_FILE
**file
, UINT64 mode
)
52 EFI_STATUS efi_status
;
55 EFI_DEVICE_PATH
*loadpath
= NULL
;
56 CHAR16
*PathName
= NULL
;
58 efi_status
= uefi_call_wrapper(BS
->HandleProtocol
, 3, image
,
59 &IMAGE_PROTOCOL
, (void **)&li
);
61 if (efi_status
!= EFI_SUCCESS
)
62 return simple_file_open_by_handle(image
, name
, file
, mode
);
64 efi_status
= generate_path(name
, li
, &loadpath
, &PathName
);
66 if (efi_status
!= EFI_SUCCESS
) {
67 Print(L
"Unable to generate load path for %s\n", name
);
71 device
= li
->DeviceHandle
;
73 efi_status
= simple_file_open_by_handle(device
, PathName
, file
, mode
);
82 simple_dir_read_all_by_handle(EFI_HANDLE image
, EFI_FILE
*file
, CHAR16
* name
, EFI_FILE_INFO
**entries
,
87 UINTN size
= sizeof(buf
);
88 EFI_FILE_INFO
*fi
= (void *)buf
;
90 status
= uefi_call_wrapper(file
->GetInfo
, 4, file
, &FILE_INFO
,
92 if (status
!= EFI_SUCCESS
) {
93 Print(L
"Failed to get file info\n");
96 if ((fi
->Attribute
& EFI_FILE_DIRECTORY
) == 0) {
97 Print(L
"Not a directory %s\n", name
);
98 status
= EFI_INVALID_PARAMETER
;
104 UINTN len
= sizeof(buf
);
105 status
= uefi_call_wrapper(file
->Read
, 3, file
, &len
, buf
);
106 if (status
!= EFI_SUCCESS
|| len
== 0)
111 uefi_call_wrapper(file
->SetPosition
, 2, file
, 0);
113 char *ptr
= AllocatePool(size
);
114 *entries
= (EFI_FILE_INFO
*)ptr
;
116 return EFI_OUT_OF_RESOURCES
;
118 for (i
= 0; i
< *count
; i
++) {
120 uefi_call_wrapper(file
->Read
, 3, file
, &len
, ptr
);
124 status
= EFI_SUCCESS
;
126 simple_file_close(file
);
127 if (status
!= EFI_SUCCESS
&& *entries
) {
135 simple_dir_read_all(EFI_HANDLE image
, CHAR16
*name
, EFI_FILE_INFO
**entries
,
141 status
= simple_file_open(image
, name
, &file
, EFI_FILE_MODE_READ
);
142 if (status
!= EFI_SUCCESS
) {
143 Print(L
"failed to open file %s: %d\n", name
, status
);
147 return simple_dir_read_all_by_handle(image
, file
, name
, entries
, count
);
151 simple_file_read_all(EFI_FILE
*file
, UINTN
*size
, void **buffer
)
153 EFI_STATUS efi_status
;
161 efi_status
= uefi_call_wrapper(file
->GetInfo
, 4, file
, &FILE_INFO
,
163 if (efi_status
!= EFI_SUCCESS
) {
164 Print(L
"Failed to get file info\n");
168 *size
= fi
->FileSize
;
170 *buffer
= AllocatePool(*size
);
172 Print(L
"Failed to allocate buffer of size %d\n", *size
);
173 return EFI_OUT_OF_RESOURCES
;
175 efi_status
= uefi_call_wrapper(file
->Read
, 3, file
, size
, *buffer
);
182 simple_file_write_all(EFI_FILE
*file
, UINTN size
, void *buffer
)
184 EFI_STATUS efi_status
;
186 efi_status
= uefi_call_wrapper(file
->Write
, 3, file
, &size
, buffer
);
192 simple_file_close(EFI_FILE
*file
)
194 uefi_call_wrapper(file
->Close
, 1, file
);
198 simple_volume_selector(CHAR16
**title
, CHAR16
**selected
, EFI_HANDLE
*h
)
201 EFI_HANDLE
*vol_handles
= NULL
;
206 uefi_call_wrapper(BS
->LocateHandleBuffer
, 5, ByProtocol
,
207 &SIMPLE_FS_PROTOCOL
, NULL
, &count
, &vol_handles
);
209 if (!count
|| !vol_handles
)
210 return EFI_NOT_FOUND
;
212 entries
= AllocatePool(sizeof(CHAR16
*) * (count
+1));
214 return EFI_OUT_OF_RESOURCES
;
216 for (i
= 0; i
< count
; i
++) {
218 UINTN size
= sizeof(buf
);
219 EFI_FILE_SYSTEM_INFO
*fi
= (void *)buf
;
222 EFI_FILE_IO_INTERFACE
*drive
;
224 status
= uefi_call_wrapper(BS
->HandleProtocol
, 3,
228 if (status
!= EFI_SUCCESS
|| !drive
)
231 status
= uefi_call_wrapper(drive
->OpenVolume
, 2, drive
, &root
);
232 if (status
!= EFI_SUCCESS
)
235 status
= uefi_call_wrapper(root
->GetInfo
, 4, root
, &FS_INFO
,
237 if (status
!= EFI_SUCCESS
)
240 name
= fi
->VolumeLabel
;
242 if (!name
|| StrLen(name
) == 0 || StrCmp(name
, L
" ") == 0)
243 name
= DevicePathToStr(DevicePathFromHandle(vol_handles
[i
]));
245 entries
[i
] = AllocatePool((StrLen(name
) + 2) * sizeof(CHAR16
));
248 StrCpy(entries
[i
], name
);
252 val
= console_select(title
, entries
, 0);
255 *selected
= AllocatePool((StrLen(entries
[val
]) + 1) * sizeof(CHAR16
));
257 StrCpy(*selected
, entries
[val
]);
259 *h
= vol_handles
[val
];
265 for (i
= 0; i
< count
; i
++) {
267 FreePool(entries
[i
]);
270 FreePool(vol_handles
);
277 simple_dir_filter(EFI_HANDLE image
, CHAR16
*name
, CHAR16
*filter
,
278 CHAR16
***result
, int *count
, EFI_FILE_INFO
**entries
)
281 int tot
, offs
= StrLen(filter
), i
, c
, filtercount
= 1;
284 CHAR16
*newfilter
= AllocatePool((StrLen(filter
) + 1) * sizeof(CHAR16
)),
288 return EFI_OUT_OF_RESOURCES
;
290 /* just in case efi ever stops writeable strings */
291 StrCpy(newfilter
, filter
);
293 for (i
= 0; i
< offs
; i
++) {
294 if (filter
[i
] == '|')
297 filterarr
= AllocatePool(filtercount
* sizeof(void *));
299 return EFI_OUT_OF_RESOURCES
;
301 filterarr
[c
++] = newfilter
;
302 for (i
= 0; i
< offs
; i
++) {
303 if (filter
[i
] == '|') {
305 filterarr
[c
++] = &newfilter
[i
+1];
311 status
= simple_dir_read_all(image
, name
, entries
, &tot
);
313 if (status
!= EFI_SUCCESS
)
315 ptr
= next
= *entries
;
317 for (i
= 0; i
< tot
; i
++) {
318 int len
= StrLen(next
->FileName
);
320 for (c
= 0; c
< filtercount
; c
++) {
321 offs
= StrLen(filterarr
[c
]);
323 if (StrCmp(&next
->FileName
[len
- offs
], filterarr
[c
]) == 0
324 || (next
->Attribute
& EFI_FILE_DIRECTORY
)) {
329 ptr
+= OFFSET_OF(EFI_FILE_INFO
, FileName
) + (len
+ 1)*sizeof(CHAR16
);
333 *result
= AllocatePool(((*count
) + 1) * sizeof(void *));
335 *result
= AllocatePool(2 * sizeof(void *));
338 ptr
= next
= *entries
;
340 for (i
= 0; i
< tot
; i
++) {
341 int len
= StrLen(next
->FileName
);
343 if (StrCmp(next
->FileName
, L
".") == 0)
344 /* ignore . directory */
347 if (next
->Attribute
& EFI_FILE_DIRECTORY
) {
348 (*result
)[(*count
)] = PoolPrint(L
"%s/", next
->FileName
);
349 if (!(*result
)[(*count
)]) {
350 Print(L
"Failed to allocate buffer");
351 return EFI_OUT_OF_RESOURCES
;
357 for (c
= 0; c
< filtercount
; c
++) {
358 offs
= StrLen(filterarr
[c
]);
360 if (StrCmp(&next
->FileName
[len
- offs
], filterarr
[c
]) == 0) {
361 (*result
)[(*count
)] = StrDuplicate(next
->FileName
);
362 if (!(*result
)[(*count
)]) {
363 Print(L
"Failed to allocate buffer");
364 return EFI_OUT_OF_RESOURCES
;
374 if (StrCmp(next
->FileName
, L
"..") == 0) {
375 /* place .. directory first */
376 CHAR16
*tmp
= (*result
)[(*count
) - 1];
378 (*result
)[(*count
) - 1] = (*result
)[0];
382 ptr
+= OFFSET_OF(EFI_FILE_INFO
, FileName
) + (len
+ 1)*sizeof(CHAR16
);
386 /* no entries at all ... can happen because top level dir has no . or .. */
387 (*result
)[(*count
)++] = L
"./";
389 (*result
)[*count
] = NULL
;
390 status
= EFI_SUCCESS
;
393 if (status
!= EFI_SUCCESS
) {
405 free_entries(CHAR16
**entries
, int count
)
409 for (i
= 0; i
<count
; i
++)
410 FreePool(entries
[i
]);
414 simple_file_selector(EFI_HANDLE
*im
, CHAR16
**title
, CHAR16
*name
,
415 CHAR16
*filter
, CHAR16
**result
)
420 int count
, select
, len
;
421 CHAR16
*newname
, *selected
;
432 simple_volume_selector(title
, &volname
, &h
);
439 newname
= AllocatePool((StrLen(name
) + 1)*sizeof(CHAR16
));
443 StrCpy(newname
, name
);
447 status
= simple_dir_filter(*im
, name
, filter
, &entries
, &count
, &dmp
);
449 if (status
!= EFI_SUCCESS
)
452 select
= console_select(title
, entries
, 0);
456 selected
= entries
[select
];
457 /* note that memory used by selected is valid until dmp is freed */
458 len
= StrLen(selected
);
459 if (selected
[len
- 1] == '/') {
462 /* stay where we are */
463 if (StrCmp(selected
, L
"./") == 0) {
464 free_entries(entries
, count
);
469 } else if (StrCmp(selected
, L
"../") == 0) {
472 i
= StrLen(name
) - 1;
475 for (i
= StrLen(name
); i
> 0; --i
) {
482 if (StrCmp(name
, L
"\\") != 0
483 && StrCmp(&name
[i
], L
"..") != 0) {
485 free_entries(entries
, count
);
492 newname
= AllocatePool((StrLen(name
) + len
+ 2)*sizeof(CHAR16
));
495 StrCpy(newname
, name
);
497 if (name
[StrLen(name
) - 1] != '\\')
498 StrCat(newname
, L
"\\");
499 StrCat(newname
, selected
);
500 /* remove trailing / */
501 newname
[StrLen(newname
) - 1] = '\0';
503 free_entries(entries
, count
);
512 *result
= AllocatePool((StrLen(name
) + len
+ 2)*sizeof(CHAR16
));
514 StrCpy(*result
, name
);
515 if (name
[StrLen(name
) - 1] != '\\')
516 StrCat(*result
, L
"\\");
517 StrCat(*result
, selected
);
523 free_entries(entries
, count
);