diff --git a/0001-btrfs-rename-skip_default-to-follow_default.patch b/0001-btrfs-rename-skip_default-to-follow_default.patch new file mode 100644 index 0000000..2e9019c --- /dev/null +++ b/0001-btrfs-rename-skip_default-to-follow_default.patch @@ -0,0 +1,85 @@ +From: Jeff Mahoney +Subject: grub2/btrfs: rename skip_default to follow_default + +The skip_default code in find_path uses 1 to indicate that the default +should not be skipped, which is confusing. Let's rename that to +follow_default. + +Signed-off-by: Jeff Mahoney +--- + grub-core/fs/btrfs.c | 20 ++++++++++---------- + 1 file changed, 10 insertions(+), 10 deletions(-) + +--- a/grub-core/fs/btrfs.c 2013-06-16 08:24:05.000000000 -0400 ++++ b/grub-core/fs/btrfs.c 2013-07-29 09:26:07.421380716 -0400 +@@ -1208,7 +1208,7 @@ find_path (struct grub_btrfs_data *data, + grub_size_t allocated = 0; + struct grub_btrfs_dir_item *direl = NULL; + struct grub_btrfs_key key_out; +- int skip_default; ++ int follow_default; + const char *ctoken; + grub_size_t ctokenlen; + char *path_alloc = NULL; +@@ -1220,14 +1220,14 @@ find_path (struct grub_btrfs_data *data, + key->object_id = data->sblock.root_dir_objectid; + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; +- skip_default = 1; ++ follow_default = 1; + origpath = grub_strdup (path); + if (!origpath) + return grub_errno; + + while (1) + { +- if (!skip_default) ++ if (!follow_default) + { + while (path[0] == '/') + path++; +@@ -1254,9 +1254,9 @@ find_path (struct grub_btrfs_data *data, + + if (ctokenlen == 1 && ctoken[0] == '.') + { +- if (!skip_default) ++ if (!follow_default) + path = slash; +- skip_default = 0; ++ follow_default = 0; + continue; + } + if (ctokenlen == 2 && ctoken[0] == '.' && ctoken[1] == '.') +@@ -1287,9 +1287,9 @@ find_path (struct grub_btrfs_data *data, + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + key->object_id = key_out.offset; + +- if (!skip_default) ++ if (!follow_default) + path = slash; +- skip_default = 0; ++ follow_default = 0; + + continue; + } +@@ -1359,9 +1359,9 @@ find_path (struct grub_btrfs_data *data, + return err; + } + +- if (!skip_default) ++ if (!follow_default) + path = slash; +- skip_default = 0; ++ follow_default = 0; + if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK) + { + struct grub_btrfs_inode inode; +@@ -1416,7 +1416,7 @@ find_path (struct grub_btrfs_data *data, + key->object_id = data->sblock.root_dir_objectid; + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; +- skip_default = 1; ++ follow_default = 1; + } + continue; + } diff --git a/0002-btrfs-add-ability-to-boot-from-subvolumes.patch b/0002-btrfs-add-ability-to-boot-from-subvolumes.patch new file mode 100644 index 0000000..936324b --- /dev/null +++ b/0002-btrfs-add-ability-to-boot-from-subvolumes.patch @@ -0,0 +1,659 @@ +From: Jeff Mahoney +Subject: 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 will list the subvolumes available on the +filesystem. + +set btrfs_subvol= and set btrfs_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 +--- + + grub-core/fs/btrfs.c | 529 +++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 518 insertions(+), 11 deletions(-) + +--- a/grub-core/fs/btrfs.c 2013-07-29 09:26:01.585376955 -0400 ++++ b/grub-core/fs/btrfs.c 2013-07-29 09:26:02.513377553 -0400 +@@ -28,6 +28,9 @@ + #include + #include + #include ++#include ++#include ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -63,9 +66,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]; +@@ -104,6 +109,7 @@ struct grub_btrfs_data + grub_uint64_t exttree; + grub_size_t extsize; + struct grub_btrfs_extent_data *extent; ++ grub_uint64_t fs_tree; + }; + + enum +@@ -113,6 +119,7 @@ enum + GRUB_BTRFS_ITEM_TYPE_DIR_ITEM = 0x54, + GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM = 0x6c, + GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM = 0x84, ++ GRUB_BTRFS_ITEM_TYPE_ROOT_REF = 0x9c, + GRUB_BTRFS_ITEM_TYPE_DEVICE = 0xd8, + GRUB_BTRFS_ITEM_TYPE_CHUNK = 0xe4 + }; +@@ -195,6 +202,21 @@ struct grub_btrfs_root_item + grub_uint64_t inode; + }; + ++struct grub_btrfs_inode_ref ++{ ++ grub_uint64_t index; ++ grub_uint16_t name_len; ++ const char name[0]; ++} __attribute__ ((packed)); ++ ++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; +@@ -239,6 +261,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 + }; +@@ -854,6 +884,62 @@ grub_btrfs_read_logical (struct grub_btr + 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_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) + { +@@ -889,6 +975,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; + } + +@@ -1197,6 +1290,91 @@ grub_btrfs_extent_read (struct grub_btrf + } + + 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; ++ int 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 (%d)\n"); ++ ++ err = grub_btrfs_read_logical(data, elemaddr, inode_ref, elemsize, 0); ++ if (err) ++ return grub_error(err, "read_logical caught %d\n", err); ++ ++ alloc += inode_ref->name_len + 2; ++ new = grub_malloc(alloc); ++ if (!new) ++ return grub_error(GRUB_ERR_OUT_OF_MEMORY, ++ "couldn't allocate memory for name (%d)\n", alloc); ++ ++ grub_memcpy(new, inode_ref->name, inode_ref->name_len); ++ new[inode_ref->name_len] = 0; ++ grub_free(inode_ref); ++ if (p) ++ { ++ grub_strcat(new, "/"); ++ grub_strcat(new, p); ++ new[alloc - 1] = 0; ++ grub_free(p); ++ } ++ ++ 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, + grub_uint64_t *tree, grub_uint8_t *type) +@@ -1222,6 +1400,17 @@ find_path (struct grub_btrfs_data *data, + key->offset = 0; + follow_default = 1; + origpath = grub_strdup (path); ++ ++ if (data->fs_tree) ++ { ++ *tree = data->fs_tree; ++ ++ follow_default = 0; /* only the toplevel root has a special 'default' */ ++ ++ /* 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); ++ } ++ + if (!origpath) + return grub_errno; + +@@ -1657,6 +1846,20 @@ grub_btrfs_read (grub_file_t file, char + data->tree, file->offset, buf, len); + } + ++static char * ++btrfs_unparse_uuid(struct grub_btrfs_data *data) ++{ ++ 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]), ++ grub_be_to_cpu16 (data->sblock.uuid[3]), ++ grub_be_to_cpu16 (data->sblock.uuid[4]), ++ 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) + { +@@ -1668,15 +1871,7 @@ grub_btrfs_uuid (grub_device_t device, c + if (!data) + return grub_errno; + +- *uuid = 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]), +- grub_be_to_cpu16 (data->sblock.uuid[3]), +- grub_be_to_cpu16 (data->sblock.uuid[4]), +- grub_be_to_cpu16 (data->sblock.uuid[5]), +- grub_be_to_cpu16 (data->sblock.uuid[6]), +- grub_be_to_cpu16 (data->sblock.uuid[7])); ++ *uuid = btrfs_unparse_uuid(data); + + grub_btrfs_unmount (data); + +@@ -1733,6 +1928,242 @@ grub_btrfs_embed (grub_device_t device _ + } + #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(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 = grub_le_to_cpu64(data->sblock.root_tree); ++ err = get_fs_root(data, tree, grub_cpu_to_le64(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", + .dir = grub_btrfs_dir, +@@ -1748,12 +2179,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/0003-cmdline-add-envvar-loader_cmdline_append.patch b/0003-cmdline-add-envvar-loader_cmdline_append.patch new file mode 100644 index 0000000..f32f6bf --- /dev/null +++ b/0003-cmdline-add-envvar-loader_cmdline_append.patch @@ -0,0 +1,66 @@ +From: Michael Chang +Subject: add loader_cmdline_append environment variable + +Add loader_cmdline_append environment variable that appends it's +value to the loader's command line. We can use this variable to +assign values determined at run time. It will take effect on any +subsidiary configs loaded using configfile as well. + +By means of this variable, we can, for example, set rootflags= +according to the selected btrfs snapshots and tell linux kernel's +btrfs module to mount the snapshot by the subvolume name or id. + +Signed-off-by: Michael Chang +--- + grub-core/lib/cmdline.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/grub-core/lib/cmdline.c b/grub-core/lib/cmdline.c +index a702e64..c5be945 100644 +--- a/grub-core/lib/cmdline.c ++++ b/grub-core/lib/cmdline.c +@@ -19,6 +19,8 @@ + + #include + #include ++#include ++#include + + static unsigned int check_arg (char *c, int *has_space) + { +@@ -65,6 +67,8 @@ int grub_create_loader_cmdline (int argc, char *argv[], char *buf, + int i, space; + unsigned int arg_size; + char *c; ++ const char *append = NULL; ++ grub_size_t append_size = 0; + + for (i = 0; i < argc; i++) + { +@@ -95,6 +99,23 @@ int grub_create_loader_cmdline (int argc, char *argv[], char *buf, + *buf++ = ' '; + } + ++ append = grub_env_get ("loader_cmdline_append"); ++ ++ if (append) ++ append_size = grub_strlen (append); ++ ++ if (append_size) ++ { ++ append_size++; ++ if (size >= append_size) ++ { ++ grub_strcpy (buf, append); ++ buf += append_size; ++ size -= append_size; ++ i++; ++ } ++ } ++ + /* Replace last space with null. */ + if (i) + buf--; +-- +1.8.1.4 + diff --git a/0004-btrfs-export-subvolume-envvars.patch b/0004-btrfs-export-subvolume-envvars.patch new file mode 100644 index 0000000..65e1a6e --- /dev/null +++ b/0004-btrfs-export-subvolume-envvars.patch @@ -0,0 +1,21 @@ +From: Michael Chang +Subject: export btrfs_subvol and btrfs_subvolid + +We should export btrfs_subvol and btrfs_subvolid to have both visible +to subsidiary configuration files loaded using configfile. + +Signed-off-by: Michael Chang + +Index: grub-2.00/grub-core/fs/btrfs.c +=================================================================== +--- grub-2.00.orig/grub-core/fs/btrfs.c ++++ grub-2.00/grub-core/fs/btrfs.c +@@ -2252,6 +2252,8 @@ GRUB_MOD_INIT (btrfs) + subvol_set_env); + grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env, + subvolid_set_env); ++ grub_env_export ("btrfs_subvol"); ++ grub_env_export ("btrfs_subvolid"); + } + + GRUB_MOD_FINI (btrfs) diff --git a/grub2.changes b/grub2.changes index 724748c..c00ec23 100644 --- a/grub2.changes +++ b/grub2.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Tue Dec 17 07:20:33 UTC 2013 - mchang@suse.com + +- add new patches for booting btrfs snapshot (fate#316522) (fate#316232) + * 0001-btrfs-rename-skip_default-to-follow_default.patch + * 0002-btrfs-add-ability-to-boot-from-subvolumes.patch + * 0003-cmdline-add-envvar-loader_cmdline_append.patch + * 0004-btrfs-export-subvolume-envvars.patch + ------------------------------------------------------------------- Tue Dec 10 19:13:53 UTC 2013 - arvidjaar@gmail.com diff --git a/grub2.spec b/grub2.spec index 76a649b..f4ef132 100644 --- a/grub2.spec +++ b/grub2.spec @@ -135,6 +135,11 @@ Patch37: grub2-fix-descriptor-leak-in-grub_util_is_imsm.patch Patch38: grub2-fix-x86_64-efi-startup-stack-alignment.patch Patch39: grub2-fix-x86_64-efi-callwrap-stack-alignment.patch Patch40: 0001-Fix-build-with-FreeType-2.5.1.patch +# Btrfs snapshot booting related patches +Patch100: 0001-btrfs-rename-skip_default-to-follow_default.patch +Patch101: 0002-btrfs-add-ability-to-boot-from-subvolumes.patch +Patch102: 0003-cmdline-add-envvar-loader_cmdline_append.patch +Patch103: 0004-btrfs-export-subvolume-envvars.patch Requires: gettext-runtime %if 0%{?suse_version} >= 1140 Requires: os-prober @@ -254,6 +259,10 @@ mv po/grub.pot po/%{name}.pot %patch38 -p1 %patch39 -p1 %patch40 -p1 +%patch100 -p1 +%patch101 -p1 +%patch102 -p1 +%patch103 -p1 # Generate po/LINGUAS for message catalogs ... ./linguas.sh