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);
