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