You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
703 lines
19 KiB
703 lines
19 KiB
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
|
From: Jeff Mahoney <jeffm@suse.com> |
|
Date: Tue, 9 Jul 2019 13:39:45 +0200 |
|
Subject: [PATCH] grub2/btrfs: Add ability to boot from subvolumes |
|
|
|
This patch adds the ability to specify a different root on a btrfs |
|
filesystem too boot from other than the default one. |
|
|
|
btrfs-list-snapshots <dev> will list the subvolumes available on the |
|
filesystem. |
|
|
|
set btrfs_subvol=<path> and set btrfs_subvolid=<subvolid> will specify |
|
which subvolume to use and any pathnames provided with either of those |
|
variables set will start using that root. If the subvolume or subvolume id |
|
doesn't exist, then an error case will result. |
|
|
|
It is possible to boot into a separate GRUB instance by exporting the |
|
variable and loading the config file from the subvolume. |
|
|
|
Signed-off-by: Jeff Mahoney <jeffm@suse.com> |
|
--- |
|
grub-core/fs/btrfs.c | 552 +++++++++++++++++++++++++++++++++++++++++++++++++-- |
|
include/grub/btrfs.h | 1 + |
|
2 files changed, 533 insertions(+), 20 deletions(-) |
|
|
|
diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c |
|
index 63203034dfc..f1fff7385b5 100644 |
|
--- a/grub-core/fs/btrfs.c |
|
+++ b/grub-core/fs/btrfs.c |
|
@@ -38,6 +38,9 @@ |
|
#include <zstd.h> |
|
#include <grub/i18n.h> |
|
#include <grub/btrfs.h> |
|
+#include <grub/command.h> |
|
+#include <grub/env.h> |
|
+#include <grub/extcmd.h> |
|
#include <grub/crypto.h> |
|
#include <grub/diskfilter.h> |
|
#include <grub/safemath.h> |
|
@@ -79,9 +82,11 @@ struct grub_btrfs_superblock |
|
grub_uint64_t generation; |
|
grub_uint64_t root_tree; |
|
grub_uint64_t chunk_tree; |
|
- grub_uint8_t dummy2[0x20]; |
|
+ grub_uint8_t dummy2[0x18]; |
|
+ grub_uint64_t bytes_used; |
|
grub_uint64_t root_dir_objectid; |
|
- grub_uint8_t dummy3[0x41]; |
|
+ grub_uint64_t num_devices; |
|
+ grub_uint8_t dummy3[0x39]; |
|
struct grub_btrfs_device this_device; |
|
char label[0x100]; |
|
grub_uint8_t dummy4[0x100]; |
|
@@ -121,6 +126,7 @@ struct grub_btrfs_data |
|
grub_uint64_t exttree; |
|
grub_size_t extsize; |
|
struct grub_btrfs_extent_data *extent; |
|
+ grub_uint64_t fs_tree; |
|
}; |
|
|
|
struct grub_btrfs_chunk_item |
|
@@ -191,6 +197,14 @@ struct grub_btrfs_leaf_descriptor |
|
} *data; |
|
}; |
|
|
|
+struct grub_btrfs_root_ref |
|
+{ |
|
+ grub_uint64_t dirid; |
|
+ grub_uint64_t sequence; |
|
+ grub_uint16_t name_len; |
|
+ const char name[0]; |
|
+} __attribute__ ((packed)); |
|
+ |
|
struct grub_btrfs_time |
|
{ |
|
grub_int64_t sec; |
|
@@ -236,6 +250,14 @@ struct grub_btrfs_extent_data |
|
|
|
#define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100 |
|
|
|
+#define GRUB_BTRFS_ROOT_TREE_OBJECTID 1ULL |
|
+#define GRUB_BTRFS_FS_TREE_OBJECTID 5ULL |
|
+#define GRUB_BTRFS_ROOT_REF_KEY 156 |
|
+#define GRUB_BTRFS_ROOT_ITEM_KEY 132 |
|
+ |
|
+static grub_uint64_t btrfs_default_subvolid = 0; |
|
+static char *btrfs_default_subvol = NULL; |
|
+ |
|
static grub_disk_addr_t superblock_sectors[] = { 64 * 2, 64 * 1024 * 2, |
|
256 * 1048576 * 2, 1048576ULL * 1048576ULL * 2 |
|
}; |
|
@@ -1173,6 +1195,62 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, |
|
return GRUB_ERR_NONE; |
|
} |
|
|
|
+static grub_err_t |
|
+get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree, |
|
+ grub_uint64_t objectid, grub_uint64_t offset, |
|
+ grub_uint64_t *fs_root); |
|
+ |
|
+static grub_err_t |
|
+lookup_root_by_id(struct grub_btrfs_data *data, grub_uint64_t id) |
|
+{ |
|
+ grub_err_t err; |
|
+ grub_uint64_t tree; |
|
+ |
|
+ err = get_fs_root(data, data->sblock.root_tree, id, -1, &tree); |
|
+ if (!err) |
|
+ data->fs_tree = tree; |
|
+ return err; |
|
+} |
|
+ |
|
+static grub_err_t |
|
+find_path (struct grub_btrfs_data *data, |
|
+ const char *path, struct grub_btrfs_key *key, |
|
+ grub_uint64_t *tree, grub_uint8_t *type); |
|
+ |
|
+static grub_err_t |
|
+lookup_root_by_name(struct grub_btrfs_data *data, const char *path) |
|
+{ |
|
+ grub_err_t err; |
|
+ grub_uint64_t tree = 0; |
|
+ grub_uint8_t type; |
|
+ struct grub_btrfs_key key; |
|
+ |
|
+ err = find_path (data, path, &key, &tree, &type); |
|
+ if (err) |
|
+ return grub_error(GRUB_ERR_FILE_NOT_FOUND, "couldn't locate %s\n", path); |
|
+ |
|
+ if (key.object_id != grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0) |
|
+ return grub_error(GRUB_ERR_BAD_FILE_TYPE, "%s: not a subvolume\n", path); |
|
+ |
|
+ data->fs_tree = tree; |
|
+ return GRUB_ERR_NONE; |
|
+} |
|
+ |
|
+static grub_err_t |
|
+btrfs_handle_subvol(struct grub_btrfs_data *data __attribute__ ((unused))) |
|
+{ |
|
+ if (btrfs_default_subvol) |
|
+ return lookup_root_by_name(data, btrfs_default_subvol); |
|
+ |
|
+ if (btrfs_default_subvolid) |
|
+ return lookup_root_by_id(data, btrfs_default_subvolid); |
|
+ |
|
+ data->fs_tree = 0; |
|
+ |
|
+ return GRUB_ERR_NONE; |
|
+} |
|
+ |
|
+ |
|
static struct grub_btrfs_data * |
|
grub_btrfs_mount (grub_device_t dev) |
|
{ |
|
@@ -1208,6 +1286,13 @@ grub_btrfs_mount (grub_device_t dev) |
|
data->devices_attached[0].dev = dev; |
|
data->devices_attached[0].id = data->sblock.this_device.device_id; |
|
|
|
+ err = btrfs_handle_subvol (data); |
|
+ if (err) |
|
+ { |
|
+ grub_free (data); |
|
+ return NULL; |
|
+ } |
|
+ |
|
return data; |
|
} |
|
|
|
@@ -1673,6 +1758,91 @@ get_root (struct grub_btrfs_data *data, struct grub_btrfs_key *key, |
|
return GRUB_ERR_NONE; |
|
} |
|
|
|
+static grub_err_t |
|
+find_pathname(struct grub_btrfs_data *data, grub_uint64_t objectid, |
|
+ grub_uint64_t fs_root, const char *name, char **pathname) |
|
+{ |
|
+ grub_err_t err; |
|
+ struct grub_btrfs_key key = { |
|
+ .object_id = objectid, |
|
+ .type = GRUB_BTRFS_ITEM_TYPE_INODE_REF, |
|
+ .offset = 0, |
|
+ }; |
|
+ struct grub_btrfs_key key_out; |
|
+ struct grub_btrfs_leaf_descriptor desc; |
|
+ char *p = grub_strdup (name); |
|
+ grub_disk_addr_t elemaddr; |
|
+ grub_size_t elemsize; |
|
+ grub_size_t alloc = grub_strlen(name) + 1; |
|
+ |
|
+ err = lower_bound(data, &key, &key_out, fs_root, |
|
+ &elemaddr, &elemsize, &desc, 0); |
|
+ if (err) |
|
+ return grub_error(err, "lower_bound caught %d\n", err); |
|
+ |
|
+ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) |
|
+ next(data, &desc, &elemaddr, &elemsize, &key_out); |
|
+ |
|
+ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) |
|
+ { |
|
+ return grub_error(GRUB_ERR_FILE_NOT_FOUND, |
|
+ "Can't find inode ref for {%"PRIuGRUB_UINT64_T |
|
+ ", %u, %"PRIuGRUB_UINT64_T"} %"PRIuGRUB_UINT64_T |
|
+ "/%"PRIuGRUB_SIZE"\n", |
|
+ key_out.object_id, key_out.type, |
|
+ key_out.offset, elemaddr, elemsize); |
|
+ } |
|
+ |
|
+ |
|
+ while (key_out.type == GRUB_BTRFS_ITEM_TYPE_INODE_REF && |
|
+ key_out.object_id != key_out.offset) { |
|
+ struct grub_btrfs_inode_ref *inode_ref; |
|
+ char *new; |
|
+ |
|
+ inode_ref = grub_malloc(elemsize + 1); |
|
+ if (!inode_ref) |
|
+ return grub_error(GRUB_ERR_OUT_OF_MEMORY, |
|
+ "couldn't allocate memory for inode_ref (%"PRIuGRUB_SIZE")\n", elemsize); |
|
+ |
|
+ err = grub_btrfs_read_logical(data, elemaddr, inode_ref, elemsize, 0); |
|
+ if (err) |
|
+ return grub_error(err, "read_logical caught %d\n", err); |
|
+ |
|
+ alloc += grub_le_to_cpu16 (inode_ref->n) + 2; |
|
+ new = grub_malloc(alloc); |
|
+ if (!new) |
|
+ return grub_error(GRUB_ERR_OUT_OF_MEMORY, |
|
+ "couldn't allocate memory for name (%"PRIuGRUB_SIZE")\n", alloc); |
|
+ |
|
+ grub_memcpy(new, inode_ref->name, grub_le_to_cpu16 (inode_ref->n)); |
|
+ if (p) |
|
+ { |
|
+ new[grub_le_to_cpu16 (inode_ref->n)] = '/'; |
|
+ grub_strcpy (new + grub_le_to_cpu16 (inode_ref->n) + 1, p); |
|
+ grub_free(p); |
|
+ } |
|
+ else |
|
+ new[grub_le_to_cpu16 (inode_ref->n)] = 0; |
|
+ grub_free(inode_ref); |
|
+ |
|
+ p = new; |
|
+ |
|
+ key.object_id = key_out.offset; |
|
+ |
|
+ err = lower_bound(data, &key, &key_out, fs_root, &elemaddr, |
|
+ &elemsize, &desc, 0); |
|
+ if (err) |
|
+ return grub_error(err, "lower_bound caught %d\n", err); |
|
+ |
|
+ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) |
|
+ next(data, &desc, &elemaddr, &elemsize, &key_out); |
|
+ |
|
+ } |
|
+ |
|
+ *pathname = p; |
|
+ return 0; |
|
+} |
|
+ |
|
static grub_err_t |
|
find_path (struct grub_btrfs_data *data, |
|
const char *path, struct grub_btrfs_key *key, |
|
@@ -1691,14 +1861,26 @@ find_path (struct grub_btrfs_data *data, |
|
char *origpath = NULL; |
|
unsigned symlinks_max = 32; |
|
|
|
- err = get_root (data, key, tree, type); |
|
- if (err) |
|
- return err; |
|
- |
|
origpath = grub_strdup (path); |
|
if (!origpath) |
|
return grub_errno; |
|
|
|
+ if (data->fs_tree) |
|
+ { |
|
+ *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; |
|
+ *tree = data->fs_tree; |
|
+ /* This is a tree root, so everything starts at objectid 256 */ |
|
+ key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); |
|
+ key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; |
|
+ key->offset = 0; |
|
+ } |
|
+ else |
|
+ { |
|
+ err = get_root (data, key, tree, type); |
|
+ if (err) |
|
+ return err; |
|
+ } |
|
+ |
|
while (1) |
|
{ |
|
while (path[0] == '/') |
|
@@ -1871,9 +2053,21 @@ find_path (struct grub_btrfs_data *data, |
|
path = path_alloc = tmp; |
|
if (path[0] == '/') |
|
{ |
|
- err = get_root (data, key, tree, type); |
|
- if (err) |
|
- return err; |
|
+ if (data->fs_tree) |
|
+ { |
|
+ *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; |
|
+ *tree = data->fs_tree; |
|
+ /* This is a tree root, so everything starts at objectid 256 */ |
|
+ key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); |
|
+ key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; |
|
+ key->offset = 0; |
|
+ } |
|
+ else |
|
+ { |
|
+ err = get_root (data, key, tree, type); |
|
+ if (err) |
|
+ return err; |
|
+ } |
|
} |
|
continue; |
|
} |
|
@@ -2114,18 +2308,10 @@ grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len) |
|
data->tree, file->offset, buf, len); |
|
} |
|
|
|
-static grub_err_t |
|
-grub_btrfs_uuid (grub_device_t device, char **uuid) |
|
+static char * |
|
+btrfs_unparse_uuid(struct grub_btrfs_data *data) |
|
{ |
|
- struct grub_btrfs_data *data; |
|
- |
|
- *uuid = NULL; |
|
- |
|
- data = grub_btrfs_mount (device); |
|
- if (!data) |
|
- return grub_errno; |
|
- |
|
- *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", |
|
+ return grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", |
|
grub_be_to_cpu16 (data->sblock.uuid[0]), |
|
grub_be_to_cpu16 (data->sblock.uuid[1]), |
|
grub_be_to_cpu16 (data->sblock.uuid[2]), |
|
@@ -2134,6 +2320,20 @@ grub_btrfs_uuid (grub_device_t device, char **uuid) |
|
grub_be_to_cpu16 (data->sblock.uuid[5]), |
|
grub_be_to_cpu16 (data->sblock.uuid[6]), |
|
grub_be_to_cpu16 (data->sblock.uuid[7])); |
|
+} |
|
+ |
|
+static grub_err_t |
|
+grub_btrfs_uuid (grub_device_t device, char **uuid) |
|
+{ |
|
+ struct grub_btrfs_data *data; |
|
+ |
|
+ *uuid = NULL; |
|
+ |
|
+ data = grub_btrfs_mount (device); |
|
+ if (!data) |
|
+ return grub_errno; |
|
+ |
|
+ *uuid = btrfs_unparse_uuid(data); |
|
|
|
grub_btrfs_unmount (data); |
|
|
|
@@ -2190,6 +2390,242 @@ grub_btrfs_embed (grub_device_t device __attribute__ ((unused)), |
|
} |
|
#endif |
|
|
|
+static grub_err_t |
|
+grub_cmd_btrfs_info (grub_command_t cmd __attribute__ ((unused)), int argc, |
|
+ char **argv) |
|
+{ |
|
+ grub_device_t dev; |
|
+ char *devname; |
|
+ struct grub_btrfs_data *data; |
|
+ char *uuid; |
|
+ |
|
+ if (argc < 1) |
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); |
|
+ |
|
+ devname = grub_file_get_device_name(argv[0]); |
|
+ |
|
+ if (!devname) |
|
+ return grub_errno; |
|
+ |
|
+ dev = grub_device_open (devname); |
|
+ grub_free (devname); |
|
+ if (!dev) |
|
+ return grub_errno; |
|
+ |
|
+ data = grub_btrfs_mount (dev); |
|
+ if (!data) |
|
+ { |
|
+ grub_device_close(dev); |
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to open fs"); |
|
+ } |
|
+ |
|
+ if (data->sblock.label) |
|
+ grub_printf("Label: '%s' ", data->sblock.label); |
|
+ else |
|
+ grub_printf("Label: none "); |
|
+ |
|
+ uuid = btrfs_unparse_uuid(data); |
|
+ |
|
+ grub_printf(" uuid: %s\n\tTotal devices %" PRIuGRUB_UINT64_T |
|
+ " FS bytes used %" PRIuGRUB_UINT64_T "\n", |
|
+ uuid, grub_cpu_to_le64(data->sblock.num_devices), |
|
+ grub_cpu_to_le64(data->sblock.bytes_used)); |
|
+ |
|
+ grub_btrfs_unmount (data); |
|
+ |
|
+ return 0; |
|
+} |
|
+ |
|
+static grub_err_t |
|
+get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree, |
|
+ grub_uint64_t objectid, grub_uint64_t offset, |
|
+ grub_uint64_t *fs_root) |
|
+{ |
|
+ grub_err_t err; |
|
+ struct grub_btrfs_key key_in = { |
|
+ .object_id = objectid, |
|
+ .type = GRUB_BTRFS_ROOT_ITEM_KEY, |
|
+ .offset = offset, |
|
+ }, key_out; |
|
+ struct grub_btrfs_leaf_descriptor desc; |
|
+ grub_disk_addr_t elemaddr; |
|
+ grub_size_t elemsize; |
|
+ struct grub_btrfs_root_item ri; |
|
+ |
|
+ err = lower_bound(data, &key_in, &key_out, tree, |
|
+ &elemaddr, &elemsize, &desc, 0); |
|
+ |
|
+ if (err) |
|
+ return err; |
|
+ |
|
+ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM || elemaddr == 0) |
|
+ return grub_error(GRUB_ERR_FILE_NOT_FOUND, |
|
+ N_("can't find fs root for subvol %"PRIuGRUB_UINT64_T"\n"), |
|
+ key_in.object_id); |
|
+ |
|
+ err = grub_btrfs_read_logical (data, elemaddr, &ri, sizeof (ri), 0); |
|
+ if (err) |
|
+ return err; |
|
+ |
|
+ *fs_root = ri.tree; |
|
+ |
|
+ return GRUB_ERR_NONE; |
|
+} |
|
+ |
|
+static const struct grub_arg_option options[] = { |
|
+ {"output", 'o', 0, N_("Output to a variable instead of the console."), |
|
+ N_("VARNAME"), ARG_TYPE_STRING}, |
|
+ {"path-only", 'p', 0, N_("Show only the path of the subvolume."), 0, 0}, |
|
+ {"id-only", 'i', 0, N_("Show only the id of the subvolume."), 0, 0}, |
|
+ {0, 0, 0, 0, 0, 0} |
|
+}; |
|
+ |
|
+static grub_err_t |
|
+grub_cmd_btrfs_list_subvols (struct grub_extcmd_context *ctxt, |
|
+ int argc, char **argv) |
|
+{ |
|
+ struct grub_btrfs_data *data; |
|
+ grub_device_t dev; |
|
+ char *devname; |
|
+ grub_uint64_t tree; |
|
+ struct grub_btrfs_key key_in = { |
|
+ .object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_FS_TREE_OBJECTID), |
|
+ .type = GRUB_BTRFS_ROOT_REF_KEY, |
|
+ .offset = 0, |
|
+ }, key_out; |
|
+ struct grub_btrfs_leaf_descriptor desc; |
|
+ grub_disk_addr_t elemaddr; |
|
+ grub_uint64_t fs_root = 0; |
|
+ grub_size_t elemsize; |
|
+ grub_size_t allocated = 0; |
|
+ int r = 0; |
|
+ grub_err_t err; |
|
+ char *buf = NULL; |
|
+ int print = 1; |
|
+ int path_only = ctxt->state[1].set; |
|
+ int num_only = ctxt->state[2].set; |
|
+ char *varname = NULL; |
|
+ char *output = NULL; |
|
+ |
|
+ if (ctxt->state[0].set) { |
|
+ varname = ctxt->state[0].arg; |
|
+ print = 0; |
|
+ } |
|
+ |
|
+ if (argc < 1) |
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); |
|
+ |
|
+ devname = grub_file_get_device_name(argv[0]); |
|
+ if (!devname) |
|
+ return grub_errno; |
|
+ |
|
+ dev = grub_device_open (devname); |
|
+ grub_free (devname); |
|
+ if (!dev) |
|
+ return grub_errno; |
|
+ |
|
+ data = grub_btrfs_mount(dev); |
|
+ if (!data) |
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "could not open device"); |
|
+ |
|
+ tree = data->sblock.root_tree; |
|
+ err = get_fs_root(data, tree, grub_cpu_to_le64_compile_time (GRUB_BTRFS_FS_TREE_OBJECTID), |
|
+ 0, &fs_root); |
|
+ if (err) |
|
+ goto out; |
|
+ |
|
+ err = lower_bound(data, &key_in, &key_out, tree, |
|
+ &elemaddr, &elemsize, &desc, 0); |
|
+ |
|
+ if (err) |
|
+ { |
|
+ grub_btrfs_unmount(data); |
|
+ return err; |
|
+ } |
|
+ |
|
+ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF || elemaddr == 0) |
|
+ { |
|
+ r = next(data, &desc, &elemaddr, &elemsize, &key_out); |
|
+ } |
|
+ |
|
+ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF) { |
|
+ err = GRUB_ERR_FILE_NOT_FOUND; |
|
+ grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("can't find root refs")); |
|
+ goto out; |
|
+ } |
|
+ |
|
+ do |
|
+ { |
|
+ struct grub_btrfs_root_ref *ref; |
|
+ char *p = NULL; |
|
+ |
|
+ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF) |
|
+ { |
|
+ r = 0; |
|
+ break; |
|
+ } |
|
+ |
|
+ if (elemsize > allocated) |
|
+ { |
|
+ grub_free(buf); |
|
+ allocated = 2 * elemsize; |
|
+ buf = grub_malloc(allocated + 1); |
|
+ if (!buf) |
|
+ { |
|
+ r = -grub_errno; |
|
+ break; |
|
+ } |
|
+ } |
|
+ ref = (struct grub_btrfs_root_ref *)buf; |
|
+ |
|
+ err = grub_btrfs_read_logical(data, elemaddr, buf, elemsize, 0); |
|
+ if (err) |
|
+ { |
|
+ r = -err; |
|
+ break; |
|
+ } |
|
+ buf[elemsize] = 0; |
|
+ |
|
+ find_pathname(data, ref->dirid, fs_root, ref->name, &p); |
|
+ |
|
+ if (print) |
|
+ { |
|
+ if (num_only) |
|
+ grub_printf("ID %"PRIuGRUB_UINT64_T"\n", key_out.offset); |
|
+ else if (path_only) |
|
+ grub_printf("%s\n", p); |
|
+ else |
|
+ grub_printf("ID %"PRIuGRUB_UINT64_T" path %s\n", key_out.offset, p); |
|
+ } else { |
|
+ char *old = output; |
|
+ if (num_only) |
|
+ output = grub_xasprintf("%s%"PRIuGRUB_UINT64_T"\n", |
|
+ old ?: "", key_out.offset); |
|
+ else if (path_only) |
|
+ output = grub_xasprintf("%s%s\n", old ?: "", p); |
|
+ else |
|
+ output = grub_xasprintf("%sID %"PRIuGRUB_UINT64_T" path %s\n", |
|
+ old ?: "", key_out.offset, p); |
|
+ |
|
+ if (old) |
|
+ grub_free(old); |
|
+ } |
|
+ |
|
+ r = next(data, &desc, &elemaddr, &elemsize, &key_out); |
|
+ } while(r > 0); |
|
+ |
|
+ if (output) |
|
+ grub_env_set(varname, output); |
|
+ |
|
+out: |
|
+ free_iterator(&desc); |
|
+ grub_btrfs_unmount(data); |
|
+ |
|
+ grub_device_close (dev); |
|
+ |
|
+ return 0; |
|
+} |
|
+ |
|
static struct grub_fs grub_btrfs_fs = { |
|
.name = "btrfs", |
|
.fs_dir = grub_btrfs_dir, |
|
@@ -2205,12 +2641,88 @@ static struct grub_fs grub_btrfs_fs = { |
|
#endif |
|
}; |
|
|
|
+static grub_command_t cmd_info; |
|
+static grub_extcmd_t cmd_list_subvols; |
|
+ |
|
+static char * |
|
+subvolid_set_env (struct grub_env_var *var __attribute__ ((unused)), |
|
+ const char *val) |
|
+{ |
|
+ unsigned long long result = 0; |
|
+ |
|
+ grub_errno = GRUB_ERR_NONE; |
|
+ if (*val) |
|
+ { |
|
+ result = grub_strtoull(val, NULL, 10); |
|
+ if (grub_errno) |
|
+ return NULL; |
|
+ } |
|
+ |
|
+ grub_free (btrfs_default_subvol); |
|
+ btrfs_default_subvol = NULL; |
|
+ btrfs_default_subvolid = result; |
|
+ return grub_strdup(val); |
|
+} |
|
+ |
|
+static const char * |
|
+subvolid_get_env (struct grub_env_var *var __attribute__ ((unused)), |
|
+ const char *val __attribute__ ((unused))) |
|
+{ |
|
+ if (btrfs_default_subvol) |
|
+ return grub_xasprintf("subvol:%s", btrfs_default_subvol); |
|
+ else if (btrfs_default_subvolid) |
|
+ return grub_xasprintf("%"PRIuGRUB_UINT64_T, btrfs_default_subvolid); |
|
+ else |
|
+ return ""; |
|
+} |
|
+ |
|
+static char * |
|
+subvol_set_env (struct grub_env_var *var __attribute__ ((unused)), |
|
+ const char *val) |
|
+{ |
|
+ grub_free (btrfs_default_subvol); |
|
+ btrfs_default_subvol = grub_strdup (val); |
|
+ btrfs_default_subvolid = 0; |
|
+ return grub_strdup(val); |
|
+} |
|
+ |
|
+static const char * |
|
+subvol_get_env (struct grub_env_var *var __attribute__ ((unused)), |
|
+ const char *val __attribute__ ((unused))) |
|
+{ |
|
+ if (btrfs_default_subvol) |
|
+ return btrfs_default_subvol; |
|
+ else if (btrfs_default_subvolid) |
|
+ return grub_xasprintf("subvolid:%" PRIuGRUB_UINT64_T, |
|
+ btrfs_default_subvolid); |
|
+ else |
|
+ return ""; |
|
+} |
|
+ |
|
GRUB_MOD_INIT (btrfs) |
|
{ |
|
grub_fs_register (&grub_btrfs_fs); |
|
+ cmd_info = grub_register_command("btrfs-info", grub_cmd_btrfs_info, |
|
+ "DEVICE", |
|
+ "Print BtrFS info about DEVICE."); |
|
+ cmd_list_subvols = grub_register_extcmd("btrfs-list-subvols", |
|
+ grub_cmd_btrfs_list_subvols, 0, |
|
+ "[-p|-n] [-o var] DEVICE", |
|
+ "Print list of BtrFS subvolumes on " |
|
+ "DEVICE.", options); |
|
+ grub_register_variable_hook ("btrfs_subvol", subvol_get_env, |
|
+ subvol_set_env); |
|
+ grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env, |
|
+ subvolid_set_env); |
|
} |
|
|
|
GRUB_MOD_FINI (btrfs) |
|
{ |
|
+ grub_register_variable_hook ("btrfs_subvol", NULL, NULL); |
|
+ grub_register_variable_hook ("btrfs_subvolid", NULL, NULL); |
|
+ grub_unregister_command (cmd_info); |
|
+ grub_unregister_extcmd (cmd_list_subvols); |
|
grub_fs_unregister (&grub_btrfs_fs); |
|
} |
|
+ |
|
+// vim: si et sw=2: |
|
diff --git a/include/grub/btrfs.h b/include/grub/btrfs.h |
|
index 9d93fb6c182..234ad976771 100644 |
|
--- a/include/grub/btrfs.h |
|
+++ b/include/grub/btrfs.h |
|
@@ -29,6 +29,7 @@ enum |
|
GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM = 0x84, |
|
GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF = 0x90, |
|
GRUB_BTRFS_ITEM_TYPE_DEVICE = 0xd8, |
|
+ GRUB_BTRFS_ITEM_TYPE_ROOT_REF = 0x9c, |
|
GRUB_BTRFS_ITEM_TYPE_CHUNK = 0xe4 |
|
}; |
|
|
|
|