Bug Summary

File:build-scan/../src/basic/btrfs-util.c
Warning:line 1047, column 42
Array access (from variable 'qgroups') results in a null pointer dereference

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name btrfs-util.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I src/basic/libbasic.a.p -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/udev -I ../src/udev -I src/libudev -I ../src/libudev -I src/core -I ../src/core -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I src/libsystemd-network -I ../src/libsystemd-network -I . -I .. -I /usr/include/blkid -I /usr/include/libmount -D _FILE_OFFSET_BITS=64 -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility default -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/basic/btrfs-util.c
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include <errno(*__errno_location ()).h>
4#include <fcntl.h>
5#include <inttypes.h>
6#include <linux1/fs.h>
7#include <linux1/loop.h>
8#include <stddef.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/ioctl.h>
13#include <sys/stat.h>
14#include <sys/statfs.h>
15#include <sys/sysmacros.h>
16#include <unistd.h>
17
18#if HAVE_LINUX_BTRFS_H1
19#include <linux1/btrfs.h>
20#endif
21
22#include "alloc-util.h"
23#include "blockdev-util.h"
24#include "btrfs-ctree.h"
25#include "btrfs-util.h"
26#include "chattr-util.h"
27#include "copy.h"
28#include "device-nodes.h"
29#include "fd-util.h"
30#include "fileio.h"
31#include "io-util.h"
32#include "macro.h"
33#include "missing.h"
34#include "path-util.h"
35#include "rm-rf.h"
36#include "smack-util.h"
37#include "sparse-endian.h"
38#include "stat-util.h"
39#include "string-util.h"
40#include "time-util.h"
41#include "util.h"
42
43/* WARNING: Be careful with file system ioctls! When we get an fd, we
44 * need to make sure it either refers to only a regular file or
45 * directory, or that it is located on btrfs, before invoking any
46 * btrfs ioctls. The ioctl numbers are reused by some device drivers
47 * (such as DRM), and hence might have bad effects when invoked on
48 * device nodes (that reference drivers) rather than fds to normal
49 * files or directories. */
50
51static int validate_subvolume_name(const char *name) {
52
53 if (!filename_is_valid(name))
54 return -EINVAL22;
55
56 if (strlen(name) > BTRFS_SUBVOL_NAME_MAX4039)
57 return -E2BIG7;
58
59 return 0;
60}
61
62static int open_parent(const char *path, int flags) {
63 _cleanup_free___attribute__((cleanup(freep))) char *parent = NULL((void*)0);
64 int fd;
65
66 assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("path"), "../src/basic/btrfs-util.c", 66
, __PRETTY_FUNCTION__); } while (0)
;
67
68 parent = dirname_malloc(path);
69 if (!parent)
70 return -ENOMEM12;
71
72 fd = open(parent, flags);
73 if (fd < 0)
74 return -errno(*__errno_location ());
75
76 return fd;
77}
78
79static int extract_subvolume_name(const char *path, const char **subvolume) {
80 const char *fn;
81 int r;
82
83 assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("path"), "../src/basic/btrfs-util.c", 83
, __PRETTY_FUNCTION__); } while (0)
;
84 assert(subvolume)do { if ((__builtin_expect(!!(!(subvolume)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("subvolume"), "../src/basic/btrfs-util.c"
, 84, __PRETTY_FUNCTION__); } while (0)
;
85
86 fn = basename(path);
87
88 r = validate_subvolume_name(fn);
89 if (r < 0)
90 return r;
91
92 *subvolume = fn;
93 return 0;
94}
95
96int btrfs_is_filesystem(int fd) {
97 struct statfs sfs;
98
99 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 99, __PRETTY_FUNCTION__); } while (0)
;
100
101 if (fstatfs(fd, &sfs) < 0)
102 return -errno(*__errno_location ());
103
104 return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)(sfs.f_type == (typeof(sfs.f_type)) 0x9123683E);
105}
106
107int btrfs_is_subvol_fd(int fd) {
108 struct stat st;
109
110 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 110, __PRETTY_FUNCTION__); } while (0)
;
111
112 /* On btrfs subvolumes always have the inode 256 */
113
114 if (fstat(fd, &st) < 0)
115 return -errno(*__errno_location ());
116
117 if (!S_ISDIR(st.st_mode)((((st.st_mode)) & 0170000) == (0040000)) || st.st_ino != 256)
118 return 0;
119
120 return btrfs_is_filesystem(fd);
121}
122
123int btrfs_is_subvol(const char *path) {
124 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
125
126 assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("path"), "../src/basic/btrfs-util.c", 126
, __PRETTY_FUNCTION__); } while (0)
;
127
128 fd = open(path, O_RDONLY00|O_NOCTTY0400|O_CLOEXEC02000000|O_DIRECTORY0200000);
129 if (fd < 0)
130 return -errno(*__errno_location ());
131
132 return btrfs_is_subvol_fd(fd);
133}
134
135int btrfs_subvol_make(const char *path) {
136 struct btrfs_ioctl_vol_args args = {};
137 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
138 const char *subvolume;
139 int r;
140
141 assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("path"), "../src/basic/btrfs-util.c", 141
, __PRETTY_FUNCTION__); } while (0)
;
142
143 r = extract_subvolume_name(path, &subvolume);
144 if (r < 0)
145 return r;
146
147 fd = open_parent(path, O_RDONLY00|O_NOCTTY0400|O_CLOEXEC02000000|O_DIRECTORY0200000);
148 if (fd < 0)
149 return fd;
150
151 strncpy(args.name, subvolume, sizeof(args.name)-1);
152
153 if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE(((1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((14)) << 0) | ((((sizeof(struct btrfs_ioctl_vol_args
)))) << ((0 +8)+8)))
, &args) < 0)
154 return -errno(*__errno_location ());
155
156 return 0;
157}
158
159int btrfs_subvol_set_read_only_fd(int fd, bool_Bool b) {
160 uint64_t flags, nflags;
161 struct stat st;
162
163 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 163, __PRETTY_FUNCTION__); } while (0)
;
164
165 if (fstat(fd, &st) < 0)
166 return -errno(*__errno_location ());
167
168 if (!S_ISDIR(st.st_mode)((((st.st_mode)) & 0170000) == (0040000)) || st.st_ino != 256)
169 return -EINVAL22;
170
171 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS(((2U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((25)) << 0) | ((((sizeof(__u64)))) << ((0 +8
)+8)))
, &flags) < 0)
172 return -errno(*__errno_location ());
173
174 if (b)
175 nflags = flags | BTRFS_SUBVOL_RDONLY(1ULL << 1);
176 else
177 nflags = flags & ~BTRFS_SUBVOL_RDONLY(1ULL << 1);
178
179 if (flags == nflags)
180 return 0;
181
182 if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS(((1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((26)) << 0) | ((((sizeof(__u64)))) << ((0 +8
)+8)))
, &nflags) < 0)
183 return -errno(*__errno_location ());
184
185 return 0;
186}
187
188int btrfs_subvol_set_read_only(const char *path, bool_Bool b) {
189 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
190
191 fd = open(path, O_RDONLY00|O_NOCTTY0400|O_CLOEXEC02000000|O_DIRECTORY0200000);
192 if (fd < 0)
193 return -errno(*__errno_location ());
194
195 return btrfs_subvol_set_read_only_fd(fd, b);
196}
197
198int btrfs_subvol_get_read_only_fd(int fd) {
199 uint64_t flags;
200 struct stat st;
201
202 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 202, __PRETTY_FUNCTION__); } while (0)
;
203
204 if (fstat(fd, &st) < 0)
205 return -errno(*__errno_location ());
206
207 if (!S_ISDIR(st.st_mode)((((st.st_mode)) & 0170000) == (0040000)) || st.st_ino != 256)
208 return -EINVAL22;
209
210 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS(((2U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((25)) << 0) | ((((sizeof(__u64)))) << ((0 +8
)+8)))
, &flags) < 0)
211 return -errno(*__errno_location ());
212
213 return !!(flags & BTRFS_SUBVOL_RDONLY(1ULL << 1));
214}
215
216int btrfs_reflink(int infd, int outfd) {
217 int r;
218
219 assert(infd >= 0)do { if ((__builtin_expect(!!(!(infd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("infd >= 0"), "../src/basic/btrfs-util.c"
, 219, __PRETTY_FUNCTION__); } while (0)
;
220 assert(outfd >= 0)do { if ((__builtin_expect(!!(!(outfd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("outfd >= 0"), "../src/basic/btrfs-util.c"
, 220, __PRETTY_FUNCTION__); } while (0)
;
221
222 /* Make sure we invoke the ioctl on a regular file, so that no device driver accidentally gets it. */
223
224 r = fd_verify_regular(outfd);
225 if (r < 0)
226 return r;
227
228 if (ioctl(outfd, BTRFS_IOC_CLONE(((1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((9)) << 0) | ((((sizeof(int)))) << ((0 +8)+8
)))
, infd) < 0)
229 return -errno(*__errno_location ());
230
231 return 0;
232}
233
234int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
235 struct btrfs_ioctl_clone_range_args args = {
236 .src_fd = infd,
237 .src_offset = in_offset,
238 .src_length = sz,
239 .dest_offset = out_offset,
240 };
241 int r;
242
243 assert(infd >= 0)do { if ((__builtin_expect(!!(!(infd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("infd >= 0"), "../src/basic/btrfs-util.c"
, 243, __PRETTY_FUNCTION__); } while (0)
;
244 assert(outfd >= 0)do { if ((__builtin_expect(!!(!(outfd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("outfd >= 0"), "../src/basic/btrfs-util.c"
, 244, __PRETTY_FUNCTION__); } while (0)
;
245 assert(sz > 0)do { if ((__builtin_expect(!!(!(sz > 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("sz > 0"), "../src/basic/btrfs-util.c"
, 245, __PRETTY_FUNCTION__); } while (0)
;
246
247 r = fd_verify_regular(outfd);
248 if (r < 0)
249 return r;
250
251 if (ioctl(outfd, BTRFS_IOC_CLONE_RANGE(((1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((13)) << 0) | ((((sizeof(struct btrfs_ioctl_clone_range_args
)))) << ((0 +8)+8)))
, &args) < 0)
252 return -errno(*__errno_location ());
253
254 return 0;
255}
256
257int btrfs_get_block_device_fd(int fd, dev_t *dev) {
258 struct btrfs_ioctl_fs_info_args fsi = {};
259 uint64_t id;
260 int r;
261
262 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 262, __PRETTY_FUNCTION__); } while (0)
;
263 assert(dev)do { if ((__builtin_expect(!!(!(dev)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("dev"), "../src/basic/btrfs-util.c", 263
, __PRETTY_FUNCTION__); } while (0)
;
264
265 r = btrfs_is_filesystem(fd);
266 if (r < 0)
267 return r;
268 if (!r)
269 return -ENOTTY25;
270
271 if (ioctl(fd, BTRFS_IOC_FS_INFO(((2U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((31)) << 0) | ((((sizeof(struct btrfs_ioctl_fs_info_args
)))) << ((0 +8)+8)))
, &fsi) < 0)
272 return -errno(*__errno_location ());
273
274 /* We won't do this for btrfs RAID */
275 if (fsi.num_devices != 1) {
276 *dev = 0;
277 return 0;
278 }
279
280 for (id = 1; id <= fsi.max_id; id++) {
281 struct btrfs_ioctl_dev_info_args di = {
282 .devid = id,
283 };
284 struct stat st;
285
286 if (ioctl(fd, BTRFS_IOC_DEV_INFO(((2U|1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +
8)) | (((30)) << 0) | ((((sizeof(struct btrfs_ioctl_dev_info_args
)))) << ((0 +8)+8)))
, &di) < 0) {
287 if (errno(*__errno_location ()) == ENODEV19)
288 continue;
289
290 return -errno(*__errno_location ());
291 }
292
293 if (stat((char*) di.path, &st) < 0)
294 return -errno(*__errno_location ());
295
296 if (!S_ISBLK(st.st_mode)((((st.st_mode)) & 0170000) == (0060000)))
297 return -ENODEV19;
298
299 if (major(st.st_rdev)gnu_dev_major (st.st_rdev) == 0)
300 return -ENODEV19;
301
302 *dev = st.st_rdev;
303 return 1;
304 }
305
306 return -ENODEV19;
307}
308
309int btrfs_get_block_device(const char *path, dev_t *dev) {
310 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
311
312 assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("path"), "../src/basic/btrfs-util.c", 312
, __PRETTY_FUNCTION__); } while (0)
;
313 assert(dev)do { if ((__builtin_expect(!!(!(dev)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("dev"), "../src/basic/btrfs-util.c", 313
, __PRETTY_FUNCTION__); } while (0)
;
314
315 fd = open(path, O_RDONLY00|O_NOCTTY0400|O_CLOEXEC02000000);
316 if (fd < 0)
317 return -errno(*__errno_location ());
318
319 return btrfs_get_block_device_fd(fd, dev);
320}
321
322int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
323 struct btrfs_ioctl_ino_lookup_args args = {
324 .objectid = BTRFS_FIRST_FREE_OBJECTID256
325 };
326 int r;
327
328 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 328, __PRETTY_FUNCTION__); } while (0)
;
329 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/basic/btrfs-util.c", 329
, __PRETTY_FUNCTION__); } while (0)
;
330
331 r = btrfs_is_filesystem(fd);
332 if (r < 0)
333 return r;
334 if (!r)
335 return -ENOTTY25;
336
337 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP(((2U|1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +
8)) | (((18)) << 0) | ((((sizeof(struct btrfs_ioctl_ino_lookup_args
)))) << ((0 +8)+8)))
, &args) < 0)
338 return -errno(*__errno_location ());
339
340 *ret = args.treeid;
341 return 0;
342}
343
344int btrfs_subvol_get_id(int fd, const char *subvol, uint64_t *ret) {
345 _cleanup_close___attribute__((cleanup(closep))) int subvol_fd = -1;
346
347 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 347, __PRETTY_FUNCTION__); } while (0)
;
348 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/basic/btrfs-util.c", 348
, __PRETTY_FUNCTION__); } while (0)
;
349
350 subvol_fd = openat(fd, subvol, O_RDONLY00|O_CLOEXEC02000000|O_NOCTTY0400|O_NOFOLLOW0400000);
351 if (subvol_fd < 0)
352 return -errno(*__errno_location ());
353
354 return btrfs_subvol_get_id_fd(subvol_fd, ret);
355}
356
357static bool_Bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
358 assert(args)do { if ((__builtin_expect(!!(!(args)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("args"), "../src/basic/btrfs-util.c", 358
, __PRETTY_FUNCTION__); } while (0)
;
359
360 /* the objectid, type, offset together make up the btrfs key,
361 * which is considered a single 136byte integer when
362 * comparing. This call increases the counter by one, dealing
363 * with the overflow between the overflows */
364
365 if (args->key.min_offset < (uint64_t) -1) {
366 args->key.min_offset++;
367 return true1;
368 }
369
370 if (args->key.min_type < (uint8_t) -1) {
371 args->key.min_type++;
372 args->key.min_offset = 0;
373 return true1;
374 }
375
376 if (args->key.min_objectid < (uint64_t) -1) {
377 args->key.min_objectid++;
378 args->key.min_offset = 0;
379 args->key.min_type = 0;
380 return true1;
381 }
382
383 return 0;
384}
385
386static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
387 assert(args)do { if ((__builtin_expect(!!(!(args)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("args"), "../src/basic/btrfs-util.c", 387
, __PRETTY_FUNCTION__); } while (0)
;
388 assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("h"), "../src/basic/btrfs-util.c", 388, __PRETTY_FUNCTION__
); } while (0)
;
389
390 args->key.min_objectid = h->objectid;
391 args->key.min_type = h->type;
392 args->key.min_offset = h->offset;
393}
394
395static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
396 assert(args)do { if ((__builtin_expect(!!(!(args)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("args"), "../src/basic/btrfs-util.c", 396
, __PRETTY_FUNCTION__); } while (0)
;
397
398 /* Compare min and max */
399
400 if (args->key.min_objectid < args->key.max_objectid)
401 return -1;
402 if (args->key.min_objectid > args->key.max_objectid)
403 return 1;
404
405 if (args->key.min_type < args->key.max_type)
406 return -1;
407 if (args->key.min_type > args->key.max_type)
408 return 1;
409
410 if (args->key.min_offset < args->key.max_offset)
411 return -1;
412 if (args->key.min_offset > args->key.max_offset)
413 return 1;
414
415 return 0;
416}
417
418#define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args)for ((i) = 0, (sh) = (const struct btrfs_ioctl_search_header*
) (args).buf; (i) < (args).key.nr_items; (i)++, (sh) = (const
struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof
(struct btrfs_ioctl_search_header) + (sh)->len))
\
419 for ((i) = 0, \
420 (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
421 (i) < (args).key.nr_items; \
422 (i)++, \
423 (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
424
425#define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh)((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header
)))
\
426 ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
427
428int btrfs_subvol_get_info_fd(int fd, uint64_t subvol_id, BtrfsSubvolInfo *ret) {
429 struct btrfs_ioctl_search_args args = {
430 /* Tree of tree roots */
431 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID1,
432
433 /* Look precisely for the subvolume items */
434 .key.min_type = BTRFS_ROOT_ITEM_KEY132,
435 .key.max_type = BTRFS_ROOT_ITEM_KEY132,
436
437 .key.min_offset = 0,
438 .key.max_offset = (uint64_t) -1,
439
440 /* No restrictions on the other components */
441 .key.min_transid = 0,
442 .key.max_transid = (uint64_t) -1,
443 };
444
445 bool_Bool found = false0;
446 int r;
447
448 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 448, __PRETTY_FUNCTION__); } while (0)
;
449 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/basic/btrfs-util.c", 449
, __PRETTY_FUNCTION__); } while (0)
;
450
451 if (subvol_id == 0) {
452 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
453 if (r < 0)
454 return r;
455 } else {
456 r = btrfs_is_filesystem(fd);
457 if (r < 0)
458 return r;
459 if (!r)
460 return -ENOTTY25;
461 }
462
463 args.key.min_objectid = args.key.max_objectid = subvol_id;
464
465 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
466 const struct btrfs_ioctl_search_header *sh;
467 unsigned i;
468
469 args.key.nr_items = 256;
470 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH(((2U|1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +
8)) | (((17)) << 0) | ((((sizeof(struct btrfs_ioctl_search_args
)))) << ((0 +8)+8)))
, &args) < 0)
471 return -errno(*__errno_location ());
472
473 if (args.key.nr_items <= 0)
474 break;
475
476 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args)for ((i) = 0, (sh) = (const struct btrfs_ioctl_search_header*
) (args).buf; (i) < (args).key.nr_items; (i)++, (sh) = (const
struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof
(struct btrfs_ioctl_search_header) + (sh)->len))
{
477
478 const struct btrfs_root_item *ri;
479
480 /* Make sure we start the next search at least from this entry */
481 btrfs_ioctl_search_args_set(&args, sh);
482
483 if (sh->objectid != subvol_id)
484 continue;
485 if (sh->type != BTRFS_ROOT_ITEM_KEY132)
486 continue;
487
488 /* Older versions of the struct lacked the otime setting */
489 if (sh->len < offsetof(struct btrfs_root_item, otime)__builtin_offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
490 continue;
491
492 ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh)((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header
)))
;
493
494 ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC((usec_t) 1000000ULL) +
495 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC((nsec_t) 1000ULL);
496
497 ret->subvol_id = subvol_id;
498 ret->read_only = le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY(1ULL << 0);
499
500 assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid))GCC diagnostic push ; GCC diagnostic ignored "-Wdeclaration-after-statement"
; struct _assert_struct_3 { char x[(sizeof(ri->uuid) == sizeof
(ret->uuid)) ? 0 : -1]; }; GCC diagnostic pop
;
501 memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
502 memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
503
504 found = true1;
505 goto finish;
506 }
507
508 /* Increase search key by one, to read the next item, if we can. */
509 if (!btrfs_ioctl_search_args_inc(&args))
510 break;
511 }
512
513finish:
514 if (!found)
515 return -ENODATA61;
516
517 return 0;
518}
519
520int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *ret) {
521
522 struct btrfs_ioctl_search_args args = {
523 /* Tree of quota items */
524 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID8ULL,
525
526 /* The object ID is always 0 */
527 .key.min_objectid = 0,
528 .key.max_objectid = 0,
529
530 /* Look precisely for the quota items */
531 .key.min_type = BTRFS_QGROUP_STATUS_KEY240,
532 .key.max_type = BTRFS_QGROUP_LIMIT_KEY244,
533
534 /* No restrictions on the other components */
535 .key.min_transid = 0,
536 .key.max_transid = (uint64_t) -1,
537 };
538
539 bool_Bool found_info = false0, found_limit = false0;
540 int r;
541
542 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 542, __PRETTY_FUNCTION__); } while (0)
;
543 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/basic/btrfs-util.c", 543
, __PRETTY_FUNCTION__); } while (0)
;
544
545 if (qgroupid == 0) {
546 r = btrfs_subvol_get_id_fd(fd, &qgroupid);
547 if (r < 0)
548 return r;
549 } else {
550 r = btrfs_is_filesystem(fd);
551 if (r < 0)
552 return r;
553 if (!r)
554 return -ENOTTY25;
555 }
556
557 args.key.min_offset = args.key.max_offset = qgroupid;
558
559 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
560 const struct btrfs_ioctl_search_header *sh;
561 unsigned i;
562
563 args.key.nr_items = 256;
564 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH(((2U|1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +
8)) | (((17)) << 0) | ((((sizeof(struct btrfs_ioctl_search_args
)))) << ((0 +8)+8)))
, &args) < 0) {
565 if (errno(*__errno_location ()) == ENOENT2) /* quota tree is missing: quota disabled */
566 break;
567
568 return -errno(*__errno_location ());
569 }
570
571 if (args.key.nr_items <= 0)
572 break;
573
574 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args)for ((i) = 0, (sh) = (const struct btrfs_ioctl_search_header*
) (args).buf; (i) < (args).key.nr_items; (i)++, (sh) = (const
struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof
(struct btrfs_ioctl_search_header) + (sh)->len))
{
575
576 /* Make sure we start the next search at least from this entry */
577 btrfs_ioctl_search_args_set(&args, sh);
578
579 if (sh->objectid != 0)
580 continue;
581 if (sh->offset != qgroupid)
582 continue;
583
584 if (sh->type == BTRFS_QGROUP_INFO_KEY242) {
585 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh)((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header
)))
;
586
587 ret->referenced = le64toh(qii->rfer);
588 ret->exclusive = le64toh(qii->excl);
589
590 found_info = true1;
591
592 } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY244) {
593 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh)((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header
)))
;
594
595 if (le64toh(qli->flags) & BTRFS_QGROUP_LIMIT_MAX_RFER(1ULL << 0))
596 ret->referenced_max = le64toh(qli->max_rfer);
597 else
598 ret->referenced_max = (uint64_t) -1;
599
600 if (le64toh(qli->flags) & BTRFS_QGROUP_LIMIT_MAX_EXCL(1ULL << 1))
601 ret->exclusive_max = le64toh(qli->max_excl);
602 else
603 ret->exclusive_max = (uint64_t) -1;
604
605 found_limit = true1;
606 }
607
608 if (found_info && found_limit)
609 goto finish;
610 }
611
612 /* Increase search key by one, to read the next item, if we can. */
613 if (!btrfs_ioctl_search_args_inc(&args))
614 break;
615 }
616
617finish:
618 if (!found_limit && !found_info)
619 return -ENODATA61;
620
621 if (!found_info) {
622 ret->referenced = (uint64_t) -1;
623 ret->exclusive = (uint64_t) -1;
624 }
625
626 if (!found_limit) {
627 ret->referenced_max = (uint64_t) -1;
628 ret->exclusive_max = (uint64_t) -1;
629 }
630
631 return 0;
632}
633
634int btrfs_qgroup_get_quota(const char *path, uint64_t qgroupid, BtrfsQuotaInfo *ret) {
635 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
636
637 fd = open(path, O_RDONLY00|O_CLOEXEC02000000|O_NOCTTY0400|O_NOFOLLOW0400000);
638 if (fd < 0)
639 return -errno(*__errno_location ());
640
641 return btrfs_qgroup_get_quota_fd(fd, qgroupid, ret);
642}
643
644int btrfs_subvol_find_subtree_qgroup(int fd, uint64_t subvol_id, uint64_t *ret) {
645 uint64_t level, lowest = (uint64_t) -1, lowest_qgroupid = 0;
646 _cleanup_free___attribute__((cleanup(freep))) uint64_t *qgroups = NULL((void*)0);
647 int r, n, i;
648
649 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 649, __PRETTY_FUNCTION__); } while (0)
;
650 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/basic/btrfs-util.c", 650
, __PRETTY_FUNCTION__); } while (0)
;
651
652 /* This finds the "subtree" qgroup for a specific
653 * subvolume. This only works for subvolumes that have been
654 * prepared with btrfs_subvol_auto_qgroup_fd() with
655 * insert_intermediary_qgroup=true (or equivalent). For others
656 * it will return the leaf qgroup instead. The two cases may
657 * be distuingished via the return value, which is 1 in case
658 * an appropriate "subtree" qgroup was found, and 0
659 * otherwise. */
660
661 if (subvol_id == 0) {
662 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
663 if (r < 0)
664 return r;
665 }
666
667 r = btrfs_qgroupid_split(subvol_id, &level, NULL((void*)0));
668 if (r < 0)
669 return r;
670 if (level != 0) /* Input must be a leaf qgroup */
671 return -EINVAL22;
672
673 n = btrfs_qgroup_find_parents(fd, subvol_id, &qgroups);
674 if (n < 0)
675 return n;
676
677 for (i = 0; i < n; i++) {
678 uint64_t id;
679
680 r = btrfs_qgroupid_split(qgroups[i], &level, &id);
681 if (r < 0)
682 return r;
683
684 if (id != subvol_id)
685 continue;
686
687 if (lowest == (uint64_t) -1 || level < lowest) {
688 lowest_qgroupid = qgroups[i];
689 lowest = level;
690 }
691 }
692
693 if (lowest == (uint64_t) -1) {
694 /* No suitable higher-level qgroup found, let's return
695 * the leaf qgroup instead, and indicate that with the
696 * return value. */
697
698 *ret = subvol_id;
699 return 0;
700 }
701
702 *ret = lowest_qgroupid;
703 return 1;
704}
705
706int btrfs_subvol_get_subtree_quota_fd(int fd, uint64_t subvol_id, BtrfsQuotaInfo *ret) {
707 uint64_t qgroupid;
708 int r;
709
710 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 710, __PRETTY_FUNCTION__); } while (0)
;
711 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/basic/btrfs-util.c", 711
, __PRETTY_FUNCTION__); } while (0)
;
712
713 /* This determines the quota data of the qgroup with the
714 * lowest level, that shares the id part with the specified
715 * subvolume. This is useful for determining the quota data
716 * for entire subvolume subtrees, as long as the subtrees have
717 * been set up with btrfs_qgroup_subvol_auto_fd() or in a
718 * compatible way */
719
720 r = btrfs_subvol_find_subtree_qgroup(fd, subvol_id, &qgroupid);
721 if (r < 0)
722 return r;
723
724 return btrfs_qgroup_get_quota_fd(fd, qgroupid, ret);
725}
726
727int btrfs_subvol_get_subtree_quota(const char *path, uint64_t subvol_id, BtrfsQuotaInfo *ret) {
728 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
729
730 fd = open(path, O_RDONLY00|O_CLOEXEC02000000|O_NOCTTY0400|O_NOFOLLOW0400000);
731 if (fd < 0)
732 return -errno(*__errno_location ());
733
734 return btrfs_subvol_get_subtree_quota_fd(fd, subvol_id, ret);
735}
736
737int btrfs_defrag_fd(int fd) {
738 int r;
739
740 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 740, __PRETTY_FUNCTION__); } while (0)
;
741
742 r = fd_verify_regular(fd);
743 if (r < 0)
744 return r;
745
746 if (ioctl(fd, BTRFS_IOC_DEFRAG(((1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((2)) << 0) | ((((sizeof(struct btrfs_ioctl_vol_args
)))) << ((0 +8)+8)))
, NULL((void*)0)) < 0)
747 return -errno(*__errno_location ());
748
749 return 0;
750}
751
752int btrfs_defrag(const char *p) {
753 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
754
755 fd = open(p, O_RDWR02|O_CLOEXEC02000000|O_NOCTTY0400|O_NOFOLLOW0400000);
756 if (fd < 0)
757 return -errno(*__errno_location ());
758
759 return btrfs_defrag_fd(fd);
760}
761
762int btrfs_quota_enable_fd(int fd, bool_Bool b) {
763 struct btrfs_ioctl_quota_ctl_args args = {
764 .cmd = b ? BTRFS_QUOTA_CTL_ENABLE1 : BTRFS_QUOTA_CTL_DISABLE2,
765 };
766 int r;
767
768 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 768, __PRETTY_FUNCTION__); } while (0)
;
769
770 r = btrfs_is_filesystem(fd);
771 if (r < 0)
772 return r;
773 if (!r)
774 return -ENOTTY25;
775
776 if (ioctl(fd, BTRFS_IOC_QUOTA_CTL(((2U|1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +
8)) | (((40)) << 0) | ((((sizeof(struct btrfs_ioctl_quota_ctl_args
)))) << ((0 +8)+8)))
, &args) < 0)
777 return -errno(*__errno_location ());
778
779 return 0;
780}
781
782int btrfs_quota_enable(const char *path, bool_Bool b) {
783 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
784
785 fd = open(path, O_RDONLY00|O_CLOEXEC02000000|O_NOCTTY0400|O_NOFOLLOW0400000);
786 if (fd < 0)
787 return -errno(*__errno_location ());
788
789 return btrfs_quota_enable_fd(fd, b);
790}
791
792int btrfs_qgroup_set_limit_fd(int fd, uint64_t qgroupid, uint64_t referenced_max) {
793
794 struct btrfs_ioctl_qgroup_limit_args args = {
795 .lim.max_rfer = referenced_max,
796 .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER(1ULL << 0),
797 };
798 unsigned c;
799 int r;
800
801 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 801, __PRETTY_FUNCTION__); } while (0)
;
802
803 if (qgroupid == 0) {
804 r = btrfs_subvol_get_id_fd(fd, &qgroupid);
805 if (r < 0)
806 return r;
807 } else {
808 r = btrfs_is_filesystem(fd);
809 if (r < 0)
810 return r;
811 if (!r)
812 return -ENOTTY25;
813 }
814
815 args.qgroupid = qgroupid;
816
817 for (c = 0;; c++) {
818 if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT(((2U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((43)) << 0) | ((((sizeof(struct btrfs_ioctl_qgroup_limit_args
)))) << ((0 +8)+8)))
, &args) < 0) {
819
820 if (errno(*__errno_location ()) == EBUSY16 && c < 10) {
821 (void) btrfs_quota_scan_wait(fd);
822 continue;
823 }
824
825 return -errno(*__errno_location ());
826 }
827
828 break;
829 }
830
831 return 0;
832}
833
834int btrfs_qgroup_set_limit(const char *path, uint64_t qgroupid, uint64_t referenced_max) {
835 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
836
837 fd = open(path, O_RDONLY00|O_CLOEXEC02000000|O_NOCTTY0400|O_NOFOLLOW0400000);
838 if (fd < 0)
839 return -errno(*__errno_location ());
840
841 return btrfs_qgroup_set_limit_fd(fd, qgroupid, referenced_max);
842}
843
844int btrfs_subvol_set_subtree_quota_limit_fd(int fd, uint64_t subvol_id, uint64_t referenced_max) {
845 uint64_t qgroupid;
846 int r;
847
848 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 848, __PRETTY_FUNCTION__); } while (0)
;
849
850 r = btrfs_subvol_find_subtree_qgroup(fd, subvol_id, &qgroupid);
851 if (r < 0)
852 return r;
853
854 return btrfs_qgroup_set_limit_fd(fd, qgroupid, referenced_max);
855}
856
857int btrfs_subvol_set_subtree_quota_limit(const char *path, uint64_t subvol_id, uint64_t referenced_max) {
858 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
859
860 fd = open(path, O_RDONLY00|O_CLOEXEC02000000|O_NOCTTY0400|O_NOFOLLOW0400000);
861 if (fd < 0)
862 return -errno(*__errno_location ());
863
864 return btrfs_subvol_set_subtree_quota_limit_fd(fd, subvol_id, referenced_max);
865}
866
867int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool_Bool grow_only) {
868 struct btrfs_ioctl_vol_args args = {};
869 char p[SYS_BLOCK_PATH_MAX("/loop/backing_file")((sizeof("""/sys/dev/block/""") - 1) + (2+(sizeof(dev_t) <=
1 ? 3 : sizeof(dev_t) <= 2 ? 5 : sizeof(dev_t) <= 4 ? 10
: sizeof(dev_t) <= 8 ? 20 : sizeof(int[-2*(sizeof(dev_t) >
8)]))) + 1 + (2+(sizeof(dev_t) <= 1 ? 3 : sizeof(dev_t) <=
2 ? 5 : sizeof(dev_t) <= 4 ? 10 : sizeof(dev_t) <= 8 ?
20 : sizeof(int[-2*(sizeof(dev_t) > 8)]))) + strlen_ptr("/loop/backing_file"
))
];
870 _cleanup_free___attribute__((cleanup(freep))) char *backing = NULL((void*)0);
871 _cleanup_close___attribute__((cleanup(closep))) int loop_fd = -1, backing_fd = -1;
872 struct stat st;
873 dev_t dev = 0;
874 int r;
875
876 /* In contrast to btrfs quota ioctls ftruncate() cannot make sense of "infinity" or file sizes > 2^31 */
877 if (!FILE_SIZE_VALID(new_size))
878 return -EINVAL22;
879
880 /* btrfs cannot handle file systems < 16M, hence use this as minimum */
881 if (new_size < 16*1024*1024)
882 new_size = 16*1024*1024;
883
884 r = btrfs_get_block_device_fd(fd, &dev);
885 if (r < 0)
886 return r;
887 if (r == 0)
888 return -ENODEV19;
889
890 xsprintf_sys_block_path(p, "/loop/backing_file", dev)do { if ((__builtin_expect(!!(!(((size_t) snprintf(p, __extension__
(__builtin_choose_expr( !__builtin_types_compatible_p(typeof
(p), typeof(&*(p))), sizeof(p)/sizeof((p)[0]), ((void)0))
), "/sys/dev/block/%u:%u%s", gnu_dev_major (dev), gnu_dev_minor
(dev), strempty("/loop/backing_file")) < (__extension__ (
__builtin_choose_expr( !__builtin_types_compatible_p(typeof(p
), typeof(&*(p))), sizeof(p)/sizeof((p)[0]), ((void)0))))
))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("xsprintf: "
"p" "[] must be big enough"), "../src/basic/btrfs-util.c", 890
, __PRETTY_FUNCTION__); } while (0)
;
891 r = read_one_line_file(p, &backing);
892 if (r == -ENOENT2)
893 return -ENODEV19;
894 if (r < 0)
895 return r;
896 if (isempty(backing) || !path_is_absolute(backing))
897 return -ENODEV19;
898
899 backing_fd = open(backing, O_RDWR02|O_CLOEXEC02000000|O_NOCTTY0400);
900 if (backing_fd < 0)
901 return -errno(*__errno_location ());
902
903 if (fstat(backing_fd, &st) < 0)
904 return -errno(*__errno_location ());
905 if (!S_ISREG(st.st_mode)((((st.st_mode)) & 0170000) == (0100000)))
906 return -ENODEV19;
907
908 if (new_size == (uint64_t) st.st_size)
909 return 0;
910
911 if (grow_only && new_size < (uint64_t) st.st_size)
912 return -EINVAL22;
913
914 xsprintf_sys_block_path(p, NULL, dev)do { if ((__builtin_expect(!!(!(((size_t) snprintf(p, __extension__
(__builtin_choose_expr( !__builtin_types_compatible_p(typeof
(p), typeof(&*(p))), sizeof(p)/sizeof((p)[0]), ((void)0))
), "/sys/dev/block/%u:%u%s", gnu_dev_major (dev), gnu_dev_minor
(dev), strempty(((void*)0))) < (__extension__ (__builtin_choose_expr
( !__builtin_types_compatible_p(typeof(p), typeof(&*(p)))
, sizeof(p)/sizeof((p)[0]), ((void)0))))))),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("xsprintf: " "p" "[] must be big enough"
), "../src/basic/btrfs-util.c", 914, __PRETTY_FUNCTION__); } while
(0)
;
915 loop_fd = open(p, O_RDWR02|O_CLOEXEC02000000|O_NOCTTY0400);
916 if (loop_fd < 0)
917 return -errno(*__errno_location ());
918
919 if (snprintf(args.name, sizeof(args.name), "%" PRIu64"l" "u", new_size) >= (int) sizeof(args.name))
920 return -EINVAL22;
921
922 if (new_size < (uint64_t) st.st_size) {
923 /* Decrease size: first decrease btrfs size, then shorten loopback */
924 if (ioctl(fd, BTRFS_IOC_RESIZE(((1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((3)) << 0) | ((((sizeof(struct btrfs_ioctl_vol_args
)))) << ((0 +8)+8)))
, &args) < 0)
925 return -errno(*__errno_location ());
926 }
927
928 if (ftruncate(backing_fd, new_size) < 0)
929 return -errno(*__errno_location ());
930
931 if (ioctl(loop_fd, LOOP_SET_CAPACITY0x4C07, 0) < 0)
932 return -errno(*__errno_location ());
933
934 if (new_size > (uint64_t) st.st_size) {
935 /* Increase size: first enlarge loopback, then increase btrfs size */
936 if (ioctl(fd, BTRFS_IOC_RESIZE(((1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((3)) << 0) | ((((sizeof(struct btrfs_ioctl_vol_args
)))) << ((0 +8)+8)))
, &args) < 0)
937 return -errno(*__errno_location ());
938 }
939
940 /* Make sure the free disk space is correctly updated for both file systems */
941 (void) fsync(fd);
942 (void) fsync(backing_fd);
943
944 return 1;
945}
946
947int btrfs_resize_loopback(const char *p, uint64_t new_size, bool_Bool grow_only) {
948 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
949
950 fd = open(p, O_RDONLY00|O_NOCTTY0400|O_CLOEXEC02000000);
951 if (fd < 0)
952 return -errno(*__errno_location ());
953
954 return btrfs_resize_loopback_fd(fd, new_size, grow_only);
955}
956
957int btrfs_qgroupid_make(uint64_t level, uint64_t id, uint64_t *ret) {
958 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/basic/btrfs-util.c", 958
, __PRETTY_FUNCTION__); } while (0)
;
959
960 if (level >= (UINT64_C(1)1UL << (64 - BTRFS_QGROUP_LEVEL_SHIFT48)))
961 return -EINVAL22;
962
963 if (id >= (UINT64_C(1)1UL << BTRFS_QGROUP_LEVEL_SHIFT48))
964 return -EINVAL22;
965
966 *ret = (level << BTRFS_QGROUP_LEVEL_SHIFT48) | id;
967 return 0;
968}
969
970int btrfs_qgroupid_split(uint64_t qgroupid, uint64_t *level, uint64_t *id) {
971 assert(level || id)do { if ((__builtin_expect(!!(!(level || id)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("level || id"), "../src/basic/btrfs-util.c"
, 971, __PRETTY_FUNCTION__); } while (0)
;
972
973 if (level)
974 *level = qgroupid >> BTRFS_QGROUP_LEVEL_SHIFT48;
975
976 if (id)
977 *id = qgroupid & ((UINT64_C(1)1UL << BTRFS_QGROUP_LEVEL_SHIFT48) - 1);
978
979 return 0;
980}
981
982static int qgroup_create_or_destroy(int fd, bool_Bool b, uint64_t qgroupid) {
983
984 struct btrfs_ioctl_qgroup_create_args args = {
985 .create = b,
986 .qgroupid = qgroupid,
987 };
988 unsigned c;
989 int r;
990
991 r = btrfs_is_filesystem(fd);
992 if (r < 0)
993 return r;
994 if (r == 0)
995 return -ENOTTY25;
996
997 for (c = 0;; c++) {
998 if (ioctl(fd, BTRFS_IOC_QGROUP_CREATE(((1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((42)) << 0) | ((((sizeof(struct btrfs_ioctl_qgroup_create_args
)))) << ((0 +8)+8)))
, &args) < 0) {
999
1000 /* If quota is not enabled, we get EINVAL. Turn this into a recognizable error */
1001 if (errno(*__errno_location ()) == EINVAL22)
1002 return -ENOPROTOOPT92;
1003
1004 if (errno(*__errno_location ()) == EBUSY16 && c < 10) {
1005 (void) btrfs_quota_scan_wait(fd);
1006 continue;
1007 }
1008
1009 return -errno(*__errno_location ());
1010 }
1011
1012 break;
1013 }
1014
1015 return 0;
1016}
1017
1018int btrfs_qgroup_create(int fd, uint64_t qgroupid) {
1019 return qgroup_create_or_destroy(fd, true1, qgroupid);
1020}
1021
1022int btrfs_qgroup_destroy(int fd, uint64_t qgroupid) {
1023 return qgroup_create_or_destroy(fd, false0, qgroupid);
1024}
1025
1026int btrfs_qgroup_destroy_recursive(int fd, uint64_t qgroupid) {
1027 _cleanup_free___attribute__((cleanup(freep))) uint64_t *qgroups = NULL((void*)0);
27
'qgroups' initialized to a null pointer value
1028 uint64_t subvol_id;
1029 int i, n, r;
1030
1031 /* Destroys the specified qgroup, but unassigns it from all
1032 * its parents first. Also, it recursively destroys all
1033 * qgroups it is assgined to that have the same id part of the
1034 * qgroupid as the specified group. */
1035
1036 r = btrfs_qgroupid_split(qgroupid, NULL((void*)0), &subvol_id);
1037 if (r
27.1
'r' is >= 0
< 0)
28
Taking false branch
1038 return r;
1039
1040 n = btrfs_qgroup_find_parents(fd, qgroupid, &qgroups);
29
Calling 'btrfs_qgroup_find_parents'
44
Returning from 'btrfs_qgroup_find_parents'
1041 if (n < 0)
45
Assuming 'n' is >= 0
46
Taking false branch
1042 return n;
1043
1044 for (i = 0; i < n; i++) {
47
Assuming 'i' is < 'n'
48
Loop condition is true. Entering loop body
1045 uint64_t id;
1046
1047 r = btrfs_qgroupid_split(qgroups[i], NULL((void*)0), &id);
49
Array access (from variable 'qgroups') results in a null pointer dereference
1048 if (r < 0)
1049 return r;
1050
1051 r = btrfs_qgroup_unassign(fd, qgroupid, qgroups[i]);
1052 if (r < 0)
1053 return r;
1054
1055 if (id != subvol_id)
1056 continue;
1057
1058 /* The parent qgroupid shares the same id part with
1059 * us? If so, destroy it too. */
1060
1061 (void) btrfs_qgroup_destroy_recursive(fd, qgroups[i]);
1062 }
1063
1064 return btrfs_qgroup_destroy(fd, qgroupid);
1065}
1066
1067int btrfs_quota_scan_start(int fd) {
1068 struct btrfs_ioctl_quota_rescan_args args = {};
1069
1070 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 1070, __PRETTY_FUNCTION__); } while (0)
;
1071
1072 if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN(((1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((44)) << 0) | ((((sizeof(struct btrfs_ioctl_quota_rescan_args
)))) << ((0 +8)+8)))
, &args) < 0)
1073 return -errno(*__errno_location ());
1074
1075 return 0;
1076}
1077
1078int btrfs_quota_scan_wait(int fd) {
1079 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 1079, __PRETTY_FUNCTION__); } while (0)
;
1080
1081 if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN_WAIT(((0U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((46)) << 0) | ((0) << ((0 +8)+8)))
) < 0)
1082 return -errno(*__errno_location ());
1083
1084 return 0;
1085}
1086
1087int btrfs_quota_scan_ongoing(int fd) {
1088 struct btrfs_ioctl_quota_rescan_args args = {};
1089
1090 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 1090, __PRETTY_FUNCTION__); } while (0)
;
1091
1092 if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN_STATUS(((2U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((45)) << 0) | ((((sizeof(struct btrfs_ioctl_quota_rescan_args
)))) << ((0 +8)+8)))
, &args) < 0)
1093 return -errno(*__errno_location ());
1094
1095 return !!args.flags;
1096}
1097
1098static int qgroup_assign_or_unassign(int fd, bool_Bool b, uint64_t child, uint64_t parent) {
1099 struct btrfs_ioctl_qgroup_assign_args args = {
1100 .assign = b,
1101 .src = child,
1102 .dst = parent,
1103 };
1104 unsigned c;
1105 int r;
1106
1107 r = btrfs_is_filesystem(fd);
1108 if (r < 0)
1109 return r;
1110 if (r == 0)
1111 return -ENOTTY25;
1112
1113 for (c = 0;; c++) {
1114 r = ioctl(fd, BTRFS_IOC_QGROUP_ASSIGN(((1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((41)) << 0) | ((((sizeof(struct btrfs_ioctl_qgroup_assign_args
)))) << ((0 +8)+8)))
, &args);
1115 if (r < 0) {
1116 if (errno(*__errno_location ()) == EBUSY16 && c < 10) {
1117 (void) btrfs_quota_scan_wait(fd);
1118 continue;
1119 }
1120
1121 return -errno(*__errno_location ());
1122 }
1123
1124 if (r == 0)
1125 return 0;
1126
1127 /* If the return value is > 0, we need to request a rescan */
1128
1129 (void) btrfs_quota_scan_start(fd);
1130 return 1;
1131 }
1132}
1133
1134int btrfs_qgroup_assign(int fd, uint64_t child, uint64_t parent) {
1135 return qgroup_assign_or_unassign(fd, true1, child, parent);
1136}
1137
1138int btrfs_qgroup_unassign(int fd, uint64_t child, uint64_t parent) {
1139 return qgroup_assign_or_unassign(fd, false0, child, parent);
1140}
1141
1142static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, BtrfsRemoveFlags flags) {
1143 struct btrfs_ioctl_search_args args = {
1144 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID1,
1145
1146 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID256,
1147 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID-256ULL,
1148
1149 .key.min_type = BTRFS_ROOT_BACKREF_KEY144,
1150 .key.max_type = BTRFS_ROOT_BACKREF_KEY144,
1151
1152 .key.min_transid = 0,
1153 .key.max_transid = (uint64_t) -1,
1154 };
1155
1156 struct btrfs_ioctl_vol_args vol_args = {};
1157 _cleanup_close___attribute__((cleanup(closep))) int subvol_fd = -1;
1158 struct stat st;
1159 bool_Bool made_writable = false0;
1160 int r;
1161
1162 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 1162, __PRETTY_FUNCTION__); } while (0)
;
1163 assert(subvolume)do { if ((__builtin_expect(!!(!(subvolume)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("subvolume"), "../src/basic/btrfs-util.c"
, 1163, __PRETTY_FUNCTION__); } while (0)
;
1164
1165 if (fstat(fd, &st) < 0)
1166 return -errno(*__errno_location ());
1167
1168 if (!S_ISDIR(st.st_mode)((((st.st_mode)) & 0170000) == (0040000)))
1169 return -EINVAL22;
1170
1171 subvol_fd = openat(fd, subvolume, O_RDONLY00|O_NOCTTY0400|O_CLOEXEC02000000|O_DIRECTORY0200000|O_NOFOLLOW0400000);
1172 if (subvol_fd < 0)
1173 return -errno(*__errno_location ());
1174
1175 if (subvol_id == 0) {
1176 r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
1177 if (r < 0)
1178 return r;
1179 }
1180
1181 /* First, try to remove the subvolume. If it happens to be
1182 * already empty, this will just work. */
1183 strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
1184 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY(((1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((15)) << 0) | ((((sizeof(struct btrfs_ioctl_vol_args
)))) << ((0 +8)+8)))
, &vol_args) >= 0) {
1185 (void) btrfs_qgroup_destroy_recursive(fd, subvol_id); /* for the leaf subvolumes, the qgroup id is identical to the subvol id */
1186 return 0;
1187 }
1188 if (!(flags & BTRFS_REMOVE_RECURSIVE) || errno(*__errno_location ()) != ENOTEMPTY39)
1189 return -errno(*__errno_location ());
1190
1191 /* OK, the subvolume is not empty, let's look for child
1192 * subvolumes, and remove them, first */
1193
1194 args.key.min_offset = args.key.max_offset = subvol_id;
1195
1196 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
1197 const struct btrfs_ioctl_search_header *sh;
1198 unsigned i;
1199
1200 args.key.nr_items = 256;
1201 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH(((2U|1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +
8)) | (((17)) << 0) | ((((sizeof(struct btrfs_ioctl_search_args
)))) << ((0 +8)+8)))
, &args) < 0)
1202 return -errno(*__errno_location ());
1203
1204 if (args.key.nr_items <= 0)
1205 break;
1206
1207 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args)for ((i) = 0, (sh) = (const struct btrfs_ioctl_search_header*
) (args).buf; (i) < (args).key.nr_items; (i)++, (sh) = (const
struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof
(struct btrfs_ioctl_search_header) + (sh)->len))
{
1208 _cleanup_free___attribute__((cleanup(freep))) char *p = NULL((void*)0);
1209 const struct btrfs_root_ref *ref;
1210 struct btrfs_ioctl_ino_lookup_args ino_args;
1211
1212 btrfs_ioctl_search_args_set(&args, sh);
1213
1214 if (sh->type != BTRFS_ROOT_BACKREF_KEY144)
1215 continue;
1216 if (sh->offset != subvol_id)
1217 continue;
1218
1219 ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh)((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header
)))
;
1220
1221 p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
1222 if (!p)
1223 return -ENOMEM12;
1224
1225 zero(ino_args)(({ size_t _l_ = (sizeof(ino_args)); void *_x_ = (&(ino_args
)); _l_ == 0 ? _x_ : memset(_x_, 0, _l_); }))
;
1226 ino_args.treeid = subvol_id;
1227 ino_args.objectid = htole64(ref->dirid);
1228
1229 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP(((2U|1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +
8)) | (((18)) << 0) | ((((sizeof(struct btrfs_ioctl_ino_lookup_args
)))) << ((0 +8)+8)))
, &ino_args) < 0)
1230 return -errno(*__errno_location ());
1231
1232 if (!made_writable) {
1233 r = btrfs_subvol_set_read_only_fd(subvol_fd, false0);
1234 if (r < 0)
1235 return r;
1236
1237 made_writable = true1;
1238 }
1239
1240 if (isempty(ino_args.name))
1241 /* Subvolume is in the top-level
1242 * directory of the subvolume. */
1243 r = subvol_remove_children(subvol_fd, p, sh->objectid, flags);
1244 else {
1245 _cleanup_close___attribute__((cleanup(closep))) int child_fd = -1;
1246
1247 /* Subvolume is somewhere further down,
1248 * hence we need to open the
1249 * containing directory first */
1250
1251 child_fd = openat(subvol_fd, ino_args.name, O_RDONLY00|O_NOCTTY0400|O_CLOEXEC02000000|O_DIRECTORY0200000|O_NOFOLLOW0400000);
1252 if (child_fd < 0)
1253 return -errno(*__errno_location ());
1254
1255 r = subvol_remove_children(child_fd, p, sh->objectid, flags);
1256 }
1257 if (r < 0)
1258 return r;
1259 }
1260
1261 /* Increase search key by one, to read the next item, if we can. */
1262 if (!btrfs_ioctl_search_args_inc(&args))
1263 break;
1264 }
1265
1266 /* OK, the child subvolumes should all be gone now, let's try
1267 * again to remove the subvolume */
1268 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY(((1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((15)) << 0) | ((((sizeof(struct btrfs_ioctl_vol_args
)))) << ((0 +8)+8)))
, &vol_args) < 0)
1269 return -errno(*__errno_location ());
1270
1271 (void) btrfs_qgroup_destroy_recursive(fd, subvol_id);
1272 return 0;
1273}
1274
1275int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) {
1276 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
1277 const char *subvolume;
1278 int r;
1279
1280 assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("path"), "../src/basic/btrfs-util.c", 1280
, __PRETTY_FUNCTION__); } while (0)
;
1281
1282 r = extract_subvolume_name(path, &subvolume);
1283 if (r < 0)
1284 return r;
1285
1286 fd = open_parent(path, O_RDONLY00|O_NOCTTY0400|O_CLOEXEC02000000|O_DIRECTORY0200000);
1287 if (fd < 0)
1288 return fd;
1289
1290 return subvol_remove_children(fd, subvolume, 0, flags);
1291}
1292
1293int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags) {
1294 return subvol_remove_children(fd, subvolume, 0, flags);
1295}
1296
1297int btrfs_qgroup_copy_limits(int fd, uint64_t old_qgroupid, uint64_t new_qgroupid) {
1298
1299 struct btrfs_ioctl_search_args args = {
1300 /* Tree of quota items */
1301 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID8ULL,
1302
1303 /* The object ID is always 0 */
1304 .key.min_objectid = 0,
1305 .key.max_objectid = 0,
1306
1307 /* Look precisely for the quota items */
1308 .key.min_type = BTRFS_QGROUP_LIMIT_KEY244,
1309 .key.max_type = BTRFS_QGROUP_LIMIT_KEY244,
1310
1311 /* For our qgroup */
1312 .key.min_offset = old_qgroupid,
1313 .key.max_offset = old_qgroupid,
1314
1315 /* No restrictions on the other components */
1316 .key.min_transid = 0,
1317 .key.max_transid = (uint64_t) -1,
1318 };
1319
1320 int r;
1321
1322 r = btrfs_is_filesystem(fd);
1323 if (r < 0)
1324 return r;
1325 if (!r)
1326 return -ENOTTY25;
1327
1328 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
1329 const struct btrfs_ioctl_search_header *sh;
1330 unsigned i;
1331
1332 args.key.nr_items = 256;
1333 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH(((2U|1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +
8)) | (((17)) << 0) | ((((sizeof(struct btrfs_ioctl_search_args
)))) << ((0 +8)+8)))
, &args) < 0) {
1334 if (errno(*__errno_location ()) == ENOENT2) /* quota tree missing: quota is not enabled, hence nothing to copy */
1335 break;
1336
1337 return -errno(*__errno_location ());
1338 }
1339
1340 if (args.key.nr_items <= 0)
1341 break;
1342
1343 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args)for ((i) = 0, (sh) = (const struct btrfs_ioctl_search_header*
) (args).buf; (i) < (args).key.nr_items; (i)++, (sh) = (const
struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof
(struct btrfs_ioctl_search_header) + (sh)->len))
{
1344 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh)((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header
)))
;
1345 struct btrfs_ioctl_qgroup_limit_args qargs;
1346 unsigned c;
1347
1348 /* Make sure we start the next search at least from this entry */
1349 btrfs_ioctl_search_args_set(&args, sh);
1350
1351 if (sh->objectid != 0)
1352 continue;
1353 if (sh->type != BTRFS_QGROUP_LIMIT_KEY244)
1354 continue;
1355 if (sh->offset != old_qgroupid)
1356 continue;
1357
1358 /* We found the entry, now copy things over. */
1359
1360 qargs = (struct btrfs_ioctl_qgroup_limit_args) {
1361 .qgroupid = new_qgroupid,
1362
1363 .lim.max_rfer = le64toh(qli->max_rfer),
1364 .lim.max_excl = le64toh(qli->max_excl),
1365 .lim.rsv_rfer = le64toh(qli->rsv_rfer),
1366 .lim.rsv_excl = le64toh(qli->rsv_excl),
1367
1368 .lim.flags = le64toh(qli->flags) & (BTRFS_QGROUP_LIMIT_MAX_RFER(1ULL << 0)|
1369 BTRFS_QGROUP_LIMIT_MAX_EXCL(1ULL << 1)|
1370 BTRFS_QGROUP_LIMIT_RSV_RFER(1ULL << 2)|
1371 BTRFS_QGROUP_LIMIT_RSV_EXCL(1ULL << 3)),
1372 };
1373
1374 for (c = 0;; c++) {
1375 if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT(((2U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((43)) << 0) | ((((sizeof(struct btrfs_ioctl_qgroup_limit_args
)))) << ((0 +8)+8)))
, &qargs) < 0) {
1376 if (errno(*__errno_location ()) == EBUSY16 && c < 10) {
1377 (void) btrfs_quota_scan_wait(fd);
1378 continue;
1379 }
1380 return -errno(*__errno_location ());
1381 }
1382
1383 break;
1384 }
1385
1386 return 1;
1387 }
1388
1389 /* Increase search key by one, to read the next item, if we can. */
1390 if (!btrfs_ioctl_search_args_inc(&args))
1391 break;
1392 }
1393
1394 return 0;
1395}
1396
1397static int copy_quota_hierarchy(int fd, uint64_t old_subvol_id, uint64_t new_subvol_id) {
1398 _cleanup_free___attribute__((cleanup(freep))) uint64_t *old_qgroups = NULL((void*)0), *old_parent_qgroups = NULL((void*)0);
1399 bool_Bool copy_from_parent = false0, insert_intermediary_qgroup = false0;
1400 int n_old_qgroups, n_old_parent_qgroups, r, i;
1401 uint64_t old_parent_id;
1402
1403 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 1403, __PRETTY_FUNCTION__); } while (0)
;
1404
1405 /* Copies a reduced form of quota information from the old to
1406 * the new subvolume. */
1407
1408 n_old_qgroups = btrfs_qgroup_find_parents(fd, old_subvol_id, &old_qgroups);
1409 if (n_old_qgroups <= 0) /* Nothing to copy */
1410 return n_old_qgroups;
1411
1412 r = btrfs_subvol_get_parent(fd, old_subvol_id, &old_parent_id);
1413 if (r == -ENXIO6)
1414 /* We have no parent, hence nothing to copy. */
1415 n_old_parent_qgroups = 0;
1416 else if (r < 0)
1417 return r;
1418 else {
1419 n_old_parent_qgroups = btrfs_qgroup_find_parents(fd, old_parent_id, &old_parent_qgroups);
1420 if (n_old_parent_qgroups < 0)
1421 return n_old_parent_qgroups;
1422 }
1423
1424 for (i = 0; i < n_old_qgroups; i++) {
1425 uint64_t id;
1426 int j;
1427
1428 r = btrfs_qgroupid_split(old_qgroups[i], NULL((void*)0), &id);
1429 if (r < 0)
1430 return r;
1431
1432 if (id == old_subvol_id) {
1433 /* The old subvolume was member of a qgroup
1434 * that had the same id, but a different level
1435 * as it self. Let's set up something similar
1436 * in the destination. */
1437 insert_intermediary_qgroup = true1;
1438 break;
1439 }
1440
1441 for (j = 0; j < n_old_parent_qgroups; j++)
1442 if (old_parent_qgroups[j] == old_qgroups[i]) {
1443 /* The old subvolume shared a common
1444 * parent qgroup with its parent
1445 * subvolume. Let's set up something
1446 * similar in the destination. */
1447 copy_from_parent = true1;
1448 }
1449 }
1450
1451 if (!insert_intermediary_qgroup && !copy_from_parent)
1452 return 0;
1453
1454 return btrfs_subvol_auto_qgroup_fd(fd, new_subvol_id, insert_intermediary_qgroup);
1455}
1456
1457static int copy_subtree_quota_limits(int fd, uint64_t old_subvol, uint64_t new_subvol) {
1458 uint64_t old_subtree_qgroup, new_subtree_qgroup;
1459 bool_Bool changed;
1460 int r;
1461
1462 /* First copy the leaf limits */
1463 r = btrfs_qgroup_copy_limits(fd, old_subvol, new_subvol);
1464 if (r < 0)
1465 return r;
1466 changed = r > 0;
1467
1468 /* Then, try to copy the subtree limits, if there are any. */
1469 r = btrfs_subvol_find_subtree_qgroup(fd, old_subvol, &old_subtree_qgroup);
1470 if (r < 0)
1471 return r;
1472 if (r == 0)
1473 return changed;
1474
1475 r = btrfs_subvol_find_subtree_qgroup(fd, new_subvol, &new_subtree_qgroup);
1476 if (r < 0)
1477 return r;
1478 if (r == 0)
1479 return changed;
1480
1481 r = btrfs_qgroup_copy_limits(fd, old_subtree_qgroup, new_subtree_qgroup);
1482 if (r != 0)
1483 return r;
1484
1485 return changed;
1486}
1487
1488static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t old_subvol_id, BtrfsSnapshotFlags flags) {
1489
1490 struct btrfs_ioctl_search_args args = {
1491 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID1,
1492
1493 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID256,
1494 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID-256ULL,
1495
1496 .key.min_type = BTRFS_ROOT_BACKREF_KEY144,
1497 .key.max_type = BTRFS_ROOT_BACKREF_KEY144,
1498
1499 .key.min_transid = 0,
1500 .key.max_transid = (uint64_t) -1,
1501 };
1502
1503 struct btrfs_ioctl_vol_args_v2 vol_args = {
1504 .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY(1ULL << 1) : 0,
1505 .fd = old_fd,
1506 };
1507 _cleanup_close___attribute__((cleanup(closep))) int subvolume_fd = -1;
1508 uint64_t new_subvol_id;
1509 int r;
1510
1511 assert(old_fd >= 0)do { if ((__builtin_expect(!!(!(old_fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("old_fd >= 0"), "../src/basic/btrfs-util.c"
, 1511, __PRETTY_FUNCTION__); } while (0)
;
1512 assert(new_fd >= 0)do { if ((__builtin_expect(!!(!(new_fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("new_fd >= 0"), "../src/basic/btrfs-util.c"
, 1512, __PRETTY_FUNCTION__); } while (0)
;
1513 assert(subvolume)do { if ((__builtin_expect(!!(!(subvolume)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("subvolume"), "../src/basic/btrfs-util.c"
, 1513, __PRETTY_FUNCTION__); } while (0)
;
1514
1515 strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
1516
1517 if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2(((1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +8))
| (((23)) << 0) | ((((sizeof(struct btrfs_ioctl_vol_args_v2
)))) << ((0 +8)+8)))
, &vol_args) < 0)
1518 return -errno(*__errno_location ());
1519
1520 if (!(flags & BTRFS_SNAPSHOT_RECURSIVE) &&
1521 !(flags & BTRFS_SNAPSHOT_QUOTA))
1522 return 0;
1523
1524 if (old_subvol_id == 0) {
1525 r = btrfs_subvol_get_id_fd(old_fd, &old_subvol_id);
1526 if (r < 0)
1527 return r;
1528 }
1529
1530 r = btrfs_subvol_get_id(new_fd, vol_args.name, &new_subvol_id);
1531 if (r < 0)
1532 return r;
1533
1534 if (flags & BTRFS_SNAPSHOT_QUOTA)
1535 (void) copy_quota_hierarchy(new_fd, old_subvol_id, new_subvol_id);
1536
1537 if (!(flags & BTRFS_SNAPSHOT_RECURSIVE)) {
1538
1539 if (flags & BTRFS_SNAPSHOT_QUOTA)
1540 (void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id);
1541
1542 return 0;
1543 }
1544
1545 args.key.min_offset = args.key.max_offset = old_subvol_id;
1546
1547 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
1548 const struct btrfs_ioctl_search_header *sh;
1549 unsigned i;
1550
1551 args.key.nr_items = 256;
1552 if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH(((2U|1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +
8)) | (((17)) << 0) | ((((sizeof(struct btrfs_ioctl_search_args
)))) << ((0 +8)+8)))
, &args) < 0)
1553 return -errno(*__errno_location ());
1554
1555 if (args.key.nr_items <= 0)
1556 break;
1557
1558 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args)for ((i) = 0, (sh) = (const struct btrfs_ioctl_search_header*
) (args).buf; (i) < (args).key.nr_items; (i)++, (sh) = (const
struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof
(struct btrfs_ioctl_search_header) + (sh)->len))
{
1559 _cleanup_free___attribute__((cleanup(freep))) char *p = NULL((void*)0), *c = NULL((void*)0), *np = NULL((void*)0);
1560 struct btrfs_ioctl_ino_lookup_args ino_args;
1561 const struct btrfs_root_ref *ref;
1562 _cleanup_close___attribute__((cleanup(closep))) int old_child_fd = -1, new_child_fd = -1;
1563
1564 btrfs_ioctl_search_args_set(&args, sh);
1565
1566 if (sh->type != BTRFS_ROOT_BACKREF_KEY144)
1567 continue;
1568
1569 /* Avoid finding the source subvolume a second
1570 * time */
1571 if (sh->offset != old_subvol_id)
1572 continue;
1573
1574 /* Avoid running into loops if the new
1575 * subvolume is below the old one. */
1576 if (sh->objectid == new_subvol_id)
1577 continue;
1578
1579 ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh)((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header
)))
;
1580 p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
1581 if (!p)
1582 return -ENOMEM12;
1583
1584 zero(ino_args)(({ size_t _l_ = (sizeof(ino_args)); void *_x_ = (&(ino_args
)); _l_ == 0 ? _x_ : memset(_x_, 0, _l_); }))
;
1585 ino_args.treeid = old_subvol_id;
1586 ino_args.objectid = htole64(ref->dirid);
1587
1588 if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP(((2U|1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +
8)) | (((18)) << 0) | ((((sizeof(struct btrfs_ioctl_ino_lookup_args
)))) << ((0 +8)+8)))
, &ino_args) < 0)
1589 return -errno(*__errno_location ());
1590
1591 /* The kernel returns an empty name if the
1592 * subvolume is in the top-level directory,
1593 * and otherwise appends a slash, so that we
1594 * can just concatenate easily here, without
1595 * adding a slash. */
1596 c = strappend(ino_args.name, p);
1597 if (!c)
1598 return -ENOMEM12;
1599
1600 old_child_fd = openat(old_fd, c, O_RDONLY00|O_NOCTTY0400|O_CLOEXEC02000000|O_DIRECTORY0200000|O_NOFOLLOW0400000);
1601 if (old_child_fd < 0)
1602 return -errno(*__errno_location ());
1603
1604 np = strjoin(subvolume, "/", ino_args.name)strjoin_real((subvolume), "/", ino_args.name, ((void*)0));
1605 if (!np)
1606 return -ENOMEM12;
1607
1608 new_child_fd = openat(new_fd, np, O_RDONLY00|O_NOCTTY0400|O_CLOEXEC02000000|O_DIRECTORY0200000|O_NOFOLLOW0400000);
1609 if (new_child_fd < 0)
1610 return -errno(*__errno_location ());
1611
1612 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1613 /* If the snapshot is read-only we
1614 * need to mark it writable
1615 * temporarily, to put the subsnapshot
1616 * into place. */
1617
1618 if (subvolume_fd < 0) {
1619 subvolume_fd = openat(new_fd, subvolume, O_RDONLY00|O_NOCTTY0400|O_CLOEXEC02000000|O_DIRECTORY0200000|O_NOFOLLOW0400000);
1620 if (subvolume_fd < 0)
1621 return -errno(*__errno_location ());
1622 }
1623
1624 r = btrfs_subvol_set_read_only_fd(subvolume_fd, false0);
1625 if (r < 0)
1626 return r;
1627 }
1628
1629 /* When btrfs clones the subvolumes, child
1630 * subvolumes appear as empty directories. Remove
1631 * them, so that we can create a new snapshot
1632 * in their place */
1633 if (unlinkat(new_child_fd, p, AT_REMOVEDIR0x200) < 0) {
1634 int k = -errno(*__errno_location ());
1635
1636 if (flags & BTRFS_SNAPSHOT_READ_ONLY)
1637 (void) btrfs_subvol_set_read_only_fd(subvolume_fd, true1);
1638
1639 return k;
1640 }
1641
1642 r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
1643
1644 /* Restore the readonly flag */
1645 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1646 int k;
1647
1648 k = btrfs_subvol_set_read_only_fd(subvolume_fd, true1);
1649 if (r >= 0 && k < 0)
1650 return k;
1651 }
1652
1653 if (r < 0)
1654 return r;
1655 }
1656
1657 /* Increase search key by one, to read the next item, if we can. */
1658 if (!btrfs_ioctl_search_args_inc(&args))
1659 break;
1660 }
1661
1662 if (flags & BTRFS_SNAPSHOT_QUOTA)
1663 (void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id);
1664
1665 return 0;
1666}
1667
1668int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
1669 _cleanup_close___attribute__((cleanup(closep))) int new_fd = -1;
1670 const char *subvolume;
1671 int r;
1672
1673 assert(old_fd >= 0)do { if ((__builtin_expect(!!(!(old_fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("old_fd >= 0"), "../src/basic/btrfs-util.c"
, 1673, __PRETTY_FUNCTION__); } while (0)
;
1674 assert(new_path)do { if ((__builtin_expect(!!(!(new_path)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("new_path"), "../src/basic/btrfs-util.c"
, 1674, __PRETTY_FUNCTION__); } while (0)
;
1675
1676 r = btrfs_is_subvol_fd(old_fd);
1677 if (r < 0)
1678 return r;
1679 if (r == 0) {
1680 bool_Bool plain_directory = false0;
1681
1682 /* If the source isn't a proper subvolume, fail unless fallback is requested */
1683 if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
1684 return -EISDIR21;
1685
1686 r = btrfs_subvol_make(new_path);
1687 if (r == -ENOTTY25 && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) {
1688 /* If the destination doesn't support subvolumes, then use a plain directory, if that's requested. */
1689 if (mkdir(new_path, 0755) < 0)
1690 return -errno(*__errno_location ());
1691
1692 plain_directory = true1;
1693 } else if (r < 0)
1694 return r;
1695
1696 r = copy_directory_fd(old_fd, new_path, COPY_MERGE|COPY_REFLINK);
1697 if (r < 0)
1698 goto fallback_fail;
1699
1700 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1701
1702 if (plain_directory) {
1703 /* Plain directories have no recursive read-only flag, but something pretty close to
1704 * it: the IMMUTABLE bit. Let's use this here, if this is requested. */
1705
1706 if (flags & BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE)
1707 (void) chattr_path(new_path, FS_IMMUTABLE_FL0x00000010, FS_IMMUTABLE_FL0x00000010);
1708 } else {
1709 r = btrfs_subvol_set_read_only(new_path, true1);
1710 if (r < 0)
1711 goto fallback_fail;
1712 }
1713 }
1714
1715 return 0;
1716
1717 fallback_fail:
1718 (void) rm_rf(new_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
1719 return r;
1720 }
1721
1722 r = extract_subvolume_name(new_path, &subvolume);
1723 if (r < 0)
1724 return r;
1725
1726 new_fd = open_parent(new_path, O_RDONLY00|O_NOCTTY0400|O_CLOEXEC02000000|O_DIRECTORY0200000);
1727 if (new_fd < 0)
1728 return new_fd;
1729
1730 return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
1731}
1732
1733int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
1734 _cleanup_close___attribute__((cleanup(closep))) int old_fd = -1;
1735
1736 assert(old_path)do { if ((__builtin_expect(!!(!(old_path)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("old_path"), "../src/basic/btrfs-util.c"
, 1736, __PRETTY_FUNCTION__); } while (0)
;
1737 assert(new_path)do { if ((__builtin_expect(!!(!(new_path)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("new_path"), "../src/basic/btrfs-util.c"
, 1737, __PRETTY_FUNCTION__); } while (0)
;
1738
1739 old_fd = open(old_path, O_RDONLY00|O_NOCTTY0400|O_CLOEXEC02000000|O_DIRECTORY0200000);
1740 if (old_fd < 0)
1741 return -errno(*__errno_location ());
1742
1743 return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);
1744}
1745
1746int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret) {
1747
1748 struct btrfs_ioctl_search_args args = {
1749 /* Tree of quota items */
1750 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID8ULL,
1751
1752 /* Look precisely for the quota relation items */
1753 .key.min_type = BTRFS_QGROUP_RELATION_KEY246,
1754 .key.max_type = BTRFS_QGROUP_RELATION_KEY246,
1755
1756 /* No restrictions on the other components */
1757 .key.min_offset = 0,
1758 .key.max_offset = (uint64_t) -1,
1759
1760 .key.min_transid = 0,
1761 .key.max_transid = (uint64_t) -1,
1762 };
1763
1764 _cleanup_free___attribute__((cleanup(freep))) uint64_t *items = NULL((void*)0);
1765 size_t n_items = 0, n_allocated = 0;
1766 int r;
1767
1768 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 1768, __PRETTY_FUNCTION__); } while (0)
;
30
Taking false branch
31
Loop condition is false. Exiting loop
1769 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/basic/btrfs-util.c", 1769
, __PRETTY_FUNCTION__); } while (0)
;
32
Taking false branch
33
Loop condition is false. Exiting loop
1770
1771 if (qgroupid
33.1
'qgroupid' is not equal to 0
== 0) {
34
Taking false branch
1772 r = btrfs_subvol_get_id_fd(fd, &qgroupid);
1773 if (r < 0)
1774 return r;
1775 } else {
1776 r = btrfs_is_filesystem(fd);
1777 if (r
34.1
'r' is >= 0
< 0)
35
Taking false branch
1778 return r;
1779 if (!r
35.1
'r' is 1
)
36
Taking false branch
1780 return -ENOTTY25;
1781 }
1782
1783 args.key.min_objectid = args.key.max_objectid = qgroupid;
1784
1785 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
37
Assuming the condition is true
38
Loop condition is true. Entering loop body
1786 const struct btrfs_ioctl_search_header *sh;
1787 unsigned i;
1788
1789 args.key.nr_items = 256;
1790 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH(((2U|1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +
8)) | (((17)) << 0) | ((((sizeof(struct btrfs_ioctl_search_args
)))) << ((0 +8)+8)))
, &args) < 0
) {
39
Assuming the condition is true
40
Taking true branch
1791 if (errno(*__errno_location ()) == ENOENT2) /* quota tree missing: quota is disabled */
41
Assuming the condition is false
42
Taking false branch
1792 break;
1793
1794 return -errno(*__errno_location ());
43
Returning without writing to '*ret'
1795 }
1796
1797 if (args.key.nr_items <= 0)
1798 break;
1799
1800 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args)for ((i) = 0, (sh) = (const struct btrfs_ioctl_search_header*
) (args).buf; (i) < (args).key.nr_items; (i)++, (sh) = (const
struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof
(struct btrfs_ioctl_search_header) + (sh)->len))
{
1801
1802 /* Make sure we start the next search at least from this entry */
1803 btrfs_ioctl_search_args_set(&args, sh);
1804
1805 if (sh->type != BTRFS_QGROUP_RELATION_KEY246)
1806 continue;
1807 if (sh->offset < sh->objectid)
1808 continue;
1809 if (sh->objectid != qgroupid)
1810 continue;
1811
1812 if (!GREEDY_REALLOC(items, n_allocated, n_items+1)greedy_realloc((void**) &(items), &(n_allocated), (n_items
+1), sizeof((items)[0]))
)
1813 return -ENOMEM12;
1814
1815 items[n_items++] = sh->offset;
1816 }
1817
1818 /* Increase search key by one, to read the next item, if we can. */
1819 if (!btrfs_ioctl_search_args_inc(&args))
1820 break;
1821 }
1822
1823 if (n_items <= 0) {
1824 *ret = NULL((void*)0);
1825 return 0;
1826 }
1827
1828 *ret = TAKE_PTR(items)({ typeof(items) _ptr_ = (items); (items) = ((void*)0); _ptr_
; })
;
1829
1830 return (int) n_items;
1831}
1832
1833int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool_Bool insert_intermediary_qgroup) {
1834 _cleanup_free___attribute__((cleanup(freep))) uint64_t *qgroups = NULL((void*)0);
1835 uint64_t parent_subvol;
1836 bool_Bool changed = false0;
1837 int n = 0, r;
1838
1839 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 1839, __PRETTY_FUNCTION__); } while (0)
;
4
Taking false branch
5
Loop condition is false. Exiting loop
1840
1841 /*
1842 * Sets up the specified subvolume's qgroup automatically in
1843 * one of two ways:
1844 *
1845 * If insert_intermediary_qgroup is false, the subvolume's
1846 * leaf qgroup will be assigned to the same parent qgroups as
1847 * the subvolume's parent subvolume.
1848 *
1849 * If insert_intermediary_qgroup is true a new intermediary
1850 * higher-level qgroup is created, with a higher level number,
1851 * but reusing the id of the subvolume. The level number is
1852 * picked as one smaller than the lowest level qgroup the
1853 * parent subvolume is a member of. If the parent subvolume's
1854 * leaf qgroup is assigned to no higher-level qgroup a new
1855 * qgroup of level 255 is created instead. Either way, the new
1856 * qgroup is then assigned to the parent's higher-level
1857 * qgroup, and the subvolume itself is assigned to it.
1858 *
1859 * If the subvolume is already assigned to a higher level
1860 * qgroup, no operation is executed.
1861 *
1862 * Effectively this means: regardless if
1863 * insert_intermediary_qgroup is true or not, after this
1864 * function is invoked the subvolume will be accounted within
1865 * the same qgroups as the parent. However, if it is true, it
1866 * will also get its own higher-level qgroup, which may in
1867 * turn be used by subvolumes created beneath this subvolume
1868 * later on.
1869 *
1870 * This hence defines a simple default qgroup setup for
1871 * subvolumes, as long as this function is invoked on each
1872 * created subvolume: each subvolume is always accounting
1873 * together with its immediate parents. Optionally, if
1874 * insert_intermediary_qgroup is true, it will also get a
1875 * qgroup that then includes all its own child subvolumes.
1876 */
1877
1878 if (subvol_id == 0) {
6
Assuming 'subvol_id' is not equal to 0
7
Taking false branch
1879 r = btrfs_is_subvol_fd(fd);
1880 if (r < 0)
1881 return r;
1882 if (!r)
1883 return -ENOTTY25;
1884
1885 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
1886 if (r < 0)
1887 return r;
1888 }
1889
1890 n = btrfs_qgroup_find_parents(fd, subvol_id, &qgroups);
1891 if (n < 0)
8
Assuming 'n' is >= 0
9
Taking false branch
1892 return n;
1893 if (n > 0) /* already parent qgroups set up, let's bail */
10
Assuming 'n' is <= 0
11
Taking false branch
1894 return 0;
1895
1896 qgroups = mfree(qgroups);
1897
1898 r = btrfs_subvol_get_parent(fd, subvol_id, &parent_subvol);
1899 if (r == -ENXIO6)
12
Assuming the condition is true
13
Taking true branch
1900 /* No parent, hence no qgroup memberships */
1901 n = 0;
1902 else if (r < 0)
1903 return r;
1904 else {
1905 n = btrfs_qgroup_find_parents(fd, parent_subvol, &qgroups);
1906 if (n < 0)
1907 return n;
1908 }
1909
1910 if (insert_intermediary_qgroup) {
14
Assuming 'insert_intermediary_qgroup' is true
15
Taking true branch
1911 uint64_t lowest = 256, new_qgroupid;
1912 bool_Bool created = false0;
1913 int i;
1914
1915 /* Determine the lowest qgroup that the parent
1916 * subvolume is assigned to. */
1917
1918 for (i = 0; i < n; i++) {
16
Loop condition is false. Execution continues on line 1929
1919 uint64_t level;
1920
1921 r = btrfs_qgroupid_split(qgroups[i], &level, NULL((void*)0));
1922 if (r < 0)
1923 return r;
1924
1925 if (level < lowest)
1926 lowest = level;
1927 }
1928
1929 if (lowest
16.1
'lowest' is > 1
<= 1) /* There are no levels left we could use insert an intermediary qgroup at */
17
Taking false branch
1930 return -EBUSY16;
1931
1932 r = btrfs_qgroupid_make(lowest - 1, subvol_id, &new_qgroupid);
1933 if (r
17.1
'r' is >= 0
< 0)
18
Taking false branch
1934 return r;
1935
1936 /* Create the new intermediary group, unless it already exists */
1937 r = btrfs_qgroup_create(fd, new_qgroupid);
1938 if (r < 0 && r != -EEXIST17)
19
Assuming 'r' is >= 0
1939 return r;
1940 if (r
19.1
'r' is >= 0
>= 0)
20
Taking true branch
1941 changed = created = true1;
1942
1943 for (i = 0; i < n; i++) {
21
Loop condition is false. Execution continues on line 1955
1944 r = btrfs_qgroup_assign(fd, new_qgroupid, qgroups[i]);
1945 if (r < 0 && r != -EEXIST17) {
1946 if (created)
1947 (void) btrfs_qgroup_destroy_recursive(fd, new_qgroupid);
1948
1949 return r;
1950 }
1951 if (r >= 0)
1952 changed = true1;
1953 }
1954
1955 r = btrfs_qgroup_assign(fd, subvol_id, new_qgroupid);
1956 if (r < 0 && r != -EEXIST17) {
22
Assuming 'r' is < 0
23
Assuming the condition is true
24
Taking true branch
1957 if (created
24.1
'created' is true
)
25
Taking true branch
1958 (void) btrfs_qgroup_destroy_recursive(fd, new_qgroupid);
26
Calling 'btrfs_qgroup_destroy_recursive'
1959 return r;
1960 }
1961 if (r >= 0)
1962 changed = true1;
1963
1964 } else {
1965 int i;
1966
1967 /* Assign our subvolume to all the same qgroups as the parent */
1968
1969 for (i = 0; i < n; i++) {
1970 r = btrfs_qgroup_assign(fd, subvol_id, qgroups[i]);
1971 if (r < 0 && r != -EEXIST17)
1972 return r;
1973 if (r >= 0)
1974 changed = true1;
1975 }
1976 }
1977
1978 return changed;
1979}
1980
1981int btrfs_subvol_auto_qgroup(const char *path, uint64_t subvol_id, bool_Bool create_intermediary_qgroup) {
1982 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
1983
1984 fd = open(path, O_RDONLY00|O_NOCTTY0400|O_CLOEXEC02000000|O_DIRECTORY0200000);
1985 if (fd < 0)
1
Assuming 'fd' is >= 0
2
Taking false branch
1986 return -errno(*__errno_location ());
1987
1988 return btrfs_subvol_auto_qgroup_fd(fd, subvol_id, create_intermediary_qgroup);
3
Calling 'btrfs_subvol_auto_qgroup_fd'
1989}
1990
1991int btrfs_subvol_get_parent(int fd, uint64_t subvol_id, uint64_t *ret) {
1992
1993 struct btrfs_ioctl_search_args args = {
1994 /* Tree of tree roots */
1995 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID1,
1996
1997 /* Look precisely for the subvolume items */
1998 .key.min_type = BTRFS_ROOT_BACKREF_KEY144,
1999 .key.max_type = BTRFS_ROOT_BACKREF_KEY144,
2000
2001 /* No restrictions on the other components */
2002 .key.min_offset = 0,
2003 .key.max_offset = (uint64_t) -1,
2004
2005 .key.min_transid = 0,
2006 .key.max_transid = (uint64_t) -1,
2007 };
2008 int r;
2009
2010 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/basic/btrfs-util.c"
, 2010, __PRETTY_FUNCTION__); } while (0)
;
2011 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/basic/btrfs-util.c", 2011
, __PRETTY_FUNCTION__); } while (0)
;
2012
2013 if (subvol_id == 0) {
2014 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
2015 if (r < 0)
2016 return r;
2017 } else {
2018 r = btrfs_is_filesystem(fd);
2019 if (r < 0)
2020 return r;
2021 if (!r)
2022 return -ENOTTY25;
2023 }
2024
2025 args.key.min_objectid = args.key.max_objectid = subvol_id;
2026
2027 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
2028 const struct btrfs_ioctl_search_header *sh;
2029 unsigned i;
2030
2031 args.key.nr_items = 256;
2032 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH(((2U|1U) << (((0 +8)+8)+14)) | (((0x94)) << (0 +
8)) | (((17)) << 0) | ((((sizeof(struct btrfs_ioctl_search_args
)))) << ((0 +8)+8)))
, &args) < 0)
2033 return negative_errno();
2034
2035 if (args.key.nr_items <= 0)
2036 break;
2037
2038 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args)for ((i) = 0, (sh) = (const struct btrfs_ioctl_search_header*
) (args).buf; (i) < (args).key.nr_items; (i)++, (sh) = (const
struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof
(struct btrfs_ioctl_search_header) + (sh)->len))
{
2039
2040 if (sh->type != BTRFS_ROOT_BACKREF_KEY144)
2041 continue;
2042 if (sh->objectid != subvol_id)
2043 continue;
2044
2045 *ret = sh->offset;
2046 return 0;
2047 }
2048 }
2049
2050 return -ENXIO6;
2051}