1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /*
3 * This file is part of libmount from util-linux project.
4 *
5 * Copyright (C) 2016 David Sterba <dsterba@suse.cz>
6 * Copyright (C) 2016 Stanislav Brabec <sbrabec@suse.cz>
7 *
8 * libmount is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
12 *
13 * Based on kernel ctree.h, rbtree.h and btrfs-progs.
14 */
15 #include <dirent.h>
16 #include <sys/ioctl.h>
17 #include <stdlib.h>
18 #include <stdint.h>
19 #include <linux/btrfs.h>
20
21 #include "mountP.h"
22 #include "bitops.h"
23
24
25 /* linux/btrfs.h lacks large parts of stuff needed for getting default
26 * sub-volume. Suppose that if BTRFS_DIR_ITEM_KEY is not defined, all
27 * declarations are still missing.
28 */
29 #ifndef BTRFS_DIR_ITEM_KEY
30
31 /*
32 * dir items are the name -> inode pointers in a directory. There is one
33 * for every name in a directory.
34 */
35 #define BTRFS_DIR_ITEM_KEY 84
36
37 /* holds pointers to all of the tree roots */
38 #define BTRFS_ROOT_TREE_OBJECTID 1ULL
39
40 /* directory objectid inside the root tree */
41 #define BTRFS_ROOT_TREE_DIR_OBJECTID 6ULL
42
43 /*
44 * the key defines the order in the tree, and so it also defines (optimal)
45 * block layout. objectid corresponds with the inode number. The flags
46 * tells us things about the object, and is a kind of stream selector.
47 * so for a given inode, keys with flags of 1 might refer to the inode
48 * data, flags of 2 may point to file data in the btree and flags == 3
49 * may point to extents.
50 *
51 * offset is the starting byte offset for this key in the stream.
52 *
53 * btrfs_disk_key is in disk byte order. struct btrfs_key is always
54 * in cpu native order. Otherwise they are identical and their sizes
55 * should be the same (ie both packed)
56 */
57 struct btrfs_disk_key {
58 uint64_t objectid; /* little endian */
59 uint8_t type;
60 uint64_t offset; /* little endian */
61 } __attribute__ ((__packed__));
62
63 struct btrfs_dir_item {
64 struct btrfs_disk_key location;
65 uint64_t transid; /* little endian */
66 uint16_t data_len; /* little endian */
67 uint16_t name_len; /* little endian */
68 uint8_t type;
69 } __attribute__ ((__packed__));
70
71 #define BTRFS_SETGET_STACK_FUNCS(name, type, member, bits) \
72 static inline uint##bits##_t btrfs_##name(const type *s) \
73 { \
74 return le##bits##_to_cpu(s->member); \
75 }
76
77 /* struct btrfs_disk_key */
78 BTRFS_SETGET_STACK_FUNCS(disk_key_objectid, struct btrfs_disk_key,
79 objectid, 64)
80
81 BTRFS_SETGET_STACK_FUNCS(stack_dir_name_len, struct btrfs_dir_item, name_len, 16)
82
83 /*
84 Red Black Trees
85 */
86 struct rb_node {
87 unsigned long __rb_parent_color;
88 struct rb_node *rb_right;
89 struct rb_node *rb_left;
90 } __attribute__((aligned(sizeof(long))));
91 /* The alignment might seem pointless, but allegedly CRIS needs it */
92
93 #endif /* BTRFS_DIR_ITEM_KEY */
94
95 /*
96 * btrfs_get_default_subvol_id:
97 * @path: Path to mounted btrfs volume
98 *
99 * Searches for the btrfs default subvolume id.
100 *
101 * Returns: default subvolume id or UINT64_MAX (-1) in case of no
102 * default subvolume or error. In case of error, errno is set
103 * properly.
104 */
btrfs_get_default_subvol_id(const char * path)105 uint64_t btrfs_get_default_subvol_id(const char *path)
106 {
107 int iocret;
108 int fd;
109 DIR *dirstream;
110 struct btrfs_ioctl_search_args args;
111 struct btrfs_ioctl_search_key *sk = &args.key;
112 struct btrfs_ioctl_search_header *sh;
113 uint64_t found = UINT64_MAX;
114
115 dirstream = opendir(path);
116 if (!dirstream) {
117 DBG(BTRFS, ul_debug("opendir() failed for \"%s\" [errno=%d %m]", path, errno));
118 return UINT64_MAX;
119 }
120 fd = dirfd(dirstream);
121 if (fd < 0) {
122 DBG(BTRFS, ul_debug("dirfd(opendir()) failed for \"%s\" [errno=%d %m]", path, errno));
123 goto out;
124 }
125
126 memset(&args, 0, sizeof(args));
127 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
128 sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
129 sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
130 sk->min_type = BTRFS_DIR_ITEM_KEY;
131 sk->max_type = BTRFS_DIR_ITEM_KEY;
132 sk->max_offset = UINT64_MAX;
133 sk->max_transid = UINT64_MAX;
134 sk->nr_items = 1;
135
136 iocret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
137 if (iocret < 0) {
138 DBG(BTRFS, ul_debug("ioctl() failed for \"%s\" [errno=%d %m]", path, errno));
139 goto out;
140 }
141
142 /* the ioctl returns the number of items it found in nr_items */
143 if (sk->nr_items == 0) {
144 DBG(BTRFS, ul_debug("root tree dir object id not found"));
145 goto out;
146 }
147 DBG(BTRFS, ul_debug("found %d root tree dir object id items", sk->nr_items));
148
149 sh = (struct btrfs_ioctl_search_header *)args.buf;
150
151 if (sh->type == BTRFS_DIR_ITEM_KEY) {
152 struct btrfs_dir_item *di;
153 int name_len;
154 char *name;
155
156 di = (struct btrfs_dir_item *)(sh + 1);
157 name_len = btrfs_stack_dir_name_len(di);
158 name = (char *)(di + 1);
159
160 if (!strncmp("default", name, name_len)) {
161 found = btrfs_disk_key_objectid(&di->location);
162 DBG(BTRFS, ul_debug("\"default\" id is %llu", (unsigned long long)found));
163 } else {
164 DBG(BTRFS, ul_debug("\"default\" id not found in tree root"));
165 goto out;
166 }
167 } else {
168 DBG(BTRFS, ul_debug("unexpected type found: %d", (int)sh->type));
169 goto out;
170 }
171
172 out:
173 closedir(dirstream);
174 return found;
175 }
176