+From c46d7f1567870b9a2bb0a946af741a585e96fe67 Mon Sep 17 00:00:00 2001
+From: Yi Yang <yi.y.yang@intel.com>
+Date: Tue, 12 Jul 2011 14:53:50 +0800
+Subject: [PATCH] btrfs: Correctly determine the installation subvolume
+
+There are multiple ways to set up subvolumes in btrfs. Use a general
+determination method which works for all schemes.
+
+Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
+---
+ extlinux/btrfs.h | 105 +++++++++++++++++++
+ extlinux/main.c | 303 ++++++++++++++++++++++++++++++++++++++++++++++++------
+ 2 files changed, 376 insertions(+), 32 deletions(-)
+
+diff --git a/extlinux/btrfs.h b/extlinux/btrfs.h
+index 39a861a..be0c24e 100644
+--- a/extlinux/btrfs.h
++++ b/extlinux/btrfs.h
+@@ -1,6 +1,9 @@
+ #ifndef _BTRFS_H_
+ #define _BTRFS_H_
+
++#include <asm/types.h>
++#include <linux/ioctl.h>
++
+ #define BTRFS_SUPER_MAGIC 0x9123683E
+ #define BTRFS_SUPER_INFO_OFFSET (64 * 1024)
+ #define BTRFS_SUPER_INFO_SIZE 4096
+@@ -8,6 +11,40 @@
+ #define BTRFS_CSUM_SIZE 32
+ #define BTRFS_FSID_SIZE 16
+
++typedef __u64 u64;
++typedef __u32 u32;
++typedef __u16 u16;
++typedef __u8 u8;
++typedef u64 __le64;
++typedef u16 __le16;
++
++#define BTRFS_ROOT_BACKREF_KEY 144
++#define BTRFS_ROOT_TREE_DIR_OBJECTID 6ULL
++#define BTRFS_DIR_ITEM_KEY 84
++
++/*
++ * * this is used for both forward and backward root refs
++ * */
++struct btrfs_root_ref {
++ __le64 dirid;
++ __le64 sequence;
++ __le16 name_len;
++} __attribute__ ((__packed__));
++
++struct btrfs_disk_key {
++ __le64 objectid;
++ u8 type;
++ __le64 offset;
++} __attribute__ ((__packed__));
++
++struct btrfs_dir_item {
++ struct btrfs_disk_key location;
++ __le64 transid;
++ __le16 data_len;
++ __le16 name_len;
++ u8 type;
++} __attribute__ ((__packed__));
++
+ struct btrfs_super_block {
+ unsigned char csum[BTRFS_CSUM_SIZE];
+ /* the first 3 fields must match struct btrfs_header */
+@@ -19,4 +56,72 @@ struct btrfs_super_block {
+ u64 magic;
+ } __attribute__ ((__packed__));
+
++
++#define BTRFS_IOCTL_MAGIC 0x94
++#define BTRFS_VOL_NAME_MAX 255
++#define BTRFS_PATH_NAME_MAX 4087
++
++struct btrfs_ioctl_vol_args {
++ __s64 fd;
++ char name[BTRFS_PATH_NAME_MAX + 1];
++};
++
++struct btrfs_ioctl_search_key {
++ /* which root are we searching. 0 is the tree of tree roots */
++ __u64 tree_id;
++
++ /* keys returned will be >= min and <= max */
++ __u64 min_objectid;
++ __u64 max_objectid;
++
++ /* keys returned will be >= min and <= max */
++ __u64 min_offset;
++ __u64 max_offset;
++
++ /* max and min transids to search for */
++ __u64 min_transid;
++ __u64 max_transid;
++
++ /* keys returned will be >= min and <= max */
++ __u32 min_type;
++ __u32 max_type;
++
++ /*
++ * how many items did userland ask for, and how many are we
++ * returning
++ */
++ __u32 nr_items;
++
++ /* align to 64 bits */
++ __u32 unused;
++
++ /* some extra for later */
++ __u64 unused1;
++ __u64 unused2;
++ __u64 unused3;
++ __u64 unused4;
++};
++
++struct btrfs_ioctl_search_header {
++ __u64 transid;
++ __u64 objectid;
++ __u64 offset;
++ __u32 type;
++ __u32 len;
++} __attribute__((may_alias));
++
++#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key))
++/*
++ * the buf is an array of search headers where
++ * each header is followed by the actual item
++ * the type field is expanded to 32 bits for alignment
++ */
++struct btrfs_ioctl_search_args {
++ struct btrfs_ioctl_search_key key;
++ char buf[BTRFS_SEARCH_ARGS_BUFSIZE];
++};
++
++#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
++ struct btrfs_ioctl_search_args)
++
+ #endif
+diff --git a/extlinux/main.c b/extlinux/main.c
+index e5212a9..201fb02 100755
+--- a/extlinux/main.c
++++ b/extlinux/main.c
+@@ -20,12 +20,12 @@
+ #define _GNU_SOURCE /* Enable everything */
+ #include <inttypes.h>
+ /* This is needed to deal with the kernel headers imported into glibc 3.3.3. */
+-typedef uint64_t u64;
+ #include <alloca.h>
+ #include <errno.h>
+ #include <fcntl.h>
+ #include <stdio.h>
+ #include <unistd.h>
++#include <dirent.h>
+ #ifndef __KLIBC__
+ #include <mntent.h>
+ #endif
+@@ -65,7 +65,6 @@ typedef uint64_t u64;
+ boot image, the boot sector is from 0~512, the boot image starts after */
+ #define BTRFS_BOOTSECT_AREA 65536
+ #define BTRFS_EXTLINUX_OFFSET SECTOR_SIZE
+-#define BTRFS_SUBVOL_OPT "subvol="
+ #define BTRFS_SUBVOL_MAX 256 /* By btrfs specification */
+ static char subvol[BTRFS_SUBVOL_MAX];
+
+@@ -494,6 +493,270 @@ int btrfs_install_file(const char *path, int devfd, struct stat *rst)
+ return 0;
+ }
+
++/*
++ * * test if path is a subvolume:
++ * * this function return
++ * * 0-> path exists but it is not a subvolume
++ * * 1-> path exists and it is a subvolume
++ * * -1 -> path is unaccessible
++ * */
++static int test_issubvolume(char *path)
++{
++
++ struct stat st;
++ int res;
++
++ res = stat(path, &st);
++ if(res < 0 )
++ return -1;
++
++ return (st.st_ino == 256) && S_ISDIR(st.st_mode);
++
++}
++
++/*
++ * Get file handle for a file or dir
++ */
++static int open_file_or_dir(const char *fname)
++{
++ int ret;
++ struct stat st;
++ DIR *dirstream;
++ int fd;
++
++ ret = stat(fname, &st);
++ if (ret < 0) {
++ return -1;
++ }
++ if (S_ISDIR(st.st_mode)) {
++ dirstream = opendir(fname);
++ if (!dirstream) {
++ return -2;
++ }
++ fd = dirfd(dirstream);
++ } else {
++ fd = open(fname, O_RDWR);
++ }
++ if (fd < 0) {
++ return -3;
++ }
++ return fd;
++}
++
++/*
++ * Get the default subvolume of a btrfs filesystem
++ * rootdir: btrfs root dir
++ * subvol: this function will save the default subvolume name here
++ */
++static char * get_default_subvol(char * rootdir, char * subvol)
++{
++ struct btrfs_ioctl_search_args args;
++ struct btrfs_ioctl_search_key *sk = &args.key;
++ struct btrfs_ioctl_search_header *sh;
++ int ret, i;
++ int fd;
++ struct btrfs_root_ref *ref;
++ struct btrfs_dir_item *dir_item;
++ unsigned long off = 0;
++ int name_len;
++ char *name;
++ u64 dir_id;
++ char dirname[4096];
++ u64 defaultsubvolid = 0;
++
++ ret = test_issubvolume(rootdir);
++ if (ret == 1) {
++ fd = open_file_or_dir(rootdir);
++ if (fd < 0) {
++ fprintf(stderr, "ERROR: failed to open %s\n", rootdir);
++ }
++ ret = fd;
++ }
++ if (ret <= 0) {
++ subvol[0] = '\0';
++ return NULL;
++ }
++
++ memset(&args, 0, sizeof(args));
++
++ /* search in the tree of tree roots */
++ sk->tree_id = 1;
++
++ /*
++ * set the min and max to backref keys. The search will
++ * only send back this type of key now.
++ */
++ sk->max_type = BTRFS_DIR_ITEM_KEY;
++ sk->min_type = BTRFS_DIR_ITEM_KEY;
++
++ /*
++ * set all the other params to the max, we'll take any objectid
++ * and any trans
++ */
++ sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
++ sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
++
++ sk->max_offset = (u64)-1;
++ sk->min_offset = 0;
++ sk->max_transid = (u64)-1;
++
++ /* just a big number, doesn't matter much */
++ sk->nr_items = 4096;
++
++ while(1) {
++ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
++ if (ret < 0) {
++ fprintf(stderr, "ERROR: can't perform the search\n");
++ subvol[0] = '\0';
++ return NULL;
++ }
++ /* the ioctl returns the number of item it found in nr_items */
++ if (sk->nr_items == 0) {
++ break;
++ }
++
++ off = 0;
++
++ /*
++ * for each item, pull the key out of the header and then
++ * read the root_ref item it contains
++ */
++ for (i = 0; i < sk->nr_items; i++) {
++ sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
++ off += sizeof(*sh);
++ if (sh->type == BTRFS_DIR_ITEM_KEY) {
++ dir_item = (struct btrfs_dir_item *)(args.buf + off);
++ name_len = dir_item->name_len;
++ name = (char *)(dir_item + 1);
++
++
++ /*add_root(&root_lookup, sh->objectid, sh->offset,
++ dir_id, name, name_len);*/
++ strncpy(dirname, name, name_len);
++ dirname[name_len] = '\0';
++ if (strcmp(dirname, "default") == 0) {
++ defaultsubvolid = dir_item->location.objectid;
++ break;
++ }
++ }
++ off += sh->len;
++
++ /*
++ * record the mins in sk so we can make sure the
++ * next search doesn't repeat this root
++ */
++ sk->min_objectid = sh->objectid;
++ sk->min_type = sh->type;
++ sk->max_type = sh->type;
++ sk->min_offset = sh->offset;
++ }
++ if (defaultsubvolid != 0)
++ break;
++ sk->nr_items = 4096;
++ /* this iteration is done, step forward one root for the next
++ * ioctl
++ */
++ if (sk->min_objectid < (u64)-1) {
++ sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
++ sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
++ sk->max_type = BTRFS_ROOT_BACKREF_KEY;
++ sk->min_type = BTRFS_ROOT_BACKREF_KEY;
++ sk->min_offset = 0;
++ } else
++ break;
++ }
++
++ if (defaultsubvolid == 0) {
++ subvol[0] = '\0';
++ return NULL;
++ }
++
++ memset(&args, 0, sizeof(args));
++
++ /* search in the tree of tree roots */
++ sk->tree_id = 1;
++
++ /*
++ * set the min and max to backref keys. The search will
++ * only send back this type of key now.
++ */
++ sk->max_type = BTRFS_ROOT_BACKREF_KEY;
++ sk->min_type = BTRFS_ROOT_BACKREF_KEY;
++
++ /*
++ * set all the other params to the max, we'll take any objectid
++ * and any trans
++ */
++ sk->max_objectid = (u64)-1;
++ sk->max_offset = (u64)-1;
++ sk->max_transid = (u64)-1;
++
++ /* just a big number, doesn't matter much */
++ sk->nr_items = 4096;
++
++ while(1) {
++ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
++ if (ret < 0) {
++ fprintf(stderr, "ERROR: can't perform the search\n");
++ subvol[0] = '\0';
++ return NULL;
++ }
++ /* the ioctl returns the number of item it found in nr_items */
++ if (sk->nr_items == 0)
++ break;
++
++ off = 0;
++
++ /*
++ * for each item, pull the key out of the header and then
++ * read the root_ref item it contains
++ */
++ for (i = 0; i < sk->nr_items; i++) {
++ sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
++ off += sizeof(*sh);
++ if (sh->type == BTRFS_ROOT_BACKREF_KEY) {
++ ref = (struct btrfs_root_ref *)(args.buf + off);
++ name_len = ref->name_len;
++ name = (char *)(ref + 1);
++ dir_id = ref->dirid;
++
++ /*add_root(&root_lookup, sh->objectid, sh->offset,
++ dir_id, name, name_len);*/
++ if (sh->objectid == defaultsubvolid) {
++ strncpy(subvol, name, name_len);
++ subvol[name_len] = '\0';
++ dprintf("The default subvolume: %s, ID: %llu\n", subvol, sh->objectid);
++ break;
++ }
++
++ }
++
++ off += sh->len;
++
++ /*
++ * record the mins in sk so we can make sure the
++ * next search doesn't repeat this root
++ */
++ sk->min_objectid = sh->objectid;
++ sk->min_type = sh->type;
++ sk->min_offset = sh->offset;
++ }
++ if (subvol[0] != '\0')
++ break;
++ sk->nr_items = 4096;
++ /* this iteration is done, step forward one root for the next
++ * ioctl
++ */
++ if (sk->min_objectid < (u64)-1) {
++ sk->min_objectid++;
++ sk->min_type = BTRFS_ROOT_BACKREF_KEY;
++ sk->min_offset = 0;
++ } else
++ break;
++ }
++ return subvol;
++}
++
+ int install_file(const char *path, int devfd, struct stat *rst)
+ {
+ if (fs_type == EXT2 || fs_type == VFAT)
+@@ -548,19 +811,9 @@ static const char *find_device(const char *mtab_file, dev_t dev)
+ if (!strcmp(mnt->mnt_type, "btrfs") &&
+ !stat(mnt->mnt_dir, &dst) &&
+ dst.st_dev == dev) {
+- char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
+-
+- if (opt) {
+- if (!subvol[0]) {
+- char *tmp;
+-
+- strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
+- tmp = strchr(subvol, 32);
+- if (tmp)
+- *tmp = '\0';
+- }
+- break; /* should break and let upper layer try again */
+- } else
++ if (!subvol[0]) {
++ get_default_subvol(mnt->mnt_dir, subvol);
++ }
+ done = true;
+ }
+ break;
+@@ -625,24 +878,10 @@ static const char *get_devname(const char *path)
+
+ #else
+
+- /* check /etc/mtab first, since btrfs subvol info is only in here */
+- devname = find_device("/etc/mtab", st.st_dev);
+- if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
+- char parent[256];
+- char *tmp;
+-
+- strcpy(parent, path);
+- tmp = strrchr(parent, '/');
+- if (tmp) {
+- *tmp = '\0';
+- fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
+- devname = get_devname(parent);
+- } else
+- devname = NULL;
+- }
++ devname = find_device("/proc/mounts", st.st_dev);
+ if (!devname) {
+- /* Didn't find it in /etc/mtab, try /proc/mounts */
+- devname = find_device("/proc/mounts", st.st_dev);
++ /* Didn't find it in /proc/mounts, try /etc/mtab */
++ devname = find_device("/etc/mtab", st.st_dev);
+ }
+ if (!devname) {
+ fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
+--
+1.7.2.5
+