c5c941fb20b5d06ccacfadb311a3b17df7d42bc8
[external/syslinux.git] / packaging / 0001-btrfs-Correctly-determine-the-installation-subvolume.patch
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
5
6 There are multiple ways to set up subvolumes in btrfs.  Use a general
7 determination method which works for all schemes.
8
9 Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
10 ---
11  extlinux/btrfs.h |  105 +++++++++++++++++++
12  extlinux/main.c  |  303 ++++++++++++++++++++++++++++++++++++++++++++++++------
13  2 files changed, 376 insertions(+), 32 deletions(-)
14
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
19 @@ -1,6 +1,9 @@
20  #ifndef _BTRFS_H_
21  #define _BTRFS_H_
22  
23 +#include <asm/types.h>
24 +#include <linux/ioctl.h>
25 +
26  #define BTRFS_SUPER_MAGIC 0x9123683E
27  #define BTRFS_SUPER_INFO_OFFSET (64 * 1024)
28  #define BTRFS_SUPER_INFO_SIZE 4096
29 @@ -8,6 +11,40 @@
30  #define BTRFS_CSUM_SIZE 32
31  #define BTRFS_FSID_SIZE 16
32  
33 +typedef __u64 u64;
34 +typedef __u32 u32;
35 +typedef __u16 u16;
36 +typedef __u8 u8;
37 +typedef u64 __le64;
38 +typedef u16 __le16;
39 +
40 +#define BTRFS_ROOT_BACKREF_KEY  144
41 +#define BTRFS_ROOT_TREE_DIR_OBJECTID 6ULL
42 +#define BTRFS_DIR_ITEM_KEY      84
43 +
44 +/*
45 + *  * this is used for both forward and backward root refs
46 + *   */
47 +struct btrfs_root_ref {
48 +        __le64 dirid;
49 +        __le64 sequence;
50 +        __le16 name_len;
51 +} __attribute__ ((__packed__));
52 +
53 +struct btrfs_disk_key {
54 +        __le64 objectid;
55 +        u8 type;
56 +        __le64 offset;
57 +} __attribute__ ((__packed__));
58 +
59 +struct btrfs_dir_item {
60 +        struct btrfs_disk_key location;
61 +        __le64 transid;
62 +        __le16 data_len;
63 +        __le16 name_len;
64 +        u8 type;
65 +} __attribute__ ((__packed__));
66 +
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 {
71         u64 magic;
72  } __attribute__ ((__packed__));
73  
74 +
75 +#define BTRFS_IOCTL_MAGIC 0x94
76 +#define BTRFS_VOL_NAME_MAX 255
77 +#define BTRFS_PATH_NAME_MAX 4087
78 +
79 +struct btrfs_ioctl_vol_args {
80 +       __s64 fd;
81 +       char name[BTRFS_PATH_NAME_MAX + 1];
82 +};
83 +
84 +struct btrfs_ioctl_search_key {
85 +       /* which root are we searching.  0 is the tree of tree roots */
86 +       __u64 tree_id;
87 +
88 +       /* keys returned will be >= min and <= max */
89 +       __u64 min_objectid;
90 +       __u64 max_objectid;
91 +
92 +       /* keys returned will be >= min and <= max */
93 +       __u64 min_offset;
94 +       __u64 max_offset;
95 +
96 +       /* max and min transids to search for */
97 +       __u64 min_transid;
98 +       __u64 max_transid;
99 +
100 +       /* keys returned will be >= min and <= max */
101 +       __u32 min_type;
102 +       __u32 max_type;
103 +
104 +       /*
105 +        * how many items did userland ask for, and how many are we
106 +        * returning
107 +        */
108 +       __u32 nr_items;
109 +
110 +       /* align to 64 bits */
111 +       __u32 unused;
112 +
113 +       /* some extra for later */
114 +       __u64 unused1;
115 +       __u64 unused2;
116 +       __u64 unused3;
117 +       __u64 unused4;
118 +};
119 +
120 +struct btrfs_ioctl_search_header {
121 +       __u64 transid;
122 +       __u64 objectid;
123 +       __u64 offset;
124 +       __u32 type;
125 +       __u32 len;
126 +} __attribute__((may_alias));
127 +
128 +#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key))
129 +/*
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
133 + */
134 +struct btrfs_ioctl_search_args {
135 +       struct btrfs_ioctl_search_key key;
136 +       char buf[BTRFS_SEARCH_ARGS_BUFSIZE];
137 +};
138 +
139 +#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
140 +                                   struct btrfs_ioctl_search_args)
141 +
142  #endif
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
147 @@ -20,12 +20,12 @@
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;
152  #include <alloca.h>
153  #include <errno.h>
154  #include <fcntl.h>
155  #include <stdio.h>
156  #include <unistd.h>
157 +#include <dirent.h>
158  #ifndef __KLIBC__
159  #include <mntent.h>
160  #endif
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];
168  
169 @@ -494,6 +493,270 @@ int btrfs_install_file(const char *path, int devfd, struct stat *rst)
170      return 0;
171  }
172  
173 +/*
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
179 + *       */
180 +static int test_issubvolume(char *path)
181 +{
182 +
183 +        struct stat     st;
184 +        int             res;
185 +
186 +        res = stat(path, &st);
187 +        if(res < 0 )
188 +                return -1;
189 +
190 +        return (st.st_ino == 256) && S_ISDIR(st.st_mode);
191 +
192 +}
193 +
194 +/*
195 + * Get file handle for a file or dir
196 + */
197 +static int open_file_or_dir(const char *fname)
198 +{
199 +        int ret;
200 +        struct stat st;
201 +        DIR *dirstream;
202 +        int fd;
203 +
204 +        ret = stat(fname, &st);
205 +        if (ret < 0) {
206 +                return -1;
207 +        }
208 +        if (S_ISDIR(st.st_mode)) {
209 +                dirstream = opendir(fname);
210 +                if (!dirstream) {
211 +                        return -2;
212 +                }
213 +                fd = dirfd(dirstream);
214 +        } else {
215 +                fd = open(fname, O_RDWR);
216 +        }
217 +        if (fd < 0) {
218 +                return -3;
219 +        }
220 +        return fd;
221 +}
222 +
223 +/*
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
227 + */
228 +static char * get_default_subvol(char * rootdir, char * subvol)
229 +{
230 +    struct btrfs_ioctl_search_args args;
231 +    struct btrfs_ioctl_search_key *sk = &args.key;
232 +    struct btrfs_ioctl_search_header *sh;
233 +    int ret, i;
234 +    int fd;
235 +    struct btrfs_root_ref *ref;
236 +    struct btrfs_dir_item *dir_item;
237 +    unsigned long off = 0;
238 +    int name_len;
239 +    char *name;
240 +    u64 dir_id;
241 +    char dirname[4096];
242 +    u64 defaultsubvolid = 0;
243 +
244 +    ret = test_issubvolume(rootdir);
245 +    if (ret == 1) {
246 +        fd = open_file_or_dir(rootdir);
247 +        if (fd < 0) {
248 +            fprintf(stderr, "ERROR: failed to open %s\n", rootdir);
249 +        }
250 +        ret = fd;
251 +    }
252 +    if (ret <= 0) {
253 +        subvol[0] = '\0';
254 +        return NULL;
255 +    }
256 +
257 +    memset(&args, 0, sizeof(args));
258 +
259 +   /* search in the tree of tree roots */
260 +   sk->tree_id = 1;
261 +
262 +   /*
263 +    * set the min and max to backref keys.  The search will
264 +    * only send back this type of key now.
265 +    */
266 +   sk->max_type = BTRFS_DIR_ITEM_KEY;
267 +   sk->min_type = BTRFS_DIR_ITEM_KEY;
268 +
269 +   /*
270 +    * set all the other params to the max, we'll take any objectid
271 +    * and any trans
272 +    */
273 +   sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
274 +   sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
275 +
276 +   sk->max_offset = (u64)-1;
277 +   sk->min_offset = 0;
278 +   sk->max_transid = (u64)-1;
279 +
280 +   /* just a big number, doesn't matter much */
281 +   sk->nr_items = 4096;
282 +
283 +   while(1) {
284 +       ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
285 +       if (ret < 0) {
286 +           fprintf(stderr, "ERROR: can't perform the search\n");
287 +           subvol[0] = '\0';
288 +           return NULL;
289 +       }
290 +       /* the ioctl returns the number of item it found in nr_items */
291 +       if (sk->nr_items == 0) {
292 +           break;
293 +       }
294 +
295 +       off = 0;
296 +
297 +       /*
298 +        * for each item, pull the key out of the header and then
299 +        * read the root_ref item it contains
300 +        */
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);
308 +
309 +
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;
316 +                   break;
317 +               }
318 +           }
319 +           off += sh->len;
320 +
321 +           /*
322 +            * record the mins in sk so we can make sure the
323 +            * next search doesn't repeat this root
324 +            */
325 +           sk->min_objectid = sh->objectid;
326 +           sk->min_type = sh->type;
327 +           sk->max_type = sh->type;
328 +           sk->min_offset = sh->offset;
329 +       }
330 +       if (defaultsubvolid != 0)
331 +           break;
332 +       sk->nr_items = 4096;
333 +       /* this iteration is done, step forward one root for the next
334 +        * ioctl
335 +        */
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;
342 +       } else
343 +           break;
344 +   }
345 +
346 +   if (defaultsubvolid == 0) {
347 +       subvol[0] = '\0';
348 +       return NULL;
349 +   }
350 +
351 +   memset(&args, 0, sizeof(args));
352 +
353 +   /* search in the tree of tree roots */
354 +   sk->tree_id = 1;
355 +
356 +   /*
357 +    * set the min and max to backref keys.  The search will
358 +    * only send back this type of key now.
359 +    */
360 +   sk->max_type = BTRFS_ROOT_BACKREF_KEY;
361 +   sk->min_type = BTRFS_ROOT_BACKREF_KEY;
362 +
363 +   /*
364 +    * set all the other params to the max, we'll take any objectid
365 +    * and any trans
366 +    */
367 +   sk->max_objectid = (u64)-1;
368 +   sk->max_offset = (u64)-1;
369 +   sk->max_transid = (u64)-1;
370 +
371 +   /* just a big number, doesn't matter much */
372 +   sk->nr_items = 4096;
373 +
374 +   while(1) {
375 +       ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
376 +       if (ret < 0) {
377 +           fprintf(stderr, "ERROR: can't perform the search\n");
378 +           subvol[0] = '\0';
379 +           return NULL;
380 +       }
381 +       /* the ioctl returns the number of item it found in nr_items */
382 +       if (sk->nr_items == 0)
383 +           break;
384 +
385 +       off = 0;
386 +
387 +       /*
388 +        * for each item, pull the key out of the header and then
389 +        * read the root_ref item it contains
390 +        */
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;
399 +
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);
406 +                   break;
407 +               }
408 +
409 +           }
410 +
411 +           off += sh->len;
412 +
413 +           /*
414 +            * record the mins in sk so we can make sure the
415 +            * next search doesn't repeat this root
416 +            */
417 +           sk->min_objectid = sh->objectid;
418 +           sk->min_type = sh->type;
419 +           sk->min_offset = sh->offset;
420 +       }
421 +       if (subvol[0] != '\0')
422 +           break;
423 +       sk->nr_items = 4096;
424 +       /* this iteration is done, step forward one root for the next
425 +        * ioctl
426 +        */
427 +       if (sk->min_objectid < (u64)-1) {
428 +           sk->min_objectid++;
429 +           sk->min_type = BTRFS_ROOT_BACKREF_KEY;
430 +           sk->min_offset = 0;
431 +       } else
432 +           break;
433 +   }
434 +   return subvol;
435 +}
436 +
437  int install_file(const char *path, int devfd, struct stat *rst)
438  {
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) &&
443                     dst.st_dev == dev) {
444 -                   char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
445 -
446 -                   if (opt) {
447 -                       if (!subvol[0]) {
448 -                           char *tmp;
449 -
450 -                           strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
451 -                           tmp = strchr(subvol, 32);
452 -                           if (tmp)
453 -                               *tmp = '\0';
454 -                       }
455 -                       break; /* should break and let upper layer try again */
456 -                   } else
457 +                       if (!subvol[0]) {
458 +                           get_default_subvol(mnt->mnt_dir, subvol);
459 +                        }
460                         done = true;
461                 }
462                 break;
463 @@ -625,24 +878,10 @@ static const char *get_devname(const char *path)
464  
465  #else
466  
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 */
470 -       char parent[256];
471 -       char *tmp;
472 -
473 -       strcpy(parent, path);
474 -       tmp = strrchr(parent, '/');
475 -       if (tmp) {
476 -           *tmp = '\0';
477 -           fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
478 -           devname = get_devname(parent);
479 -       } else
480 -           devname = NULL;
481 -    }
482 +    devname = find_device("/proc/mounts", st.st_dev);
483      if (!devname) {
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);
488      }
489      if (!devname) {
490         fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
491 -- 
492 1.7.2.5
493