chiark / gitweb /
6761501da2589166a108be55d0ff9ae39386b4d0
[elogind.git] / src / shared / btrfs-util.c
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"
34 #include "copy.h"
35 #include "selinux-util.h"
36 #include "smack-util.h"
37 #include "btrfs-ctree.h"
38 #include "btrfs-util.h"
39
40 static 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
51 static 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
68 static 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
85 int btrfs_is_snapshot(int fd) {
86         struct stat st;
87         struct statfs sfs;
88
89         /* On btrfs subvolumes always have the inode 256 */
90
91         if (fstat(fd, &st) < 0)
92                 return -errno;
93
94         if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
95                 return 0;
96
97         if (fstatfs(fd, &sfs) < 0)
98                 return -errno;
99
100         return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
101 }
102
103 int 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
127                         r = copy_directory_fd(old_fd, new_path, true);
128                         if (r < 0) {
129                                 btrfs_subvol_remove(new_path);
130                                 return r;
131                         }
132
133                         if (read_only) {
134                                 r = btrfs_subvol_set_read_only(new_path, true);
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
164 int 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
188 int 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
206 int 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
230 int btrfs_subvol_set_read_only_fd(int fd, bool b) {
231         uint64_t flags, nflags;
232         struct stat st;
233
234         assert(fd >= 0);
235
236         if (fstat(fd, &st) < 0)
237                 return -errno;
238
239         if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
240                 return -EINVAL;
241
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
259 int 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
269 int btrfs_subvol_get_read_only_fd(int fd) {
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
278 int 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
291 int 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
311 int 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 }
358
359 int 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
374 static 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
403 static 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
412 static 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
445 int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
446         struct btrfs_ioctl_search_args args = {
447                 /* Tree of tree roots */
448                 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
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
454                 .key.min_offset = 0,
455                 .key.max_offset = (uint64_t) -1,
456
457                 /* No restrictions on the other components */
458                 .key.min_transid = 0,
459                 .key.max_transid = (uint64_t) -1,
460         };
461
462         uint64_t subvol_id;
463         bool found = false;
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;
474
475         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
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;
485
486                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
487
488                         const struct btrfs_root_item *ri;
489
490                         /* Make sure we start the next search at least from this entry */
491                         btrfs_ioctl_search_args_set(&args, sh);
492
493                         if (sh->objectid != subvol_id)
494                                 continue;
495                         if (sh->type != BTRFS_ROOT_ITEM_KEY)
496                                 continue;
497
498                         /* Older versions of the struct lacked the otime setting */
499                         if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
500                                 continue;
501
502                         ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
503
504                         ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
505                                 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
506
507                         ret->subvol_id = subvol_id;
508                         ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
509
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
518                 /* Increase search key by one, to read the next item, if we can. */
519                 if (!btrfs_ioctl_search_args_inc(&args))
520                         break;
521         }
522
523 finish:
524         if (!found)
525                 return -ENODATA;
526
527         return 0;
528 }
529
530 int 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
536                 /* The object ID is always 0 */
537                 .key.min_objectid = 0,
538                 .key.max_objectid = 0,
539
540                 /* Look precisely for the quota items */
541                 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
542                 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
543
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
562         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
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
573                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
574
575                         /* Make sure we start the next search at least from this entry */
576                         btrfs_ioctl_search_args_set(&args, sh);
577
578                         if (sh->objectid != 0)
579                                 continue;
580                         if (sh->offset != subvol_id)
581                                 continue;
582
583                         if (sh->type == BTRFS_QGROUP_INFO_KEY) {
584                                 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
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) {
592                                 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
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
609                 /* Increase search key by one, to read the next item, if we can. */
610                 if (!btrfs_ioctl_search_args_inc(&args))
611                         break;
612         }
613
614 finish:
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         }
627
628         return 0;
629 }
630
631 int 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
640 int 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 }
649
650 int btrfs_quota_enable_fd(int fd, bool b) {
651         struct btrfs_ioctl_quota_ctl_args args = {
652                 .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
653         };
654
655         assert(fd >= 0);
656
657         if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
658                 return -errno;
659
660         return 0;
661 }
662
663 int btrfs_quota_enable(const char *path, bool b) {
664         _cleanup_close_ int fd = -1;
665
666         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
667         if (fd < 0)
668                 return -errno;
669
670         return btrfs_quota_enable_fd(fd, b);
671 }
672
673 int btrfs_quota_limit_fd(int fd, uint64_t referred_max) {
674         struct btrfs_ioctl_qgroup_limit_args args = {
675                 .lim.max_rfer =
676                         referred_max == (uint64_t) -1 ? 0 :
677                         referred_max == 0 ? 1 : referred_max,
678                 .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
679         };
680
681         assert(fd >= 0);
682
683         if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0)
684                 return -errno;
685
686         return 0;
687 }
688
689 int btrfs_quota_limit(const char *path, uint64_t referred_max) {
690         _cleanup_close_ int fd = -1;
691
692         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
693         if (fd < 0)
694                 return -errno;
695
696         return btrfs_quota_limit_fd(fd, referred_max);
697 }