Modify mkrelpath to work even if device is mounted with subvolid option.
-2013-10-14 Andrey Borzenkov <arvidjaar@gmail.com>
+2013-10-28 Vladimir Serbinenko <phcoder@gmail.com>
+
+ Make / in btrfs refer to real root, not the default volume.
+ Modify mkrelpath to work even if device is mounted with subvolid option.
+
+2013-10-28 Andrey Borzenkov <arvidjaar@gmail.com>
- * Makefile.util.def: Add grub-core/kern/disk_common.c to library
+ * Makefile.util.def: Add grub-core/kern/disk_common.c to library
extra_dist.
* grub-core/Makefile.core.def: Add kern/disk_common.c to disk module
extra_dist.
/* btrfs.c - B-tree file system. */
/*
* GRUB -- GRand Unified Bootloader
- * Copyright (C) 2010 Free Software Foundation, Inc.
+ * Copyright (C) 2010,2011,2012,2013 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <grub/deflate.h>
#include <minilzo.h>
#include <grub/i18n.h>
+#include <grub/btrfs.h>
GRUB_MOD_LICENSE ("GPLv3+");
struct grub_btrfs_extent_data *extent;
};
-enum
- {
- GRUB_BTRFS_ITEM_TYPE_INODE_ITEM = 0x01,
- GRUB_BTRFS_ITEM_TYPE_INODE_REF = 0x0c,
- 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_DEVICE = 0xd8,
- GRUB_BTRFS_ITEM_TYPE_CHUNK = 0xe4
- };
-
-struct grub_btrfs_key
-{
- grub_uint64_t object_id;
- grub_uint8_t type;
- grub_uint64_t offset;
-} __attribute__ ((packed));
-
struct grub_btrfs_chunk_item
{
grub_uint64_t size;
} *data;
};
-struct grub_btrfs_root_item
-{
- grub_uint8_t dummy[0xb0];
- grub_uint64_t tree;
- grub_uint64_t inode;
-};
-
struct grub_btrfs_time
{
grub_int64_t sec;
return pos - pos0;
}
+static grub_err_t
+get_root (struct grub_btrfs_data *data, struct grub_btrfs_key *key,
+ grub_uint64_t *tree, grub_uint8_t *type)
+{
+ grub_err_t err;
+ grub_disk_addr_t elemaddr;
+ grub_size_t elemsize;
+ struct grub_btrfs_key key_out, key_in;
+ struct grub_btrfs_root_item ri;
+
+ key_in.object_id = GRUB_BTRFS_ROOT_VOL_OBJECTID;
+ key_in.offset = 0;
+ key_in.type = GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM;
+ err = lower_bound (data, &key_in, &key_out,
+ data->sblock.root_tree,
+ &elemaddr, &elemsize, NULL, 0);
+ if (err)
+ return err;
+ if (key_in.object_id != key_out.object_id
+ || key_in.type != key_out.type
+ || key_in.offset != key_out.offset)
+ return grub_error (GRUB_ERR_BAD_FS, "no root");
+ err = grub_btrfs_read_logical (data, elemaddr, &ri,
+ sizeof (ri), 0);
+ if (err)
+ return err;
+ key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
+ key->offset = 0;
+ key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK);
+ *tree = ri.tree;
+ *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
+ return GRUB_ERR_NONE;
+}
+
static grub_err_t
find_path (struct grub_btrfs_data *data,
const char *path, struct grub_btrfs_key *key,
grub_size_t allocated = 0;
struct grub_btrfs_dir_item *direl = NULL;
struct grub_btrfs_key key_out;
- int skip_default;
const char *ctoken;
grub_size_t ctokenlen;
char *path_alloc = NULL;
char *origpath = NULL;
unsigned symlinks_max = 32;
- *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
- *tree = data->sblock.root_tree;
- key->object_id = data->sblock.root_dir_objectid;
- key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
- key->offset = 0;
- skip_default = 1;
+ err = get_root (data, key, tree, type);
+ if (err)
+ return err;
+
origpath = grub_strdup (path);
if (!origpath)
return grub_errno;
while (1)
{
- if (!skip_default)
- {
- while (path[0] == '/')
- path++;
- if (!path[0])
- break;
- slash = grub_strchr (path, '/');
- if (!slash)
- slash = path + grub_strlen (path);
- ctoken = path;
- ctokenlen = slash - path;
- }
- else
- {
- ctoken = "default";
- ctokenlen = sizeof ("default") - 1;
- }
+ while (path[0] == '/')
+ path++;
+ if (!path[0])
+ break;
+ slash = grub_strchr (path, '/');
+ if (!slash)
+ slash = path + grub_strlen (path);
+ ctoken = path;
+ ctokenlen = slash - path;
if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
{
if (ctokenlen == 1 && ctoken[0] == '.')
{
- if (!skip_default)
- path = slash;
- skip_default = 0;
- continue;
+ path = slash;
+ continue;
}
if (ctokenlen == 2 && ctoken[0] == '.' && ctoken[1] == '.')
{
*type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
key->object_id = key_out.offset;
- if (!skip_default)
- path = slash;
- skip_default = 0;
+ path = slash;
continue;
}
return err;
}
- if (!skip_default)
- path = slash;
- skip_default = 0;
+ path = slash;
if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK)
{
struct grub_btrfs_inode inode;
path = path_alloc = tmp;
if (path[0] == '/')
{
- *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
- *tree = data->sblock.root_tree;
- key->object_id = data->sblock.root_dir_objectid;
- key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
- key->offset = 0;
- skip_default = 1;
+ err = get_root (data, key, tree, type);
+ if (err)
+ return err;
}
continue;
}
#include <sys/wait.h>
+#include <btrfs/ioctl.h>
#include <linux/types.h>
#include <linux/major.h>
#include <linux/raid/md_p.h>
#include <linux/raid/md_u.h>
#include <grub/i18n.h>
#include <grub/emu/exec.h>
+#include <grub/btrfs.h>
#define LVM_DEV_MAPPER_STRING "/dev/mapper/"
+#if 0
/* Defines taken from btrfs/ioctl.h. */
struct btrfs_ioctl_dev_info_args
struct btrfs_ioctl_dev_info_args)
#define BTRFS_IOC_FS_INFO _IOR(0x94, 31, \
struct btrfs_ioctl_fs_info_args)
-
+#endif
static int
grub_util_is_imsm (const char *os_dev);
return ret;
}
+static char *
+get_btrfs_fs_prefix (const char *mount_path)
+{
+ struct btrfs_ioctl_ino_lookup_args args;
+ struct stat st;
+ int fd;
+ grub_uint64_t tree_id, inode_id;
+ char *ret = NULL;
+
+ fd = open (mount_path, O_RDONLY);
+
+ if (fd < 0)
+ return NULL;
+ memset (&args, 0, sizeof(args));
+ args.objectid = GRUB_BTRFS_TREE_ROOT_OBJECTID;
+
+ if (ioctl (fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
+ return NULL;
+ tree_id = args.treeid;
+
+ if (fstat (fd, &st) < 0)
+ return NULL;
+ inode_id = st.st_ino;
+
+ while (tree_id != GRUB_BTRFS_ROOT_VOL_OBJECTID
+ || inode_id != GRUB_BTRFS_TREE_ROOT_OBJECTID)
+ {
+ grub_uint64_t *nid;
+ const char *name;
+ size_t namelen;
+ struct btrfs_ioctl_search_args sargs;
+ char *old;
+
+ memset (&sargs, 0, sizeof(sargs));
+
+ if (inode_id == GRUB_BTRFS_TREE_ROOT_OBJECTID)
+ {
+ struct grub_btrfs_root_backref *br;
+
+ sargs.key.tree_id = 1;
+ sargs.key.min_objectid = tree_id;
+ sargs.key.max_objectid = tree_id;
+
+ sargs.key.min_offset = 0;
+ sargs.key.max_offset = ~0ULL;
+ sargs.key.min_transid = 0;
+ sargs.key.max_transid = ~0ULL;
+ sargs.key.min_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF;
+ sargs.key.max_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF;
+
+ sargs.key.nr_items = 1;
+
+ if (ioctl (fd, BTRFS_IOC_TREE_SEARCH, &sargs) < 0)
+ return NULL;
+
+ if (sargs.key.nr_items == 0)
+ return NULL;
+
+ nid = (grub_uint64_t *) (sargs.buf + 16);
+ tree_id = *nid;
+ br = (struct grub_btrfs_root_backref *) (sargs.buf + 32);
+ inode_id = br->inode_id;
+ name = br->name;
+ namelen = br->n;
+ }
+ else
+ {
+ struct grub_btrfs_inode_ref *ir;
+
+ sargs.key.tree_id = tree_id;
+ sargs.key.min_objectid = inode_id;
+ sargs.key.max_objectid = inode_id;
+
+ sargs.key.min_offset = 0;
+ sargs.key.max_offset = ~0ULL;
+ sargs.key.min_transid = 0;
+ sargs.key.max_transid = ~0ULL;
+ sargs.key.min_type = GRUB_BTRFS_ITEM_TYPE_INODE_REF;
+ sargs.key.max_type = GRUB_BTRFS_ITEM_TYPE_INODE_REF;
+
+ if (ioctl (fd, BTRFS_IOC_TREE_SEARCH, &sargs) < 0)
+ return NULL;
+
+ if (sargs.key.nr_items == 0)
+ return NULL;
+
+ nid = (grub_uint64_t *) (sargs.buf + 16);
+ inode_id = *nid;
+
+ ir = (struct grub_btrfs_inode_ref *) (sargs.buf + 32);
+ name = ir->name;
+ namelen = ir->n;
+ }
+ old = ret;
+ ret = xmalloc (namelen + (old ? strlen (old) : 0) + 2);
+ ret[0] = '/';
+ memcpy (ret + 1, name, namelen);
+ if (old)
+ {
+ strcpy (ret + 1 + namelen, old);
+ free (old);
+ }
+ else
+ ret[1+namelen] = '\0';
+ }
+ if (!ret)
+ return xstrdup ("/");
+ return ret;
+}
+
+
char **
grub_find_root_devices_from_mountinfo (const char *dir, char **relroot)
{
for (i = entry_len - 1; i >= 0; i--)
{
char **ret = NULL;
+ char *fs_prefix = NULL;
if (!*entries[i].device)
continue;
if (relroot)
{
if (!slash)
- *relroot = xasprintf ("/@%s", entries[i].enc_root);
+ fs_prefix = xasprintf ("/@%s", entries[i].enc_root);
else if (strchr (slash + 1, '@'))
- *relroot = xasprintf ("/%s%s", slash + 1, entries[i].enc_root);
+ fs_prefix = xasprintf ("/%s%s", slash + 1, entries[i].enc_root);
else
- *relroot = xasprintf ("/%s@%s", slash + 1, entries[i].enc_root);
+ fs_prefix = xasprintf ("/%s@%s", slash + 1,
+ entries[i].enc_root);
}
}
else if (grub_strcmp (entries[i].fstype, "btrfs") == 0)
{
ret = grub_find_root_devices_from_btrfs (dir);
- if (relroot)
- {
- char *ptr;
- *relroot = xmalloc (strlen (entries[i].enc_root) +
- 2 + strlen (dir));
- ptr = grub_stpcpy (*relroot, entries[i].enc_root);
- if (strlen (dir) > strlen (entries[i].enc_path))
- {
- while (ptr > *relroot && *(ptr - 1) == '/')
- ptr--;
- if (dir[strlen (entries[i].enc_path)] != '/')
- *ptr++ = '/';
- ptr = grub_stpcpy (ptr, dir + strlen (entries[i].enc_path));
- }
- *ptr = 0;
- }
+ fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path);
}
if (!ret)
{
ret = xmalloc (2 * sizeof (ret[0]));
ret[0] = strdup (entries[i].device);
ret[1] = 0;
- if (relroot)
- *relroot = strdup (entries[i].enc_root);
}
- free (buf);
- free (entries);
- fclose (fp);
- return ret;
+ if (!fs_prefix)
+ fs_prefix = entries[i].enc_root;
+ if (relroot)
+ {
+ char *ptr;
+ grub_size_t enc_root_len = strlen (fs_prefix);
+ grub_size_t enc_path_len = strlen (entries[i].enc_path);
+ grub_size_t dir_strlen = strlen (dir);
+ *relroot = xmalloc (enc_root_len +
+ 2 + dir_strlen);
+ ptr = grub_stpcpy (*relroot, fs_prefix);
+ if (dir_strlen > enc_path_len)
+ {
+ while (ptr > *relroot && *(ptr - 1) == '/')
+ ptr--;
+ if (dir[enc_path_len] != '/')
+ *ptr++ = '/';
+ ptr = grub_stpcpy (ptr, dir + enc_path_len);
+ }
+ *ptr = 0;
+ }
+ if (fs_prefix != entries[i].enc_root)
+ free (fs_prefix);
+ free (buf);
+ free (entries);
+ fclose (fp);
+ return ret;
}
free (buf);
return grub_dev;
}
+
+char *
+grub_make_system_path_relative_to_its_root_os (const char *path)
+{
+ char *bind = NULL;
+ grub_size_t len;
+ grub_free (grub_find_root_devices_from_mountinfo (path, &bind));
+ if (bind && bind[0])
+ {
+ len = strlen (bind);
+ while (len > 0 && bind[len - 1] == '/')
+ {
+ bind[len - 1] = '\0';
+ len--;
+ }
+ return bind;
+ }
+ grub_free (bind);
+ return NULL;
+}
if (p == NULL)
grub_util_error (_("failed to get canonical path of `%s'"), path);
- /* For ZFS sub-pool filesystems, could be extended to others (btrfs?). */
+#ifdef __linux__
+ ret = grub_make_system_path_relative_to_its_root_os (p);
+ if (ret)
+ return ret;
+#endif
+
+ /* For ZFS sub-pool filesystems. */
#ifndef __HAIKU__
{
char *dummy;
if (offset == 0)
{
free (buf);
-#ifdef __linux__
- {
- char *bind;
- grub_free (grub_find_root_devices_from_mountinfo (buf2, &bind));
- if (bind && bind[0] && bind[1])
- {
- buf3 = bind;
- goto parsedir;
- }
- grub_free (bind);
- }
-#endif
free (buf2);
if (poolfs)
return xasprintf ("/%s/@", poolfs);
free (buf);
buf3 = xstrdup (buf2 + offset);
buf2[offset] = 0;
-#ifdef __linux__
- {
- char *bind;
- grub_free (grub_find_root_devices_from_mountinfo (buf2, &bind));
- if (bind && bind[0] && bind[1])
- {
- char *temp = buf3;
- buf3 = grub_xasprintf ("%s%s%s", bind, buf3[0] == '/' ?"":"/", buf3);
- grub_free (temp);
- }
- grub_free (bind);
- }
-#endif
free (buf2);
-#ifdef __linux__
- parsedir:
-#endif
/* Remove trailing slashes, return empty string if root directory. */
len = strlen (buf3);
while (len > 0 && buf3[len - 1] == '/')
--- /dev/null
+/* btrfs.c - B-tree file system. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2010,2011,2012,2013 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_BTRFS_H
+#define GRUB_BTRFS_H 1
+
+enum
+ {
+ GRUB_BTRFS_ITEM_TYPE_INODE_ITEM = 0x01,
+ GRUB_BTRFS_ITEM_TYPE_INODE_REF = 0x0c,
+ 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_BACKREF = 0x90,
+ GRUB_BTRFS_ITEM_TYPE_DEVICE = 0xd8,
+ GRUB_BTRFS_ITEM_TYPE_CHUNK = 0xe4
+ };
+
+enum
+ {
+ GRUB_BTRFS_ROOT_VOL_OBJECTID = 5,
+ GRUB_BTRFS_TREE_ROOT_OBJECTID = 0x100,
+ };
+
+struct grub_btrfs_root_item
+{
+ grub_uint8_t dummy[0xb0];
+ grub_uint64_t tree;
+ grub_uint64_t inode;
+};
+
+struct grub_btrfs_key
+{
+ grub_uint64_t object_id;
+ grub_uint8_t type;
+ grub_uint64_t offset;
+} __attribute__ ((packed));
+
+
+struct grub_btrfs_root_backref
+{
+ grub_uint64_t inode_id;
+ grub_uint64_t seqnr;
+ grub_uint16_t n;
+ char name[0];
+};
+
+struct grub_btrfs_inode_ref
+{
+ grub_uint64_t idxid;
+ grub_uint16_t n;
+ char name[0];
+};
+
+#endif
char **grub_guess_root_devices (const char *dir);
int grub_util_get_dev_abstraction (const char *os_dev);
char *grub_make_system_path_relative_to_its_root (const char *path);
+char *
+grub_make_system_path_relative_to_its_root_os (const char *path);
char *grub_util_get_grub_dev (const char *os_dev);
#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
void grub_util_follow_gpart_up (const char *name, grub_disk_addr_t *off_out,