1 From c46d7f1567870b9a2bb0a946af741a585e96fe67 Mon Sep 17 00:00:00 2001
2 From: Yi Yang <yi.y.yang@intel.com>
3 Date: Tue, 12 Jul 2011 14:53:50 +0800
4 Subject: [PATCH] btrfs: Correctly determine the installation subvolume
6 There are multiple ways to set up subvolumes in btrfs. Use a general
7 determination method which works for all schemes.
9 Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
11 extlinux/btrfs.h | 105 +++++++++++++++++++
12 extlinux/main.c | 303 ++++++++++++++++++++++++++++++++++++++++++++++++------
13 2 files changed, 376 insertions(+), 32 deletions(-)
15 diff --git a/extlinux/btrfs.h b/extlinux/btrfs.h
16 index 39a861a..be0c24e 100644
17 --- a/extlinux/btrfs.h
18 +++ b/extlinux/btrfs.h
23 +#include <asm/types.h>
24 +#include <linux/ioctl.h>
26 #define BTRFS_SUPER_MAGIC 0x9123683E
27 #define BTRFS_SUPER_INFO_OFFSET (64 * 1024)
28 #define BTRFS_SUPER_INFO_SIZE 4096
30 #define BTRFS_CSUM_SIZE 32
31 #define BTRFS_FSID_SIZE 16
40 +#define BTRFS_ROOT_BACKREF_KEY 144
41 +#define BTRFS_ROOT_TREE_DIR_OBJECTID 6ULL
42 +#define BTRFS_DIR_ITEM_KEY 84
45 + * * this is used for both forward and backward root refs
47 +struct btrfs_root_ref {
51 +} __attribute__ ((__packed__));
53 +struct btrfs_disk_key {
57 +} __attribute__ ((__packed__));
59 +struct btrfs_dir_item {
60 + struct btrfs_disk_key location;
65 +} __attribute__ ((__packed__));
67 struct btrfs_super_block {
68 unsigned char csum[BTRFS_CSUM_SIZE];
69 /* the first 3 fields must match struct btrfs_header */
70 @@ -19,4 +56,72 @@ struct btrfs_super_block {
72 } __attribute__ ((__packed__));
75 +#define BTRFS_IOCTL_MAGIC 0x94
76 +#define BTRFS_VOL_NAME_MAX 255
77 +#define BTRFS_PATH_NAME_MAX 4087
79 +struct btrfs_ioctl_vol_args {
81 + char name[BTRFS_PATH_NAME_MAX + 1];
84 +struct btrfs_ioctl_search_key {
85 + /* which root are we searching. 0 is the tree of tree roots */
88 + /* keys returned will be >= min and <= max */
92 + /* keys returned will be >= min and <= max */
96 + /* max and min transids to search for */
100 + /* keys returned will be >= min and <= max */
105 + * how many items did userland ask for, and how many are we
110 + /* align to 64 bits */
113 + /* some extra for later */
120 +struct btrfs_ioctl_search_header {
126 +} __attribute__((may_alias));
128 +#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key))
130 + * the buf is an array of search headers where
131 + * each header is followed by the actual item
132 + * the type field is expanded to 32 bits for alignment
134 +struct btrfs_ioctl_search_args {
135 + struct btrfs_ioctl_search_key key;
136 + char buf[BTRFS_SEARCH_ARGS_BUFSIZE];
139 +#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
140 + struct btrfs_ioctl_search_args)
143 diff --git a/extlinux/main.c b/extlinux/main.c
144 index e5212a9..201fb02 100755
145 --- a/extlinux/main.c
146 +++ b/extlinux/main.c
148 #define _GNU_SOURCE /* Enable everything */
149 #include <inttypes.h>
150 /* This is needed to deal with the kernel headers imported into glibc 3.3.3. */
151 -typedef uint64_t u64;
161 @@ -65,7 +65,6 @@ typedef uint64_t u64;
162 boot image, the boot sector is from 0~512, the boot image starts after */
163 #define BTRFS_BOOTSECT_AREA 65536
164 #define BTRFS_EXTLINUX_OFFSET SECTOR_SIZE
165 -#define BTRFS_SUBVOL_OPT "subvol="
166 #define BTRFS_SUBVOL_MAX 256 /* By btrfs specification */
167 static char subvol[BTRFS_SUBVOL_MAX];
169 @@ -494,6 +493,270 @@ int btrfs_install_file(const char *path, int devfd, struct stat *rst)
174 + * * test if path is a subvolume:
175 + * * this function return
176 + * * 0-> path exists but it is not a subvolume
177 + * * 1-> path exists and it is a subvolume
178 + * * -1 -> path is unaccessible
180 +static int test_issubvolume(char *path)
186 + res = stat(path, &st);
190 + return (st.st_ino == 256) && S_ISDIR(st.st_mode);
195 + * Get file handle for a file or dir
197 +static int open_file_or_dir(const char *fname)
204 + ret = stat(fname, &st);
208 + if (S_ISDIR(st.st_mode)) {
209 + dirstream = opendir(fname);
213 + fd = dirfd(dirstream);
215 + fd = open(fname, O_RDWR);
224 + * Get the default subvolume of a btrfs filesystem
225 + * rootdir: btrfs root dir
226 + * subvol: this function will save the default subvolume name here
228 +static char * get_default_subvol(char * rootdir, char * subvol)
230 + struct btrfs_ioctl_search_args args;
231 + struct btrfs_ioctl_search_key *sk = &args.key;
232 + struct btrfs_ioctl_search_header *sh;
235 + struct btrfs_root_ref *ref;
236 + struct btrfs_dir_item *dir_item;
237 + unsigned long off = 0;
241 + char dirname[4096];
242 + u64 defaultsubvolid = 0;
244 + ret = test_issubvolume(rootdir);
246 + fd = open_file_or_dir(rootdir);
248 + fprintf(stderr, "ERROR: failed to open %s\n", rootdir);
257 + memset(&args, 0, sizeof(args));
259 + /* search in the tree of tree roots */
263 + * set the min and max to backref keys. The search will
264 + * only send back this type of key now.
266 + sk->max_type = BTRFS_DIR_ITEM_KEY;
267 + sk->min_type = BTRFS_DIR_ITEM_KEY;
270 + * set all the other params to the max, we'll take any objectid
273 + sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
274 + sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
276 + sk->max_offset = (u64)-1;
277 + sk->min_offset = 0;
278 + sk->max_transid = (u64)-1;
280 + /* just a big number, doesn't matter much */
281 + sk->nr_items = 4096;
284 + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
286 + fprintf(stderr, "ERROR: can't perform the search\n");
290 + /* the ioctl returns the number of item it found in nr_items */
291 + if (sk->nr_items == 0) {
298 + * for each item, pull the key out of the header and then
299 + * read the root_ref item it contains
301 + for (i = 0; i < sk->nr_items; i++) {
302 + sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
303 + off += sizeof(*sh);
304 + if (sh->type == BTRFS_DIR_ITEM_KEY) {
305 + dir_item = (struct btrfs_dir_item *)(args.buf + off);
306 + name_len = dir_item->name_len;
307 + name = (char *)(dir_item + 1);
310 + /*add_root(&root_lookup, sh->objectid, sh->offset,
311 + dir_id, name, name_len);*/
312 + strncpy(dirname, name, name_len);
313 + dirname[name_len] = '\0';
314 + if (strcmp(dirname, "default") == 0) {
315 + defaultsubvolid = dir_item->location.objectid;
322 + * record the mins in sk so we can make sure the
323 + * next search doesn't repeat this root
325 + sk->min_objectid = sh->objectid;
326 + sk->min_type = sh->type;
327 + sk->max_type = sh->type;
328 + sk->min_offset = sh->offset;
330 + if (defaultsubvolid != 0)
332 + sk->nr_items = 4096;
333 + /* this iteration is done, step forward one root for the next
336 + if (sk->min_objectid < (u64)-1) {
337 + sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
338 + sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
339 + sk->max_type = BTRFS_ROOT_BACKREF_KEY;
340 + sk->min_type = BTRFS_ROOT_BACKREF_KEY;
341 + sk->min_offset = 0;
346 + if (defaultsubvolid == 0) {
351 + memset(&args, 0, sizeof(args));
353 + /* search in the tree of tree roots */
357 + * set the min and max to backref keys. The search will
358 + * only send back this type of key now.
360 + sk->max_type = BTRFS_ROOT_BACKREF_KEY;
361 + sk->min_type = BTRFS_ROOT_BACKREF_KEY;
364 + * set all the other params to the max, we'll take any objectid
367 + sk->max_objectid = (u64)-1;
368 + sk->max_offset = (u64)-1;
369 + sk->max_transid = (u64)-1;
371 + /* just a big number, doesn't matter much */
372 + sk->nr_items = 4096;
375 + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
377 + fprintf(stderr, "ERROR: can't perform the search\n");
381 + /* the ioctl returns the number of item it found in nr_items */
382 + if (sk->nr_items == 0)
388 + * for each item, pull the key out of the header and then
389 + * read the root_ref item it contains
391 + for (i = 0; i < sk->nr_items; i++) {
392 + sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
393 + off += sizeof(*sh);
394 + if (sh->type == BTRFS_ROOT_BACKREF_KEY) {
395 + ref = (struct btrfs_root_ref *)(args.buf + off);
396 + name_len = ref->name_len;
397 + name = (char *)(ref + 1);
398 + dir_id = ref->dirid;
400 + /*add_root(&root_lookup, sh->objectid, sh->offset,
401 + dir_id, name, name_len);*/
402 + if (sh->objectid == defaultsubvolid) {
403 + strncpy(subvol, name, name_len);
404 + subvol[name_len] = '\0';
405 + dprintf("The default subvolume: %s, ID: %llu\n", subvol, sh->objectid);
414 + * record the mins in sk so we can make sure the
415 + * next search doesn't repeat this root
417 + sk->min_objectid = sh->objectid;
418 + sk->min_type = sh->type;
419 + sk->min_offset = sh->offset;
421 + if (subvol[0] != '\0')
423 + sk->nr_items = 4096;
424 + /* this iteration is done, step forward one root for the next
427 + if (sk->min_objectid < (u64)-1) {
428 + sk->min_objectid++;
429 + sk->min_type = BTRFS_ROOT_BACKREF_KEY;
430 + sk->min_offset = 0;
437 int install_file(const char *path, int devfd, struct stat *rst)
439 if (fs_type == EXT2 || fs_type == VFAT)
440 @@ -548,19 +811,9 @@ static const char *find_device(const char *mtab_file, dev_t dev)
441 if (!strcmp(mnt->mnt_type, "btrfs") &&
442 !stat(mnt->mnt_dir, &dst) &&
444 - char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
450 - strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
451 - tmp = strchr(subvol, 32);
455 - break; /* should break and let upper layer try again */
458 + get_default_subvol(mnt->mnt_dir, subvol);
463 @@ -625,24 +878,10 @@ static const char *get_devname(const char *path)
467 - /* check /etc/mtab first, since btrfs subvol info is only in here */
468 - devname = find_device("/etc/mtab", st.st_dev);
469 - if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
473 - strcpy(parent, path);
474 - tmp = strrchr(parent, '/');
477 - fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
478 - devname = get_devname(parent);
482 + devname = find_device("/proc/mounts", st.st_dev);
484 - /* Didn't find it in /etc/mtab, try /proc/mounts */
485 - devname = find_device("/proc/mounts", st.st_dev);
486 + /* Didn't find it in /proc/mounts, try /etc/mtab */
487 + devname = find_device("/etc/mtab", st.st_dev);
490 fprintf(stderr, "%s: cannot find device for path %s\n", program, path);