limine/common/lib/uri.c

292 lines
7.7 KiB
C
Raw Normal View History

2020-11-02 11:20:34 +03:00
#include <stdint.h>
#include <stddef.h>
#include <lib/uri.h>
2022-08-27 00:44:47 +03:00
#include <lib/misc.h>
2020-11-02 11:20:34 +03:00
#include <lib/part.h>
#include <lib/libc.h>
#include <fs/file.h>
2020-11-05 03:37:45 +03:00
#include <mm/pmm.h>
#include <lib/print.h>
#include <pxe/tftp.h>
#include <compress/gzip.h>
2022-09-11 21:47:57 +03:00
#include <menu.h>
#include <lib/readline.h>
#include <crypt/blake2b.h>
2020-11-02 11:20:34 +03:00
2022-09-11 21:47:57 +03:00
// A URI takes the form of: resource://root/path#hash
2020-11-02 11:20:34 +03:00
// The following function splits up a URI into its componenets
2022-09-11 21:47:57 +03:00
bool uri_resolve(char *uri, char **resource, char **root, char **path, char **hash) {
size_t length = strlen(uri) + 1;
char *buf = ext_mem_alloc(length);
memcpy(buf, uri, length);
uri = buf;
2020-11-02 11:20:34 +03:00
*resource = *root = *path = NULL;
// Get resource
for (size_t i = 0; ; i++) {
if (strlen(uri + i) < 3)
return false;
if (!memcmp(uri + i, "://", 3)) {
*resource = uri;
uri[i] = 0;
uri += i + 3;
break;
}
}
// Get root
for (size_t i = 0; ; i++) {
if (uri[i] == 0)
return false;
if (uri[i] == '/') {
*root = uri;
uri[i] = 0;
uri += i + 1;
break;
}
}
// Get path
if (*uri == 0)
return false;
*path = uri;
2022-09-11 21:47:57 +03:00
// Get hash
for (int i = (int)strlen(uri) - 1; i >= 0; i--) {
if (uri[i] != '#') {
continue;
}
uri[i++] = 0;
if (hash != NULL) {
*hash = uri + i;
}
if (strlen(uri + i) != 128) {
panic(true, "Blake2b hash must be 128 characters long");
return false;
}
break;
}
2020-11-02 11:20:34 +03:00
return true;
}
static bool parse_bios_partition(char *loc, int *drive, int *partition) {
2021-06-12 14:13:19 +03:00
uint64_t val;
2020-11-02 11:20:34 +03:00
for (size_t i = 0; ; i++) {
if (loc[i] == 0)
return false;
if (loc[i] == ':') {
loc[i] = 0;
if (*loc == 0) {
2021-12-11 21:58:00 +03:00
panic(true, "Drive number cannot be omitted for hdd:// and odd://");
2020-11-02 11:20:34 +03:00
} else {
val = strtoui(loc, NULL, 10);
2021-06-12 14:13:19 +03:00
if (val < 1 || val > 256) {
2021-12-11 21:58:00 +03:00
panic(true, "Drive number outside range 1-256");
2020-11-02 11:20:34 +03:00
}
*drive = val;
2020-11-02 11:20:34 +03:00
}
loc += i + 1;
break;
}
}
val = strtoui(loc, NULL, 10);
2021-06-12 14:13:19 +03:00
if (val > 256) {
2021-12-11 21:58:00 +03:00
panic(true, "Partition number outside range 0-256");
2020-11-02 11:20:34 +03:00
}
2021-06-12 14:13:19 +03:00
*partition = val;
2020-11-02 11:20:34 +03:00
return true;
}
2021-10-21 02:27:05 +03:00
static struct file_handle *uri_hdd_dispatch(char *loc, char *path) {
int drive, partition;
2020-11-02 11:20:34 +03:00
if (!parse_bios_partition(loc, &drive, &partition))
2021-10-21 02:27:05 +03:00
return NULL;
2020-11-02 11:20:34 +03:00
2021-06-12 14:13:19 +03:00
struct volume *volume = volume_get_by_coord(false, drive, partition);
if (volume == NULL)
2021-10-21 02:27:05 +03:00
return NULL;
2021-10-21 02:27:05 +03:00
return fopen(volume, path);
}
2021-10-21 02:27:05 +03:00
static struct file_handle *uri_odd_dispatch(char *loc, char *path) {
int drive, partition;
if (!parse_bios_partition(loc, &drive, &partition))
2021-10-21 02:27:05 +03:00
return NULL;
2021-06-12 14:13:19 +03:00
struct volume *volume = volume_get_by_coord(true, drive, partition);
2021-03-04 11:15:10 +03:00
if (volume == NULL)
2021-10-21 02:27:05 +03:00
return NULL;
2020-11-02 11:20:34 +03:00
2021-10-21 02:27:05 +03:00
return fopen(volume, path);
2020-11-02 11:20:34 +03:00
}
2021-10-21 02:27:05 +03:00
static struct file_handle *uri_guid_dispatch(char *guid_str, char *path) {
2020-11-02 11:20:34 +03:00
struct guid guid;
if (!string_to_guid_be(&guid, guid_str))
2021-10-21 02:27:05 +03:00
return NULL;
2020-11-02 11:20:34 +03:00
2021-03-04 11:15:10 +03:00
struct volume *volume = volume_get_by_guid(&guid);
if (volume == NULL) {
if (!string_to_guid_mixed(&guid, guid_str))
2021-10-21 02:27:05 +03:00
return NULL;
2021-03-04 11:15:10 +03:00
volume = volume_get_by_guid(&guid);
if (volume == NULL)
2021-10-21 02:27:05 +03:00
return NULL;
}
2020-11-02 11:20:34 +03:00
2021-10-21 02:27:05 +03:00
return fopen(volume, path);
2020-11-02 11:20:34 +03:00
}
static struct file_handle *uri_fslabel_dispatch(char *fslabel, char *path) {
struct volume *volume = volume_get_by_fslabel(fslabel);
if (volume == NULL) {
return NULL;
}
return fopen(volume, path);
}
2021-10-21 02:27:05 +03:00
static struct file_handle *uri_tftp_dispatch(char *root, char *path) {
2020-11-05 03:37:45 +03:00
uint32_t ip;
if (!strcmp(root, "")) {
ip = 0;
} else {
if (inet_pton(root, &ip)) {
2022-08-28 21:16:27 +03:00
panic(true, "tftp: Invalid ipv4 address: %s", root);
2020-11-05 03:37:45 +03:00
}
}
struct file_handle *ret;
if ((ret = tftp_open(boot_volume, root, path)) == NULL) {
2021-10-21 02:27:05 +03:00
return NULL;
2020-11-05 03:37:45 +03:00
}
2021-10-21 02:27:05 +03:00
return ret;
2020-11-05 03:37:45 +03:00
}
2021-10-21 02:27:05 +03:00
static struct file_handle *uri_boot_dispatch(char *s_part, char *path) {
2021-03-13 11:08:01 +03:00
if (boot_volume->pxe)
2021-10-21 02:27:05 +03:00
return uri_tftp_dispatch(s_part, path);
2021-02-21 05:45:24 +03:00
int partition;
if (s_part[0] != '\0') {
uint64_t val = strtoui(s_part, NULL, 10);
2021-06-12 14:13:19 +03:00
if (val > 256) {
2021-12-11 21:58:00 +03:00
panic(true, "Partition number outside range 0-256");
}
2021-06-12 14:13:19 +03:00
partition = val;
} else {
partition = boot_volume->partition;
}
2021-06-12 14:13:19 +03:00
struct volume *volume = volume_get_by_coord(boot_volume->is_optical,
boot_volume->index, partition);
2021-03-04 11:15:10 +03:00
if (volume == NULL)
2021-10-21 02:27:05 +03:00
return NULL;
2021-10-21 02:27:05 +03:00
return fopen(volume, path);
}
2021-10-21 02:27:05 +03:00
struct file_handle *uri_open(char *uri) {
struct file_handle *ret;
2022-09-11 21:47:57 +03:00
char *resource = NULL, *root = NULL, *path = NULL, *hash = NULL;
if (!uri_resolve(uri, &resource, &root, &path, &hash)) {
return NULL;
}
2020-11-02 11:20:34 +03:00
2020-12-09 15:02:05 +03:00
if (resource == NULL) {
2022-09-11 21:45:04 +03:00
panic(true, "No resource specified for URI `%#`.", uri);
2020-12-09 15:02:05 +03:00
}
bool compressed = false;
if (*resource == '$') {
compressed = true;
resource++;
}
if (!strcmp(resource, "bios")) {
2021-12-11 21:58:00 +03:00
panic(true, "bios:// resource is no longer supported. Check CONFIG.md for hdd:// and odd://");
} else if (!strcmp(resource, "hdd")) {
2021-10-21 02:27:05 +03:00
ret = uri_hdd_dispatch(root, path);
} else if (!strcmp(resource, "odd")) {
2021-10-21 02:27:05 +03:00
ret = uri_odd_dispatch(root, path);
2020-12-09 15:02:05 +03:00
} else if (!strcmp(resource, "boot")) {
2021-10-21 02:27:05 +03:00
ret = uri_boot_dispatch(root, path);
2020-11-02 11:20:34 +03:00
} else if (!strcmp(resource, "guid")) {
2021-10-21 02:27:05 +03:00
ret = uri_guid_dispatch(root, path);
2020-12-10 09:22:06 +03:00
} else if (!strcmp(resource, "uuid")) {
2021-10-21 02:27:05 +03:00
ret = uri_guid_dispatch(root, path);
} else if (!strcmp(resource, "fslabel")) {
ret = uri_fslabel_dispatch(root, path);
2020-11-05 03:37:45 +03:00
} else if (!strcmp(resource, "tftp")) {
2021-10-21 02:27:05 +03:00
ret = uri_tftp_dispatch(root, path);
2020-11-02 11:20:34 +03:00
} else {
2022-07-07 12:18:09 +03:00
panic(true, "Resource `%s` not valid.", resource);
2020-11-02 11:20:34 +03:00
}
2022-09-11 21:47:57 +03:00
if (hash != NULL && ret != NULL) {
uint8_t out_buf[BLAKE2B_OUT_BYTES];
void *file_buf = freadall(ret, MEMMAP_BOOTLOADER_RECLAIMABLE);
blake2b(out_buf, file_buf, ret->size);
uint8_t hash_buf[BLAKE2B_OUT_BYTES];
for (size_t i = 0; i < sizeof(hash_buf); i++) {
hash_buf[i] = digit_to_int(hash[i * 2]) << 4 | digit_to_int(hash[i * 2 + 1]);
}
if (memcmp(hash_buf, out_buf, sizeof(out_buf)) != 0) {
if (hash_mismatch_panic) {
panic(true, "Blake2b hash for URI `%#` does not match!", uri);
} else {
print("WARNING: Blake2b hash for URI `%#` does not match!\n"
2022-09-13 11:06:39 +03:00
" Press Y to continue, press any other key to return to menu...", uri);
2022-09-11 21:47:57 +03:00
char ch = getchar();
if (ch != 'Y' && ch != 'y') {
menu(false);
}
print("\n");
}
}
}
2021-10-21 02:27:05 +03:00
if (compressed && ret != NULL) {
struct file_handle *compressed_fd = ext_mem_alloc(sizeof(struct file_handle));
void *src = freadall(ret, MEMMAP_BOOTLOADER_RECLAIMABLE);
if ((compressed_fd->fd = gzip_uncompress(src, ret->size, &compressed_fd->size)) == NULL) {
panic(true, "GZip error");
2021-12-11 21:58:00 +03:00
}
compressed_fd->vol = ret->vol;
compressed_fd->path = ext_mem_alloc(ret->path_len);
memcpy(compressed_fd->path, ret->path, ret->path_len);
compressed_fd->path_len = ret->path_len;
2021-10-21 02:27:05 +03:00
compressed_fd->is_memfile = true;
2023-02-15 23:09:46 +03:00
uint64_t src_size = ret->size;
fclose(ret);
2023-02-15 23:09:46 +03:00
pmm_free(src, src_size);
2021-10-21 02:27:05 +03:00
ret = compressed_fd;
}
return ret;
2020-11-02 11:20:34 +03:00
}