diff options
Diffstat (limited to 'lib/simple_file.c')
-rw-r--r-- | lib/simple_file.c | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/lib/simple_file.c b/lib/simple_file.c new file mode 100644 index 00000000..d345d870 --- /dev/null +++ b/lib/simple_file.c @@ -0,0 +1,528 @@ +/* + * Copyright 2012 <James.Bottomley@HansenPartnership.com> + * + * see COPYING file + */ + +#include <efi.h> +#include <efilib.h> + +#include <console.h> +#include <simple_file.h> +#include <efiauthenticated.h> +#include <execute.h> /* for generate_path() */ + +static EFI_GUID IMAGE_PROTOCOL = LOADED_IMAGE_PROTOCOL; +static EFI_GUID SIMPLE_FS_PROTOCOL = SIMPLE_FILE_SYSTEM_PROTOCOL; +static EFI_GUID FILE_INFO = EFI_FILE_INFO_ID; +static EFI_GUID FS_INFO = EFI_FILE_SYSTEM_INFO_ID; + +EFI_STATUS +simple_file_open_by_handle(EFI_HANDLE device, CHAR16 *name, EFI_FILE **file, UINT64 mode) +{ + EFI_STATUS efi_status; + EFI_FILE_IO_INTERFACE *drive; + EFI_FILE *root; + + efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device, + &SIMPLE_FS_PROTOCOL, (void **)&drive); + + if (efi_status != EFI_SUCCESS) { + Print(L"Unable to find simple file protocol (%d)\n", efi_status); + goto error; + } + + efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); + + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to open drive volume (%d)\n", efi_status); + goto error; + } + + efi_status = uefi_call_wrapper(root->Open, 5, root, file, name, + mode, 0); + + error: + return efi_status; +} + +EFI_STATUS +simple_file_open(EFI_HANDLE image, CHAR16 *name, EFI_FILE **file, UINT64 mode) +{ + EFI_STATUS efi_status; + EFI_HANDLE device; + EFI_LOADED_IMAGE *li; + EFI_DEVICE_PATH *loadpath = NULL; + CHAR16 *PathName = NULL; + + efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image, + &IMAGE_PROTOCOL, (void **)&li); + + if (efi_status != EFI_SUCCESS) + return simple_file_open_by_handle(image, name, file, mode); + + efi_status = generate_path(name, li, &loadpath, &PathName); + + if (efi_status != EFI_SUCCESS) { + Print(L"Unable to generate load path for %s\n", name); + return efi_status; + } + + device = li->DeviceHandle; + + efi_status = simple_file_open_by_handle(device, PathName, file, mode); + + FreePool(PathName); + FreePool(loadpath); + + return efi_status; +} + +EFI_STATUS +simple_dir_read_all_by_handle(EFI_HANDLE image, EFI_FILE *file, CHAR16* name, EFI_FILE_INFO **entries, + int *count) +{ + EFI_STATUS status; + char buf[4096]; + UINTN size = sizeof(buf); + EFI_FILE_INFO *fi = (void *)buf; + + status = uefi_call_wrapper(file->GetInfo, 4, file, &FILE_INFO, + &size, fi); + if (status != EFI_SUCCESS) { + Print(L"Failed to get file info\n"); + goto out; + } + if ((fi->Attribute & EFI_FILE_DIRECTORY) == 0) { + Print(L"Not a directory %s\n", name); + status = EFI_INVALID_PARAMETER; + goto out; + } + size = 0; + *count = 0; + for (;;) { + UINTN len = sizeof(buf); + status = uefi_call_wrapper(file->Read, 3, file, &len, buf); + if (status != EFI_SUCCESS || len == 0) + break; + (*count)++; + size += len; + } + uefi_call_wrapper(file->SetPosition, 2, file, 0); + + char *ptr = AllocatePool(size); + *entries = (EFI_FILE_INFO *)ptr; + if (!*entries) + return EFI_OUT_OF_RESOURCES; + int i; + for (i = 0; i < *count; i++) { + UINTN len = size; + uefi_call_wrapper(file->Read, 3, file, &len, ptr); + ptr += len; + size -= len; + } + status = EFI_SUCCESS; + out: + simple_file_close(file); + if (status != EFI_SUCCESS && *entries) { + FreePool(*entries); + *entries = NULL; + } + return status; +} + +EFI_STATUS +simple_dir_read_all(EFI_HANDLE image, CHAR16 *name, EFI_FILE_INFO **entries, + int *count) +{ + EFI_FILE *file; + EFI_STATUS status; + + status = simple_file_open(image, name, &file, EFI_FILE_MODE_READ); + if (status != EFI_SUCCESS) { + Print(L"failed to open file %s: %d\n", name, status); + return status; + } + + return simple_dir_read_all_by_handle(image, file, name, entries, count); +} + +EFI_STATUS +simple_file_read_all(EFI_FILE *file, UINTN *size, void **buffer) +{ + EFI_STATUS efi_status; + EFI_FILE_INFO *fi; + char buf[1024]; + + *size = sizeof(buf); + fi = (void *)buf; + + + efi_status = uefi_call_wrapper(file->GetInfo, 4, file, &FILE_INFO, + size, fi); + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to get file info\n"); + return efi_status; + } + + *size = fi->FileSize; + + *buffer = AllocatePool(*size); + if (!*buffer) { + Print(L"Failed to allocate buffer of size %d\n", *size); + return EFI_OUT_OF_RESOURCES; + } + efi_status = uefi_call_wrapper(file->Read, 3, file, size, *buffer); + + return efi_status; +} + + +EFI_STATUS +simple_file_write_all(EFI_FILE *file, UINTN size, void *buffer) +{ + EFI_STATUS efi_status; + + efi_status = uefi_call_wrapper(file->Write, 3, file, &size, buffer); + + return efi_status; +} + +void +simple_file_close(EFI_FILE *file) +{ + uefi_call_wrapper(file->Close, 1, file); +} + +EFI_STATUS +simple_volume_selector(CHAR16 **title, CHAR16 **selected, EFI_HANDLE *h) +{ + UINTN count, i; + EFI_HANDLE *vol_handles = NULL; + EFI_STATUS status; + CHAR16 **entries; + int val; + + uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, + &SIMPLE_FS_PROTOCOL, NULL, &count, &vol_handles); + + if (!count || !vol_handles) + return EFI_NOT_FOUND; + + entries = AllocatePool(sizeof(CHAR16 *) * (count+1)); + if (!entries) + return EFI_OUT_OF_RESOURCES; + + for (i = 0; i < count; i++) { + char buf[4096]; + UINTN size = sizeof(buf); + EFI_FILE_SYSTEM_INFO *fi = (void *)buf; + EFI_FILE *root; + CHAR16 *name; + EFI_FILE_IO_INTERFACE *drive; + + status = uefi_call_wrapper(BS->HandleProtocol, 3, + vol_handles[i], + &SIMPLE_FS_PROTOCOL, + (void **)&drive); + if (status != EFI_SUCCESS || !drive) + continue; + + status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); + if (status != EFI_SUCCESS) + continue; + + status = uefi_call_wrapper(root->GetInfo, 4, root, &FS_INFO, + &size, fi); + if (status != EFI_SUCCESS) + continue; + + name = fi->VolumeLabel; + + if (!name || StrLen(name) == 0 || StrCmp(name, L" ") == 0) + name = DevicePathToStr(DevicePathFromHandle(vol_handles[i])); + + entries[i] = AllocatePool((StrLen(name) + 2) * sizeof(CHAR16)); + if (!entries[i]) + break; + StrCpy(entries[i], name); + } + entries[i] = NULL; + + val = console_select(title, entries, 0); + + if (val >= 0) { + *selected = AllocatePool((StrLen(entries[val]) + 1) * sizeof(CHAR16)); + if (*selected) { + StrCpy(*selected , entries[val]); + } + *h = vol_handles[val]; + } else { + *selected = NULL; + *h = 0; + } + + for (i = 0; i < count; i++) { + if (entries[i]) + FreePool(entries[i]); + } + FreePool(entries); + FreePool(vol_handles); + + + return EFI_SUCCESS; +} + +EFI_STATUS +simple_dir_filter(EFI_HANDLE image, CHAR16 *name, CHAR16 *filter, + CHAR16 ***result, int *count, EFI_FILE_INFO **entries) +{ + EFI_STATUS status; + int tot, offs = StrLen(filter), i, c, filtercount = 1; + EFI_FILE_INFO *next; + void *ptr; + CHAR16 *newfilter = AllocatePool((StrLen(filter) + 1) * sizeof(CHAR16)), + **filterarr; + + if (!newfilter) + return EFI_OUT_OF_RESOURCES; + + /* just in case efi ever stops writeable strings */ + StrCpy(newfilter, filter); + + for (i = 0; i < offs; i++) { + if (filter[i] == '|') + filtercount++; + } + filterarr = AllocatePool(filtercount * sizeof(void *)); + if (!filterarr) + return EFI_OUT_OF_RESOURCES; + c = 0; + filterarr[c++] = newfilter; + for (i = 0; i < offs; i++) { + if (filter[i] == '|') { + newfilter[i] = '\0'; + filterarr[c++] = &newfilter[i+1]; + } + } + + *count = 0; + + status = simple_dir_read_all(image, name, entries, &tot); + + if (status != EFI_SUCCESS) + goto out; + ptr = next = *entries; + + for (i = 0; i < tot; i++) { + int len = StrLen(next->FileName); + + for (c = 0; c < filtercount; c++) { + offs = StrLen(filterarr[c]); + + if (StrCmp(&next->FileName[len - offs], filterarr[c]) == 0 + || (next->Attribute & EFI_FILE_DIRECTORY)) { + (*count)++; + break; + } + } + ptr += OFFSET_OF(EFI_FILE_INFO, FileName) + (len + 1)*sizeof(CHAR16); + next = ptr; + } + if (*count) + *result = AllocatePool(((*count) + 1) * sizeof(void *)); + else + *result = AllocatePool(2 * sizeof(void *)); + + *count = 0; + ptr = next = *entries; + + for (i = 0; i < tot; i++) { + int len = StrLen(next->FileName); + + if (StrCmp(next->FileName, L".") == 0) + /* ignore . directory */ + goto next; + + if (next->Attribute & EFI_FILE_DIRECTORY) { + (*result)[(*count)] = PoolPrint(L"%s/", next->FileName); + if (!(*result)[(*count)]) { + Print(L"Failed to allocate buffer"); + return EFI_OUT_OF_RESOURCES; + } + (*count)++; + goto next; + } + + for (c = 0; c < filtercount; c++) { + offs = StrLen(filterarr[c]); + + if (StrCmp(&next->FileName[len - offs], filterarr[c]) == 0) { + (*result)[(*count)] = StrDuplicate(next->FileName); + if (!(*result)[(*count)]) { + Print(L"Failed to allocate buffer"); + return EFI_OUT_OF_RESOURCES; + } + (*count)++; + } else { + continue; + } + break; + } + + next: + if (StrCmp(next->FileName, L"..") == 0) { + /* place .. directory first */ + CHAR16 *tmp = (*result)[(*count) - 1]; + + (*result)[(*count) - 1] = (*result)[0]; + (*result)[0] = tmp; + } + + ptr += OFFSET_OF(EFI_FILE_INFO, FileName) + (len + 1)*sizeof(CHAR16); + next = ptr; + } + if (*count == 0) { + /* no entries at all ... can happen because top level dir has no . or .. */ + (*result)[(*count)++] = L"./"; + } + (*result)[*count] = NULL; + status = EFI_SUCCESS; + + out: + if (status != EFI_SUCCESS) { + if (*entries) + FreePool(*entries); + *entries = NULL; + if (*result) + FreePool(*result); + *result = NULL; + } + return status; +} + +static void +free_entries(CHAR16 **entries, int count) +{ + int i; + + for (i = 0; i<count; i++) + FreePool(entries[i]); +} + +void +simple_file_selector(EFI_HANDLE *im, CHAR16 **title, CHAR16 *name, + CHAR16 *filter, CHAR16 **result) +{ + EFI_STATUS status; + CHAR16 **entries = NULL; + EFI_FILE_INFO *dmp; + int count, select, len; + CHAR16 *newname, *selected; + + *result = NULL; + if (!name) + name = L"\\"; + if (!filter) + filter = L""; + if (!*im) { + EFI_HANDLE h; + CHAR16 *volname; + + simple_volume_selector(title, &volname, &h); + if (!volname) + return; + FreePool(volname); + *im = h; + } + + newname = AllocatePool((StrLen(name) + 1)*sizeof(CHAR16)); + if (!newname) + return; + + StrCpy(newname, name); + name = newname; + + redo: + status = simple_dir_filter(*im, name, filter, &entries, &count, &dmp); + + if (status != EFI_SUCCESS) + goto out_free_name; + + select = console_select(title, entries, 0); + if (select < 0) + /* ESC key */ + goto out_free; + selected = entries[select]; + /* note that memory used by selected is valid until dmp is freed */ + len = StrLen(selected); + if (selected[len - 1] == '/') { + CHAR16 *newname; + + /* stay where we are */ + if (StrCmp(selected, L"./") == 0) { + free_entries(entries, count); + FreePool(entries); + entries = NULL; + FreePool(dmp); + goto redo; + } else if (StrCmp(selected, L"../") == 0) { + int i; + + i = StrLen(name) - 1; + + + for (i = StrLen(name); i > 0; --i) { + if (name[i] == '\\') + break; + } + if (i == 0) + i = 1; + + if (StrCmp(name, L"\\") != 0 + && StrCmp(&name[i], L"..") != 0) { + name[i] = '\0'; + free_entries(entries, count); + FreePool(entries); + entries = NULL; + FreePool(dmp); + goto redo; + } + } + newname = AllocatePool((StrLen(name) + len + 2)*sizeof(CHAR16)); + if (!newname) + goto out_free; + StrCpy(newname, name); + + if (name[StrLen(name) - 1] != '\\') + StrCat(newname, L"\\"); + StrCat(newname, selected); + /* remove trailing / */ + newname[StrLen(newname) - 1] = '\0'; + + free_entries(entries, count); + FreePool(entries); + entries = NULL; + FreePool(dmp); + FreePool(name); + name = newname; + + goto redo; + } + *result = AllocatePool((StrLen(name) + len + 2)*sizeof(CHAR16)); + if (*result) { + StrCpy(*result, name); + if (name[StrLen(name) - 1] != '\\') + StrCat(*result, L"\\"); + StrCat(*result, selected); + } + + out_free: + FreePool(dmp); + if (entries) { + free_entries(entries, count); + FreePool(entries); + } + out_free_name: + FreePool(name); +} |