chiark / gitweb /
import: when downloading raw files, show simple progress reports
[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(const char *path, bool b) {
232         _cleanup_close_ int fd = -1;
233         uint64_t flags, nflags;
234
235         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
236         if (fd < 0)
237                 return -errno;
238
239         if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
240                 return -errno;
241
242         if (b)
243                 nflags = flags | BTRFS_SUBVOL_RDONLY;
244         else
245                 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
246
247         if (flags == nflags)
248                 return 0;
249
250         if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
251                 return -errno;
252
253         return 0;
254 }
255
256 int btrfs_subvol_get_read_only_fd(int fd) {
257         uint64_t flags;
258
259         if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
260                 return -errno;
261
262         return !!(flags & BTRFS_SUBVOL_RDONLY);
263 }
264
265 int btrfs_reflink(int infd, int outfd) {
266         int r;
267
268         assert(infd >= 0);
269         assert(outfd >= 0);
270
271         r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
272         if (r < 0)
273                 return -errno;
274
275         return 0;
276 }
277
278 int btrfs_get_block_device(const char *path, dev_t *dev) {
279         struct btrfs_ioctl_fs_info_args fsi = {};
280         _cleanup_close_ int fd = -1;
281         uint64_t id;
282
283         assert(path);
284         assert(dev);
285
286         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
287         if (fd < 0)
288                 return -errno;
289
290         if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
291                 return -errno;
292
293         /* We won't do this for btrfs RAID */
294         if (fsi.num_devices != 1)
295                 return 0;
296
297         for (id = 1; id <= fsi.max_id; id++) {
298                 struct btrfs_ioctl_dev_info_args di = {
299                         .devid = id,
300                 };
301                 struct stat st;
302
303                 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
304                         if (errno == ENODEV)
305                                 continue;
306
307                         return -errno;
308                 }
309
310                 if (stat((char*) di.path, &st) < 0)
311                         return -errno;
312
313                 if (!S_ISBLK(st.st_mode))
314                         return -ENODEV;
315
316                 if (major(st.st_rdev) == 0)
317                         return -ENODEV;
318
319                 *dev = st.st_rdev;
320                 return 1;
321         }
322
323         return -ENODEV;
324 }
325
326 int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
327         struct btrfs_ioctl_ino_lookup_args args = {
328                 .objectid = BTRFS_FIRST_FREE_OBJECTID
329         };
330
331         assert(fd >= 0);
332         assert(ret);
333
334         if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
335                 return -errno;
336
337         *ret = args.treeid;
338         return 0;
339 }
340
341 static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
342         assert(args);
343
344         /* the objectid, type, offset together make up the btrfs key,
345          * which is considered a single 136byte integer when
346          * comparing. This call increases the counter by one, dealing
347          * with the overflow between the overflows */
348
349         if (args->key.min_offset < (uint64_t) -1) {
350                 args->key.min_offset++;
351                 return true;
352         }
353
354         if (args->key.min_type < (uint8_t) -1) {
355                 args->key.min_type++;
356                 args->key.min_offset = 0;
357                 return true;
358         }
359
360         if (args->key.min_objectid < (uint64_t) -1) {
361                 args->key.min_objectid++;
362                 args->key.min_offset = 0;
363                 args->key.min_type = 0;
364                 return true;
365         }
366
367         return 0;
368 }
369
370 static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
371         assert(args);
372         assert(h);
373
374         args->key.min_objectid = h->objectid;
375         args->key.min_type = h->type;
376         args->key.min_offset = h->offset;
377 }
378
379 static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
380         assert(args);
381
382         /* Compare min and max */
383
384         if (args->key.min_objectid < args->key.max_objectid)
385                 return -1;
386         if (args->key.min_objectid > args->key.max_objectid)
387                 return 1;
388
389         if (args->key.min_type < args->key.max_type)
390                 return -1;
391         if (args->key.min_type > args->key.max_type)
392                 return 1;
393
394         if (args->key.min_offset < args->key.max_offset)
395                 return -1;
396         if (args->key.min_offset > args->key.max_offset)
397                 return 1;
398
399         return 0;
400 }
401
402 #define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args)                  \
403         for ((i) = 0,                                                   \
404              (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
405              (i) < (args).key.nr_items;                                 \
406              (i)++,                                                     \
407              (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
408
409 #define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh)                              \
410         ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
411
412 int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
413         struct btrfs_ioctl_search_args args = {
414                 /* Tree of tree roots */
415                 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
416
417                 /* Look precisely for the subvolume items */
418                 .key.min_type = BTRFS_ROOT_ITEM_KEY,
419                 .key.max_type = BTRFS_ROOT_ITEM_KEY,
420
421                 .key.min_offset = 0,
422                 .key.max_offset = (uint64_t) -1,
423
424                 /* No restrictions on the other components */
425                 .key.min_transid = 0,
426                 .key.max_transid = (uint64_t) -1,
427         };
428
429         uint64_t subvol_id;
430         bool found = false;
431         int r;
432
433         assert(fd >= 0);
434         assert(ret);
435
436         r = btrfs_subvol_get_id_fd(fd, &subvol_id);
437         if (r < 0)
438                 return r;
439
440         args.key.min_objectid = args.key.max_objectid = subvol_id;
441
442         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
443                 const struct btrfs_ioctl_search_header *sh;
444                 unsigned i;
445
446                 args.key.nr_items = 256;
447                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
448                         return -errno;
449
450                 if (args.key.nr_items <= 0)
451                         break;
452
453                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
454
455                         const struct btrfs_root_item *ri;
456
457                         /* Make sure we start the next search at least from this entry */
458                         btrfs_ioctl_search_args_set(&args, sh);
459
460                         if (sh->objectid != subvol_id)
461                                 continue;
462                         if (sh->type != BTRFS_ROOT_ITEM_KEY)
463                                 continue;
464
465                         /* Older versions of the struct lacked the otime setting */
466                         if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
467                                 continue;
468
469                         ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
470
471                         ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
472                                 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
473
474                         ret->subvol_id = subvol_id;
475                         ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
476
477                         assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
478                         memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
479                         memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
480
481                         found = true;
482                         goto finish;
483                 }
484
485                 /* Increase search key by one, to read the next item, if we can. */
486                 if (!btrfs_ioctl_search_args_inc(&args))
487                         break;
488         }
489
490 finish:
491         if (!found)
492                 return -ENODATA;
493
494         return 0;
495 }
496
497 int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
498
499         struct btrfs_ioctl_search_args args = {
500                 /* Tree of quota items */
501                 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
502
503                 /* The object ID is always 0 */
504                 .key.min_objectid = 0,
505                 .key.max_objectid = 0,
506
507                 /* Look precisely for the quota items */
508                 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
509                 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
510
511                 /* No restrictions on the other components */
512                 .key.min_transid = 0,
513                 .key.max_transid = (uint64_t) -1,
514         };
515
516         uint64_t subvol_id;
517         bool found_info = false, found_limit = false;
518         int r;
519
520         assert(fd >= 0);
521         assert(ret);
522
523         r = btrfs_subvol_get_id_fd(fd, &subvol_id);
524         if (r < 0)
525                 return r;
526
527         args.key.min_offset = args.key.max_offset = subvol_id;
528
529         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
530                 const struct btrfs_ioctl_search_header *sh;
531                 unsigned i;
532
533                 args.key.nr_items = 256;
534                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
535                         return -errno;
536
537                 if (args.key.nr_items <= 0)
538                         break;
539
540                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
541
542                         /* Make sure we start the next search at least from this entry */
543                         btrfs_ioctl_search_args_set(&args, sh);
544
545                         if (sh->objectid != 0)
546                                 continue;
547                         if (sh->offset != subvol_id)
548                                 continue;
549
550                         if (sh->type == BTRFS_QGROUP_INFO_KEY) {
551                                 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
552
553                                 ret->referred = le64toh(qii->rfer);
554                                 ret->exclusive = le64toh(qii->excl);
555
556                                 found_info = true;
557
558                         } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
559                                 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
560
561                                 ret->referred_max = le64toh(qli->max_rfer);
562                                 ret->exclusive_max = le64toh(qli->max_excl);
563
564                                 if (ret->referred_max == 0)
565                                         ret->referred_max = (uint64_t) -1;
566                                 if (ret->exclusive_max == 0)
567                                         ret->exclusive_max = (uint64_t) -1;
568
569                                 found_limit = true;
570                         }
571
572                         if (found_info && found_limit)
573                                 goto finish;
574                 }
575
576                 /* Increase search key by one, to read the next item, if we can. */
577                 if (!btrfs_ioctl_search_args_inc(&args))
578                         break;
579         }
580
581 finish:
582         if (!found_limit && !found_info)
583                 return -ENODATA;
584
585         if (!found_info) {
586                 ret->referred = (uint64_t) -1;
587                 ret->exclusive = (uint64_t) -1;
588         }
589
590         if (!found_limit) {
591                 ret->referred_max = (uint64_t) -1;
592                 ret->exclusive_max = (uint64_t) -1;
593         }
594
595         return 0;
596 }
597
598 int btrfs_defrag_fd(int fd) {
599         assert(fd >= 0);
600
601         if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
602                 return -errno;
603
604         return 0;
605 }
606
607 int btrfs_defrag(const char *p) {
608         _cleanup_close_ int fd = -1;
609
610         fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
611         if (fd < 0)
612                 return -errno;
613
614         return btrfs_defrag_fd(fd);
615 }