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