]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/btrfs-util.c
machined/machinectl: when "machinectl image-status" is used without arguments show...
[thirdparty/systemd.git] / src / shared / btrfs-util.c
CommitLineData
d7c7c334
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd 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 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <stdlib.h>
23#include <sys/vfs.h>
24#include <sys/stat.h>
25
26#ifdef HAVE_LINUX_BTRFS_H
27#include <linux/btrfs.h>
28#endif
29
30#include "missing.h"
31#include "util.h"
32#include "path-util.h"
33#include "macro.h"
d7c7c334 34#include "copy.h"
d7b8eec7
LP
35#include "selinux-util.h"
36#include "smack-util.h"
10f9c755 37#include "btrfs-ctree.h"
d7c7c334
LP
38#include "btrfs-util.h"
39
40static int validate_subvolume_name(const char *name) {
41
42 if (!filename_is_valid(name))
43 return -EINVAL;
44
45 if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
46 return -E2BIG;
47
48 return 0;
49}
50
51static int open_parent(const char *path, int flags) {
52 _cleanup_free_ char *parent = NULL;
53 int r, fd;
54
55 assert(path);
56
57 r = path_get_parent(path, &parent);
58 if (r < 0)
59 return r;
60
61 fd = open(parent, flags);
62 if (fd < 0)
63 return -errno;
64
65 return fd;
66}
67
68static int extract_subvolume_name(const char *path, const char **subvolume) {
69 const char *fn;
70 int r;
71
72 assert(path);
73 assert(subvolume);
74
75 fn = basename(path);
76
77 r = validate_subvolume_name(fn);
78 if (r < 0)
79 return r;
80
81 *subvolume = fn;
82 return 0;
83}
84
85int btrfs_is_snapshot(int fd) {
86 struct stat st;
87 struct statfs sfs;
88
cd61c3bf
LP
89 /* On btrfs subvolumes always have the inode 256 */
90
91 if (fstat(fd, &st) < 0)
d7c7c334
LP
92 return -errno;
93
cd61c3bf 94 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
d7c7c334
LP
95 return 0;
96
cd61c3bf 97 if (fstatfs(fd, &sfs) < 0)
d7c7c334
LP
98 return -errno;
99
cd61c3bf 100 return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
d7c7c334
LP
101}
102
103int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_only, bool fallback_copy) {
104 struct btrfs_ioctl_vol_args_v2 args = {
105 .flags = read_only ? BTRFS_SUBVOL_RDONLY : 0,
106 };
107 _cleanup_close_ int old_fd = -1, new_fd = -1;
108 const char *subvolume;
109 int r;
110
111 assert(old_path);
112
113 old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
114 if (old_fd < 0)
115 return -errno;
116
117 r = btrfs_is_snapshot(old_fd);
118 if (r < 0)
119 return r;
120 if (r == 0) {
121
122 if (fallback_copy) {
123 r = btrfs_subvol_make(new_path);
124 if (r < 0)
125 return r;
126
f2cbe59e 127 r = copy_directory_fd(old_fd, new_path, true);
d7c7c334
LP
128 if (r < 0) {
129 btrfs_subvol_remove(new_path);
130 return r;
131 }
132
133 if (read_only) {
10f9c755 134 r = btrfs_subvol_set_read_only(new_path, true);
d7c7c334
LP
135 if (r < 0) {
136 btrfs_subvol_remove(new_path);
137 return r;
138 }
139 }
140
141 return 0;
142 }
143
144 return -EISDIR;
145 }
146
147 r = extract_subvolume_name(new_path, &subvolume);
148 if (r < 0)
149 return r;
150
151 new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
152 if (new_fd < 0)
153 return new_fd;
154
155 strncpy(args.name, subvolume, sizeof(args.name)-1);
156 args.fd = old_fd;
157
158 if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &args) < 0)
159 return -errno;
160
161 return 0;
162}
163
164int btrfs_subvol_make(const char *path) {
165 struct btrfs_ioctl_vol_args args = {};
166 _cleanup_close_ int fd = -1;
167 const char *subvolume;
168 int r;
169
170 assert(path);
171
172 r = extract_subvolume_name(path, &subvolume);
173 if (r < 0)
174 return r;
175
176 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
177 if (fd < 0)
178 return fd;
179
180 strncpy(args.name, subvolume, sizeof(args.name)-1);
181
182 if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
183 return -errno;
184
185 return 0;
186}
187
d7b8eec7
LP
188int btrfs_subvol_make_label(const char *path) {
189 int r;
190
191 assert(path);
192
193 r = mac_selinux_create_file_prepare(path, S_IFDIR);
194 if (r < 0)
195 return r;
196
197 r = btrfs_subvol_make(path);
198 mac_selinux_create_file_clear();
199
200 if (r < 0)
201 return r;
202
203 return mac_smack_fix(path, false, false);
204}
205
d7c7c334
LP
206int btrfs_subvol_remove(const char *path) {
207 struct btrfs_ioctl_vol_args args = {};
208 _cleanup_close_ int fd = -1;
209 const char *subvolume;
210 int r;
211
212 assert(path);
213
214 r = extract_subvolume_name(path, &subvolume);
215 if (r < 0)
216 return r;
217
218 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
219 if (fd < 0)
220 return fd;
221
222 strncpy(args.name, subvolume, sizeof(args.name)-1);
223
224 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args) < 0)
225 return -errno;
226
227 return 0;
228}
229
0d6e763b 230int btrfs_subvol_set_read_only_fd(int fd, bool b) {
d7c7c334 231 uint64_t flags, nflags;
0d6e763b 232 struct stat st;
d7c7c334 233
0d6e763b
LP
234 assert(fd >= 0);
235
236 if (fstat(fd, &st) < 0)
d7c7c334
LP
237 return -errno;
238
0d6e763b
LP
239 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
240 return -EINVAL;
241
d7c7c334
LP
242 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
243 return -errno;
244
245 if (b)
246 nflags = flags | BTRFS_SUBVOL_RDONLY;
247 else
248 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
249
250 if (flags == nflags)
251 return 0;
252
253 if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
254 return -errno;
255
256 return 0;
257}
258
0d6e763b
LP
259int btrfs_subvol_set_read_only(const char *path, bool b) {
260 _cleanup_close_ int fd = -1;
261
262 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
263 if (fd < 0)
264 return -errno;
265
266 return btrfs_subvol_set_read_only_fd(fd, b);
267}
268
10f9c755 269int btrfs_subvol_get_read_only_fd(int fd) {
cd61c3bf
LP
270 uint64_t flags;
271
272 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
273 return -errno;
274
275 return !!(flags & BTRFS_SUBVOL_RDONLY);
276}
277
d7c7c334
LP
278int btrfs_reflink(int infd, int outfd) {
279 int r;
280
281 assert(infd >= 0);
282 assert(outfd >= 0);
283
284 r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
285 if (r < 0)
286 return -errno;
287
288 return 0;
289}
290
1c7dd825
LP
291int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
292 struct btrfs_ioctl_clone_range_args args = {
293 .src_fd = infd,
294 .src_offset = in_offset,
295 .src_length = sz,
296 .dest_offset = out_offset,
297 };
298 int r;
299
300 assert(infd >= 0);
301 assert(outfd >= 0);
302 assert(sz > 0);
303
304 r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
305 if (r < 0)
306 return -errno;
307
308 return 0;
309}
310
d7c7c334
LP
311int btrfs_get_block_device(const char *path, dev_t *dev) {
312 struct btrfs_ioctl_fs_info_args fsi = {};
313 _cleanup_close_ int fd = -1;
314 uint64_t id;
315
316 assert(path);
317 assert(dev);
318
319 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
320 if (fd < 0)
321 return -errno;
322
323 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
324 return -errno;
325
326 /* We won't do this for btrfs RAID */
327 if (fsi.num_devices != 1)
328 return 0;
329
330 for (id = 1; id <= fsi.max_id; id++) {
331 struct btrfs_ioctl_dev_info_args di = {
332 .devid = id,
333 };
334 struct stat st;
335
336 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
337 if (errno == ENODEV)
338 continue;
339
340 return -errno;
341 }
342
343 if (stat((char*) di.path, &st) < 0)
344 return -errno;
345
346 if (!S_ISBLK(st.st_mode))
347 return -ENODEV;
348
349 if (major(st.st_rdev) == 0)
350 return -ENODEV;
351
352 *dev = st.st_rdev;
353 return 1;
354 }
355
356 return -ENODEV;
357}
10f9c755
LP
358
359int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
360 struct btrfs_ioctl_ino_lookup_args args = {
361 .objectid = BTRFS_FIRST_FREE_OBJECTID
362 };
363
364 assert(fd >= 0);
365 assert(ret);
366
367 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
368 return -errno;
369
370 *ret = args.treeid;
371 return 0;
372}
373
5743a585
LP
374static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
375 assert(args);
376
377 /* the objectid, type, offset together make up the btrfs key,
378 * which is considered a single 136byte integer when
379 * comparing. This call increases the counter by one, dealing
380 * with the overflow between the overflows */
381
382 if (args->key.min_offset < (uint64_t) -1) {
383 args->key.min_offset++;
384 return true;
385 }
386
387 if (args->key.min_type < (uint8_t) -1) {
388 args->key.min_type++;
389 args->key.min_offset = 0;
390 return true;
391 }
392
393 if (args->key.min_objectid < (uint64_t) -1) {
394 args->key.min_objectid++;
395 args->key.min_offset = 0;
396 args->key.min_type = 0;
397 return true;
398 }
399
400 return 0;
401}
402
403static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
404 assert(args);
405 assert(h);
406
407 args->key.min_objectid = h->objectid;
408 args->key.min_type = h->type;
409 args->key.min_offset = h->offset;
410}
411
412static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
413 assert(args);
414
415 /* Compare min and max */
416
417 if (args->key.min_objectid < args->key.max_objectid)
418 return -1;
419 if (args->key.min_objectid > args->key.max_objectid)
420 return 1;
421
422 if (args->key.min_type < args->key.max_type)
423 return -1;
424 if (args->key.min_type > args->key.max_type)
425 return 1;
426
427 if (args->key.min_offset < args->key.max_offset)
428 return -1;
429 if (args->key.min_offset > args->key.max_offset)
430 return 1;
431
432 return 0;
433}
434
435#define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \
436 for ((i) = 0, \
437 (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
438 (i) < (args).key.nr_items; \
439 (i)++, \
440 (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
441
442#define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh) \
443 ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
444
10f9c755
LP
445int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
446 struct btrfs_ioctl_search_args args = {
447 /* Tree of tree roots */
b6b18498 448 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
10f9c755
LP
449
450 /* Look precisely for the subvolume items */
451 .key.min_type = BTRFS_ROOT_ITEM_KEY,
452 .key.max_type = BTRFS_ROOT_ITEM_KEY,
453
10f9c755
LP
454 .key.min_offset = 0,
455 .key.max_offset = (uint64_t) -1,
5743a585
LP
456
457 /* No restrictions on the other components */
10f9c755
LP
458 .key.min_transid = 0,
459 .key.max_transid = (uint64_t) -1,
10f9c755
LP
460 };
461
10f9c755 462 uint64_t subvol_id;
b6b18498 463 bool found = false;
10f9c755
LP
464 int r;
465
466 assert(fd >= 0);
467 assert(ret);
468
469 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
470 if (r < 0)
471 return r;
472
473 args.key.min_objectid = args.key.max_objectid = subvol_id;
10f9c755 474
5743a585 475 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
b6b18498
LP
476 const struct btrfs_ioctl_search_header *sh;
477 unsigned i;
478
479 args.key.nr_items = 256;
480 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
481 return -errno;
482
483 if (args.key.nr_items <= 0)
484 break;
10f9c755 485
5743a585 486 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
10f9c755 487
b6b18498
LP
488 const struct btrfs_root_item *ri;
489
5743a585
LP
490 /* Make sure we start the next search at least from this entry */
491 btrfs_ioctl_search_args_set(&args, sh);
492
b6b18498
LP
493 if (sh->objectid != subvol_id)
494 continue;
495 if (sh->type != BTRFS_ROOT_ITEM_KEY)
496 continue;
5743a585
LP
497
498 /* Older versions of the struct lacked the otime setting */
b6b18498
LP
499 if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
500 continue;
10f9c755 501
5743a585 502 ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
10f9c755 503
b6b18498
LP
504 ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
505 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
10f9c755 506
b6b18498
LP
507 ret->subvol_id = subvol_id;
508 ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
10f9c755 509
b6b18498
LP
510 assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
511 memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
512 memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
513
514 found = true;
515 goto finish;
516 }
517
5743a585
LP
518 /* Increase search key by one, to read the next item, if we can. */
519 if (!btrfs_ioctl_search_args_inc(&args))
b6b18498
LP
520 break;
521 }
522
523finish:
524 if (!found)
525 return -ENODATA;
526
527 return 0;
528}
529
530int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
531
532 struct btrfs_ioctl_search_args args = {
533 /* Tree of quota items */
534 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
535
5743a585
LP
536 /* The object ID is always 0 */
537 .key.min_objectid = 0,
538 .key.max_objectid = 0,
539
b6b18498
LP
540 /* Look precisely for the quota items */
541 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
542 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
543
b6b18498
LP
544 /* No restrictions on the other components */
545 .key.min_transid = 0,
546 .key.max_transid = (uint64_t) -1,
547 };
548
549 uint64_t subvol_id;
550 bool found_info = false, found_limit = false;
551 int r;
552
553 assert(fd >= 0);
554 assert(ret);
555
556 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
557 if (r < 0)
558 return r;
559
560 args.key.min_offset = args.key.max_offset = subvol_id;
561
5743a585 562 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
b6b18498
LP
563 const struct btrfs_ioctl_search_header *sh;
564 unsigned i;
565
566 args.key.nr_items = 256;
567 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
568 return -errno;
569
570 if (args.key.nr_items <= 0)
571 break;
572
5743a585 573 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
b6b18498 574
5743a585
LP
575 /* Make sure we start the next search at least from this entry */
576 btrfs_ioctl_search_args_set(&args, sh);
b6b18498
LP
577
578 if (sh->objectid != 0)
579 continue;
580 if (sh->offset != subvol_id)
581 continue;
582
b6b18498 583 if (sh->type == BTRFS_QGROUP_INFO_KEY) {
5743a585 584 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
b6b18498
LP
585
586 ret->referred = le64toh(qii->rfer);
587 ret->exclusive = le64toh(qii->excl);
588
589 found_info = true;
590
591 } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
5743a585 592 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
b6b18498
LP
593
594 ret->referred_max = le64toh(qli->max_rfer);
595 ret->exclusive_max = le64toh(qli->max_excl);
596
597 if (ret->referred_max == 0)
598 ret->referred_max = (uint64_t) -1;
599 if (ret->exclusive_max == 0)
600 ret->exclusive_max = (uint64_t) -1;
601
602 found_limit = true;
603 }
604
605 if (found_info && found_limit)
606 goto finish;
607 }
608
5743a585
LP
609 /* Increase search key by one, to read the next item, if we can. */
610 if (!btrfs_ioctl_search_args_inc(&args))
b6b18498
LP
611 break;
612 }
613
614finish:
615 if (!found_limit && !found_info)
616 return -ENODATA;
617
618 if (!found_info) {
619 ret->referred = (uint64_t) -1;
620 ret->exclusive = (uint64_t) -1;
621 }
622
623 if (!found_limit) {
624 ret->referred_max = (uint64_t) -1;
625 ret->exclusive_max = (uint64_t) -1;
626 }
10f9c755
LP
627
628 return 0;
629}
f27a3864
LP
630
631int btrfs_defrag_fd(int fd) {
632 assert(fd >= 0);
633
634 if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
635 return -errno;
636
637 return 0;
638}
639
640int btrfs_defrag(const char *p) {
641 _cleanup_close_ int fd = -1;
642
643 fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
644 if (fd < 0)
645 return -errno;
646
647 return btrfs_defrag_fd(fd);
648}