v1: References: bsc#1063443 v2: References: bsc#1106381 Fix outputting invalid btrfs subvol path on non btrfs filesystem due to bogus return code handling. --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -3260,8 +3260,7 @@ } static grub_err_t -grub_cmd_btrfs_get_default_subvol (struct grub_extcmd_context *ctxt, - int argc, char **argv) +grub_btrfs_get_default_subvol (const char *name, grub_uint64_t *ret_subvolid, char **ret_subvol) { char *devname; grub_device_t dev; @@ -3270,21 +3269,8 @@ grub_uint64_t id; char *subvol = NULL; grub_uint64_t subvolid = 0; - char *varname = NULL; - char *output = NULL; - int path_only = ctxt->state[1].set; - int num_only = ctxt->state[2].set; - - if (ctxt->state[0].set) - varname = ctxt->state[0].arg; - - 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; + devname = grub_file_get_device_name(name); dev = grub_device_open (devname); grub_free (devname); if (!dev) @@ -3295,8 +3281,7 @@ { grub_device_close (dev); grub_dprintf ("btrfs", "failed to open fs\n"); - grub_errno = GRUB_ERR_NONE; - return 0; + return grub_errno; } err = grub_btrfs_get_default_subvolume_id (data, &subvolid); @@ -3325,12 +3310,47 @@ return err; } - if (subvol) - grub_free (subvol); + grub_free (subvol); subvol = path_out; id = parent_id; } + if (ret_subvolid) + *ret_subvolid = subvolid; + if (ret_subvol) + *ret_subvol = subvol; + + grub_btrfs_unmount (data); + grub_device_close (dev); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_btrfs_get_default_subvol (struct grub_extcmd_context *ctxt, + int argc, char **argv) +{ + grub_err_t err; + char *subvol = NULL; + grub_uint64_t subvolid = 0; + char *varname = NULL; + char *output = NULL; + int path_only = ctxt->state[1].set; + int num_only = ctxt->state[2].set; + + if (ctxt->state[0].set) + varname = ctxt->state[0].arg; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + if ((err = grub_btrfs_get_default_subvol (argv[0], &subvolid, &subvol)) != GRUB_ERR_NONE) + { + if (err == GRUB_ERR_BAD_FS) + err = grub_errno = GRUB_ERR_NONE; + return err; + } + if (num_only && path_only) output = grub_xasprintf ("%"PRIuGRUB_UINT64_T" /%s", subvolid, subvol); else if (num_only) @@ -3346,9 +3366,6 @@ grub_free (output); grub_free (subvol); - grub_btrfs_unmount (data); - grub_device_close (dev); - return GRUB_ERR_NONE; } @@ -3427,6 +3444,122 @@ return ""; } + +static char * +grub_btrfs_path_to_abs (const char *path) +{ + grub_err_t err; + char *device_name = NULL; + char *subvol = NULL; + const char *file_name; + char *ret; + + if (!path) + return NULL; + + if ((err = grub_btrfs_get_default_subvol (path, 0, &subvol)) != GRUB_ERR_NONE) + { + grub_errno = GRUB_ERR_NONE; + return NULL; + } + + if (!subvol || *subvol == '\0') + return NULL; + + file_name = (path[0] == '(') ? grub_strchr (path, ')') : NULL; + if (file_name) + file_name++; + else + file_name = path; + device_name = grub_file_get_device_name (path); + if (device_name) + ret = grub_xasprintf ("(%s)/%s%s", device_name, subvol, file_name); + else + ret = grub_xasprintf ("/%s%s", subvol, file_name); + + grub_free (device_name); + grub_free (subvol); + + return ret; +} + +static char * +grub_btrfs_path_to_rel (const char *path) +{ + grub_err_t err; + char *subvol = NULL; + const char *file_name; + + if (!path) + return NULL; + + if ((err = grub_btrfs_get_default_subvol (path, 0, &subvol)) != GRUB_ERR_NONE) + { + grub_errno = GRUB_ERR_NONE; + return NULL; + } + + if (!subvol || *subvol == '\0') + return NULL; + + file_name = (path[0] == '(') ? grub_strchr (path, ')') : NULL; + if (file_name) + file_name++; + else + file_name = path; + + if (*file_name == '/') + file_name++; + + if (grub_strncmp (file_name, subvol, grub_strlen (subvol)) == 0) + { + char *device_name; + char *ret; + + device_name = grub_file_get_device_name (path); + file_name += grub_strlen (subvol); + if (device_name) + ret = grub_xasprintf ("(%s)%s", device_name, file_name); + else + ret = grub_strdup (file_name); + grub_free (device_name); + grub_free (subvol); + return ret; + } + + grub_free (subvol); + return NULL; +} + +static char * +relpath_set_env (struct grub_env_var *var, + const char *val) +{ + int new_val, old_val; + new_val = (val[0] == '1' || val[0] == 'y') ? 1 : 0; + old_val = (var->value[0] == '1' || var->value[0] == 'y') ? 1 : 0; + + if (new_val != old_val) + { + const char **n; + char * (*path_to_xxx) (const char *); + const char *envname[] = {"config_file", "config_directory", NULL}; + + path_to_xxx = (new_val == 1) ? grub_btrfs_path_to_rel : grub_btrfs_path_to_abs; + for (n = envname; *n; n++) + { + char *ctmp = path_to_xxx (grub_env_get (*n)); + if (ctmp) + { + grub_env_set (*n, ctmp); + grub_free (ctmp); + } + } + } + + return grub_strdup (val); +} + GRUB_MOD_INIT (btrfs) { grub_fs_register (&grub_btrfs_fs); @@ -3450,6 +3583,8 @@ subvol_set_env); grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env, subvolid_set_env); + grub_register_variable_hook ("btrfs_relative_path", NULL, + relpath_set_env); grub_env_export ("btrfs_subvol"); grub_env_export ("btrfs_subvolid"); grub_env_export ("btrfs_relative_path"); @@ -3459,6 +3594,7 @@ { grub_register_variable_hook ("btrfs_subvol", NULL, NULL); grub_register_variable_hook ("btrfs_subvolid", NULL, NULL); + grub_register_variable_hook ("btrfs_relative_path", NULL, NULL); grub_unregister_command (cmd_info); grub_unregister_extcmd (cmd_list_subvols); grub_fs_unregister (&grub_btrfs_fs);