Line data Source code
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 "fileio.h"
38 : #include "btrfs-ctree.h"
39 : #include "btrfs-util.h"
40 :
41 : /* WARNING: Be careful with file system ioctls! When we get an fd, we
42 : * need to make sure it either refers to only a regular file or
43 : * directory, or that it is located on btrfs, before invoking any
44 : * btrfs ioctls. The ioctl numbers are reused by some device drivers
45 : * (such as DRM), and hence might have bad effects when invoked on
46 : * device nodes (that reference drivers) rather than fds to normal
47 : * files or directories. */
48 :
49 0 : static int validate_subvolume_name(const char *name) {
50 :
51 0 : if (!filename_is_valid(name))
52 0 : return -EINVAL;
53 :
54 0 : if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
55 0 : return -E2BIG;
56 :
57 0 : return 0;
58 : }
59 :
60 0 : static int open_parent(const char *path, int flags) {
61 0 : _cleanup_free_ char *parent = NULL;
62 : int r, fd;
63 :
64 0 : assert(path);
65 :
66 0 : r = path_get_parent(path, &parent);
67 0 : if (r < 0)
68 0 : return r;
69 :
70 0 : fd = open(parent, flags);
71 0 : if (fd < 0)
72 0 : return -errno;
73 :
74 0 : return fd;
75 : }
76 :
77 0 : static int extract_subvolume_name(const char *path, const char **subvolume) {
78 : const char *fn;
79 : int r;
80 :
81 0 : assert(path);
82 0 : assert(subvolume);
83 :
84 0 : fn = basename(path);
85 :
86 0 : r = validate_subvolume_name(fn);
87 0 : if (r < 0)
88 0 : return r;
89 :
90 0 : *subvolume = fn;
91 0 : return 0;
92 : }
93 :
94 18 : int btrfs_is_filesystem(int fd) {
95 : struct statfs sfs;
96 :
97 18 : assert(fd >= 0);
98 :
99 18 : if (fstatfs(fd, &sfs) < 0)
100 0 : return -errno;
101 :
102 18 : return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
103 : }
104 :
105 0 : int btrfs_is_subvol(int fd) {
106 : struct stat st;
107 :
108 0 : assert(fd >= 0);
109 :
110 : /* On btrfs subvolumes always have the inode 256 */
111 :
112 0 : if (fstat(fd, &st) < 0)
113 0 : return -errno;
114 :
115 0 : if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
116 0 : return 0;
117 :
118 0 : return btrfs_is_filesystem(fd);
119 : }
120 :
121 0 : int btrfs_subvol_make(const char *path) {
122 0 : struct btrfs_ioctl_vol_args args = {};
123 0 : _cleanup_close_ int fd = -1;
124 : const char *subvolume;
125 : int r;
126 :
127 0 : assert(path);
128 :
129 0 : r = extract_subvolume_name(path, &subvolume);
130 0 : if (r < 0)
131 0 : return r;
132 :
133 0 : fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
134 0 : if (fd < 0)
135 0 : return fd;
136 :
137 0 : strncpy(args.name, subvolume, sizeof(args.name)-1);
138 :
139 0 : if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
140 0 : return -errno;
141 :
142 0 : return 0;
143 : }
144 :
145 0 : int btrfs_subvol_make_label(const char *path) {
146 : int r;
147 :
148 0 : assert(path);
149 :
150 0 : r = mac_selinux_create_file_prepare(path, S_IFDIR);
151 0 : if (r < 0)
152 0 : return r;
153 :
154 0 : r = btrfs_subvol_make(path);
155 0 : mac_selinux_create_file_clear();
156 :
157 0 : if (r < 0)
158 0 : return r;
159 :
160 0 : return mac_smack_fix(path, false, false);
161 : }
162 :
163 0 : int btrfs_subvol_set_read_only_fd(int fd, bool b) {
164 : uint64_t flags, nflags;
165 : struct stat st;
166 :
167 0 : assert(fd >= 0);
168 :
169 0 : if (fstat(fd, &st) < 0)
170 0 : return -errno;
171 :
172 0 : if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
173 0 : return -EINVAL;
174 :
175 0 : if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
176 0 : return -errno;
177 :
178 0 : if (b)
179 0 : nflags = flags | BTRFS_SUBVOL_RDONLY;
180 : else
181 0 : nflags = flags & ~BTRFS_SUBVOL_RDONLY;
182 :
183 0 : if (flags == nflags)
184 0 : return 0;
185 :
186 0 : if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
187 0 : return -errno;
188 :
189 0 : return 0;
190 : }
191 :
192 0 : int btrfs_subvol_set_read_only(const char *path, bool b) {
193 0 : _cleanup_close_ int fd = -1;
194 :
195 0 : fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
196 0 : if (fd < 0)
197 0 : return -errno;
198 :
199 0 : return btrfs_subvol_set_read_only_fd(fd, b);
200 : }
201 :
202 0 : int btrfs_subvol_get_read_only_fd(int fd) {
203 : uint64_t flags;
204 : struct stat st;
205 :
206 0 : assert(fd >= 0);
207 :
208 0 : if (fstat(fd, &st) < 0)
209 0 : return -errno;
210 :
211 0 : if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
212 0 : return -EINVAL;
213 :
214 0 : if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
215 0 : return -errno;
216 :
217 0 : return !!(flags & BTRFS_SUBVOL_RDONLY);
218 : }
219 :
220 6 : int btrfs_reflink(int infd, int outfd) {
221 : struct stat st;
222 : int r;
223 :
224 6 : assert(infd >= 0);
225 6 : assert(outfd >= 0);
226 :
227 : /* Make sure we invoke the ioctl on a regular file, so that no
228 : * device driver accidentally gets it. */
229 :
230 6 : if (fstat(outfd, &st) < 0)
231 0 : return -errno;
232 :
233 6 : if (!S_ISREG(st.st_mode))
234 0 : return -EINVAL;
235 :
236 6 : r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
237 6 : if (r < 0)
238 6 : return -errno;
239 :
240 0 : return 0;
241 : }
242 :
243 0 : int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
244 0 : struct btrfs_ioctl_clone_range_args args = {
245 : .src_fd = infd,
246 : .src_offset = in_offset,
247 : .src_length = sz,
248 : .dest_offset = out_offset,
249 : };
250 : struct stat st;
251 : int r;
252 :
253 0 : assert(infd >= 0);
254 0 : assert(outfd >= 0);
255 0 : assert(sz > 0);
256 :
257 0 : if (fstat(outfd, &st) < 0)
258 0 : return -errno;
259 :
260 0 : if (!S_ISREG(st.st_mode))
261 0 : return -EINVAL;
262 :
263 0 : r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
264 0 : if (r < 0)
265 0 : return -errno;
266 :
267 0 : return 0;
268 : }
269 :
270 0 : int btrfs_get_block_device_fd(int fd, dev_t *dev) {
271 0 : struct btrfs_ioctl_fs_info_args fsi = {};
272 : uint64_t id;
273 : int r;
274 :
275 0 : assert(fd >= 0);
276 0 : assert(dev);
277 :
278 0 : r = btrfs_is_filesystem(fd);
279 0 : if (r < 0)
280 0 : return r;
281 0 : if (!r)
282 0 : return -ENOTTY;
283 :
284 0 : if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
285 0 : return -errno;
286 :
287 : /* We won't do this for btrfs RAID */
288 0 : if (fsi.num_devices != 1)
289 0 : return 0;
290 :
291 0 : for (id = 1; id <= fsi.max_id; id++) {
292 0 : struct btrfs_ioctl_dev_info_args di = {
293 : .devid = id,
294 : };
295 : struct stat st;
296 :
297 0 : if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
298 0 : if (errno == ENODEV)
299 0 : continue;
300 :
301 0 : return -errno;
302 : }
303 :
304 0 : if (stat((char*) di.path, &st) < 0)
305 0 : return -errno;
306 :
307 0 : if (!S_ISBLK(st.st_mode))
308 0 : return -ENODEV;
309 :
310 0 : if (major(st.st_rdev) == 0)
311 0 : return -ENODEV;
312 :
313 0 : *dev = st.st_rdev;
314 0 : return 1;
315 : }
316 :
317 0 : return -ENODEV;
318 : }
319 :
320 0 : int btrfs_get_block_device(const char *path, dev_t *dev) {
321 0 : _cleanup_close_ int fd = -1;
322 :
323 0 : assert(path);
324 0 : assert(dev);
325 :
326 0 : fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
327 0 : if (fd < 0)
328 0 : return -errno;
329 :
330 0 : return btrfs_get_block_device_fd(fd, dev);
331 : }
332 :
333 0 : int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
334 0 : struct btrfs_ioctl_ino_lookup_args args = {
335 : .objectid = BTRFS_FIRST_FREE_OBJECTID
336 : };
337 : int r;
338 :
339 0 : assert(fd >= 0);
340 0 : assert(ret);
341 :
342 0 : r = btrfs_is_filesystem(fd);
343 0 : if (r < 0)
344 0 : return r;
345 0 : if (!r)
346 0 : return -ENOTTY;
347 :
348 0 : if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
349 0 : return -errno;
350 :
351 0 : *ret = args.treeid;
352 0 : return 0;
353 : }
354 :
355 0 : int btrfs_subvol_get_id(int fd, const char *subvol, uint64_t *ret) {
356 0 : _cleanup_close_ int subvol_fd = -1;
357 :
358 0 : assert(fd >= 0);
359 0 : assert(ret);
360 :
361 0 : subvol_fd = openat(fd, subvol, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
362 0 : if (subvol_fd < 0)
363 0 : return -errno;
364 :
365 0 : return btrfs_subvol_get_id_fd(subvol_fd, ret);
366 : }
367 :
368 0 : static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
369 0 : assert(args);
370 :
371 : /* the objectid, type, offset together make up the btrfs key,
372 : * which is considered a single 136byte integer when
373 : * comparing. This call increases the counter by one, dealing
374 : * with the overflow between the overflows */
375 :
376 0 : if (args->key.min_offset < (uint64_t) -1) {
377 0 : args->key.min_offset++;
378 0 : return true;
379 : }
380 :
381 0 : if (args->key.min_type < (uint8_t) -1) {
382 0 : args->key.min_type++;
383 0 : args->key.min_offset = 0;
384 0 : return true;
385 : }
386 :
387 0 : if (args->key.min_objectid < (uint64_t) -1) {
388 0 : args->key.min_objectid++;
389 0 : args->key.min_offset = 0;
390 0 : args->key.min_type = 0;
391 0 : return true;
392 : }
393 :
394 0 : return 0;
395 : }
396 :
397 0 : static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
398 0 : assert(args);
399 0 : assert(h);
400 :
401 0 : args->key.min_objectid = h->objectid;
402 0 : args->key.min_type = h->type;
403 0 : args->key.min_offset = h->offset;
404 0 : }
405 :
406 0 : static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
407 0 : assert(args);
408 :
409 : /* Compare min and max */
410 :
411 0 : if (args->key.min_objectid < args->key.max_objectid)
412 0 : return -1;
413 0 : if (args->key.min_objectid > args->key.max_objectid)
414 0 : return 1;
415 :
416 0 : if (args->key.min_type < args->key.max_type)
417 0 : return -1;
418 0 : if (args->key.min_type > args->key.max_type)
419 0 : return 1;
420 :
421 0 : if (args->key.min_offset < args->key.max_offset)
422 0 : return -1;
423 0 : if (args->key.min_offset > args->key.max_offset)
424 0 : return 1;
425 :
426 0 : return 0;
427 : }
428 :
429 : #define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \
430 : for ((i) = 0, \
431 : (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
432 : (i) < (args).key.nr_items; \
433 : (i)++, \
434 : (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
435 :
436 : #define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh) \
437 : ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
438 :
439 0 : int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
440 0 : struct btrfs_ioctl_search_args args = {
441 : /* Tree of tree roots */
442 : .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
443 :
444 : /* Look precisely for the subvolume items */
445 : .key.min_type = BTRFS_ROOT_ITEM_KEY,
446 : .key.max_type = BTRFS_ROOT_ITEM_KEY,
447 :
448 : .key.min_offset = 0,
449 : .key.max_offset = (uint64_t) -1,
450 :
451 : /* No restrictions on the other components */
452 : .key.min_transid = 0,
453 : .key.max_transid = (uint64_t) -1,
454 : };
455 :
456 : uint64_t subvol_id;
457 0 : bool found = false;
458 : int r;
459 :
460 0 : assert(fd >= 0);
461 0 : assert(ret);
462 :
463 0 : r = btrfs_subvol_get_id_fd(fd, &subvol_id);
464 0 : if (r < 0)
465 0 : return r;
466 :
467 0 : args.key.min_objectid = args.key.max_objectid = subvol_id;
468 :
469 0 : while (btrfs_ioctl_search_args_compare(&args) <= 0) {
470 : const struct btrfs_ioctl_search_header *sh;
471 : unsigned i;
472 :
473 0 : args.key.nr_items = 256;
474 0 : if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
475 0 : return -errno;
476 :
477 0 : if (args.key.nr_items <= 0)
478 0 : break;
479 :
480 0 : FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
481 :
482 : const struct btrfs_root_item *ri;
483 :
484 : /* Make sure we start the next search at least from this entry */
485 0 : btrfs_ioctl_search_args_set(&args, sh);
486 :
487 0 : if (sh->objectid != subvol_id)
488 0 : continue;
489 0 : if (sh->type != BTRFS_ROOT_ITEM_KEY)
490 0 : continue;
491 :
492 : /* Older versions of the struct lacked the otime setting */
493 0 : if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
494 0 : continue;
495 :
496 0 : ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
497 :
498 0 : ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
499 0 : (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
500 :
501 0 : ret->subvol_id = subvol_id;
502 0 : ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
503 :
504 : assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
505 0 : memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
506 0 : memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
507 :
508 0 : found = true;
509 0 : goto finish;
510 : }
511 :
512 : /* Increase search key by one, to read the next item, if we can. */
513 0 : if (!btrfs_ioctl_search_args_inc(&args))
514 0 : break;
515 : }
516 :
517 : finish:
518 0 : if (!found)
519 0 : return -ENODATA;
520 :
521 0 : return 0;
522 : }
523 :
524 0 : int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
525 :
526 0 : struct btrfs_ioctl_search_args args = {
527 : /* Tree of quota items */
528 : .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
529 :
530 : /* The object ID is always 0 */
531 : .key.min_objectid = 0,
532 : .key.max_objectid = 0,
533 :
534 : /* Look precisely for the quota items */
535 : .key.min_type = BTRFS_QGROUP_STATUS_KEY,
536 : .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
537 :
538 : /* No restrictions on the other components */
539 : .key.min_transid = 0,
540 : .key.max_transid = (uint64_t) -1,
541 : };
542 :
543 : uint64_t subvol_id;
544 0 : bool found_info = false, found_limit = false;
545 : int r;
546 :
547 0 : assert(fd >= 0);
548 0 : assert(ret);
549 :
550 0 : r = btrfs_subvol_get_id_fd(fd, &subvol_id);
551 0 : if (r < 0)
552 0 : return r;
553 :
554 0 : args.key.min_offset = args.key.max_offset = subvol_id;
555 :
556 0 : while (btrfs_ioctl_search_args_compare(&args) <= 0) {
557 : const struct btrfs_ioctl_search_header *sh;
558 : unsigned i;
559 :
560 0 : args.key.nr_items = 256;
561 0 : if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
562 0 : return -errno;
563 :
564 0 : if (args.key.nr_items <= 0)
565 0 : break;
566 :
567 0 : FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
568 :
569 : /* Make sure we start the next search at least from this entry */
570 0 : btrfs_ioctl_search_args_set(&args, sh);
571 :
572 0 : if (sh->objectid != 0)
573 0 : continue;
574 0 : if (sh->offset != subvol_id)
575 0 : continue;
576 :
577 0 : if (sh->type == BTRFS_QGROUP_INFO_KEY) {
578 0 : const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
579 :
580 0 : ret->referenced = le64toh(qii->rfer);
581 0 : ret->exclusive = le64toh(qii->excl);
582 :
583 0 : found_info = true;
584 :
585 0 : } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
586 0 : const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
587 :
588 0 : ret->referenced_max = le64toh(qli->max_rfer);
589 0 : ret->exclusive_max = le64toh(qli->max_excl);
590 :
591 0 : if (ret->referenced_max == 0)
592 0 : ret->referenced_max = (uint64_t) -1;
593 0 : if (ret->exclusive_max == 0)
594 0 : ret->exclusive_max = (uint64_t) -1;
595 :
596 0 : found_limit = true;
597 : }
598 :
599 0 : if (found_info && found_limit)
600 0 : goto finish;
601 : }
602 :
603 : /* Increase search key by one, to read the next item, if we can. */
604 0 : if (!btrfs_ioctl_search_args_inc(&args))
605 0 : break;
606 : }
607 :
608 : finish:
609 0 : if (!found_limit && !found_info)
610 0 : return -ENODATA;
611 :
612 0 : if (!found_info) {
613 0 : ret->referenced = (uint64_t) -1;
614 0 : ret->exclusive = (uint64_t) -1;
615 : }
616 :
617 0 : if (!found_limit) {
618 0 : ret->referenced_max = (uint64_t) -1;
619 0 : ret->exclusive_max = (uint64_t) -1;
620 : }
621 :
622 0 : return 0;
623 : }
624 :
625 2 : int btrfs_defrag_fd(int fd) {
626 : struct stat st;
627 :
628 2 : assert(fd >= 0);
629 :
630 2 : if (fstat(fd, &st) < 0)
631 0 : return -errno;
632 :
633 2 : if (!S_ISREG(st.st_mode))
634 0 : return -EINVAL;
635 :
636 2 : if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
637 2 : return -errno;
638 :
639 0 : return 0;
640 : }
641 :
642 0 : int btrfs_defrag(const char *p) {
643 0 : _cleanup_close_ int fd = -1;
644 :
645 0 : fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
646 0 : if (fd < 0)
647 0 : return -errno;
648 :
649 0 : return btrfs_defrag_fd(fd);
650 : }
651 :
652 0 : int btrfs_quota_enable_fd(int fd, bool b) {
653 0 : struct btrfs_ioctl_quota_ctl_args args = {
654 : .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
655 : };
656 : int r;
657 :
658 0 : assert(fd >= 0);
659 :
660 0 : r = btrfs_is_filesystem(fd);
661 0 : if (r < 0)
662 0 : return r;
663 0 : if (!r)
664 0 : return -ENOTTY;
665 :
666 0 : if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
667 0 : return -errno;
668 :
669 0 : return 0;
670 : }
671 :
672 0 : int btrfs_quota_enable(const char *path, bool b) {
673 0 : _cleanup_close_ int fd = -1;
674 :
675 0 : fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
676 0 : if (fd < 0)
677 0 : return -errno;
678 :
679 0 : return btrfs_quota_enable_fd(fd, b);
680 : }
681 :
682 0 : int btrfs_quota_limit_fd(int fd, uint64_t referenced_max) {
683 0 : struct btrfs_ioctl_qgroup_limit_args args = {
684 : .lim.max_rfer =
685 : referenced_max == (uint64_t) -1 ? 0 :
686 : referenced_max == 0 ? 1 : referenced_max,
687 : .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
688 : };
689 : int r;
690 :
691 0 : assert(fd >= 0);
692 :
693 0 : r = btrfs_is_filesystem(fd);
694 0 : if (r < 0)
695 0 : return r;
696 0 : if (!r)
697 0 : return -ENOTTY;
698 :
699 0 : if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0)
700 0 : return -errno;
701 :
702 0 : return 0;
703 : }
704 :
705 0 : int btrfs_quota_limit(const char *path, uint64_t referenced_max) {
706 0 : _cleanup_close_ int fd = -1;
707 :
708 0 : fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
709 0 : if (fd < 0)
710 0 : return -errno;
711 :
712 0 : return btrfs_quota_limit_fd(fd, referenced_max);
713 : }
714 :
715 0 : int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
716 0 : struct btrfs_ioctl_vol_args args = {};
717 0 : _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
718 0 : _cleanup_close_ int loop_fd = -1, backing_fd = -1;
719 : struct stat st;
720 0 : dev_t dev = 0;
721 : int r;
722 :
723 : /* btrfs cannot handle file systems < 16M, hence use this as minimum */
724 0 : if (new_size < 16*1024*1024)
725 0 : new_size = 16*1024*1024;
726 :
727 0 : r = btrfs_get_block_device_fd(fd, &dev);
728 0 : if (r < 0)
729 0 : return r;
730 0 : if (r == 0)
731 0 : return -ENODEV;
732 :
733 0 : if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
734 0 : return -ENOMEM;
735 0 : r = read_one_line_file(p, &backing);
736 0 : if (r == -ENOENT)
737 0 : return -ENODEV;
738 0 : if (r < 0)
739 0 : return r;
740 0 : if (isempty(backing) || !path_is_absolute(backing))
741 0 : return -ENODEV;
742 :
743 0 : backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
744 0 : if (backing_fd < 0)
745 0 : return -errno;
746 :
747 0 : if (fstat(backing_fd, &st) < 0)
748 0 : return -errno;
749 0 : if (!S_ISREG(st.st_mode))
750 0 : return -ENODEV;
751 :
752 0 : if (new_size == (uint64_t) st.st_size)
753 0 : return 0;
754 :
755 0 : if (grow_only && new_size < (uint64_t) st.st_size)
756 0 : return -EINVAL;
757 :
758 0 : if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
759 0 : return -ENOMEM;
760 0 : loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
761 0 : if (loop_fd < 0)
762 0 : return -errno;
763 :
764 0 : if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
765 0 : return -EINVAL;
766 :
767 0 : if (new_size < (uint64_t) st.st_size) {
768 : /* Decrease size: first decrease btrfs size, then shorten loopback */
769 0 : if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
770 0 : return -errno;
771 : }
772 :
773 0 : if (ftruncate(backing_fd, new_size) < 0)
774 0 : return -errno;
775 :
776 0 : if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
777 0 : return -errno;
778 :
779 0 : if (new_size > (uint64_t) st.st_size) {
780 : /* Increase size: first enlarge loopback, then increase btrfs size */
781 0 : if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
782 0 : return -errno;
783 : }
784 :
785 : /* Make sure the free disk space is correctly updated for both file systems */
786 0 : (void) fsync(fd);
787 0 : (void) fsync(backing_fd);
788 :
789 0 : return 1;
790 : }
791 :
792 0 : int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
793 0 : _cleanup_close_ int fd = -1;
794 :
795 0 : fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
796 0 : if (fd < 0)
797 0 : return -errno;
798 :
799 0 : return btrfs_resize_loopback_fd(fd, new_size, grow_only);
800 : }
801 :
802 0 : static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, bool recursive) {
803 0 : struct btrfs_ioctl_search_args args = {
804 : .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
805 :
806 : .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
807 : .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
808 :
809 : .key.min_type = BTRFS_ROOT_BACKREF_KEY,
810 : .key.max_type = BTRFS_ROOT_BACKREF_KEY,
811 :
812 : .key.min_transid = 0,
813 : .key.max_transid = (uint64_t) -1,
814 : };
815 :
816 0 : struct btrfs_ioctl_vol_args vol_args = {};
817 0 : _cleanup_close_ int subvol_fd = -1;
818 : struct stat st;
819 0 : bool made_writable = false;
820 : int r;
821 :
822 0 : assert(fd >= 0);
823 0 : assert(subvolume);
824 :
825 0 : if (fstat(fd, &st) < 0)
826 0 : return -errno;
827 :
828 0 : if (!S_ISDIR(st.st_mode))
829 0 : return -EINVAL;
830 :
831 : /* First, try to remove the subvolume. If it happens to be
832 : * already empty, this will just work. */
833 0 : strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
834 0 : if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0)
835 0 : return 0;
836 0 : if (!recursive || errno != ENOTEMPTY)
837 0 : return -errno;
838 :
839 : /* OK, the subvolume is not empty, let's look for child
840 : * subvolumes, and remove them, first */
841 0 : subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
842 0 : if (subvol_fd < 0)
843 0 : return -errno;
844 :
845 0 : if (subvol_id == 0) {
846 0 : r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
847 0 : if (r < 0)
848 0 : return r;
849 : }
850 :
851 0 : args.key.min_offset = args.key.max_offset = subvol_id;
852 :
853 0 : while (btrfs_ioctl_search_args_compare(&args) <= 0) {
854 : const struct btrfs_ioctl_search_header *sh;
855 : unsigned i;
856 :
857 0 : args.key.nr_items = 256;
858 0 : if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
859 0 : return -errno;
860 :
861 0 : if (args.key.nr_items <= 0)
862 0 : break;
863 :
864 0 : FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
865 0 : _cleanup_free_ char *p = NULL;
866 : const struct btrfs_root_ref *ref;
867 : struct btrfs_ioctl_ino_lookup_args ino_args;
868 :
869 0 : btrfs_ioctl_search_args_set(&args, sh);
870 :
871 0 : if (sh->type != BTRFS_ROOT_BACKREF_KEY)
872 0 : continue;
873 0 : if (sh->offset != subvol_id)
874 0 : continue;
875 :
876 0 : ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
877 :
878 0 : p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
879 0 : if (!p)
880 0 : return -ENOMEM;
881 :
882 0 : zero(ino_args);
883 0 : ino_args.treeid = subvol_id;
884 0 : ino_args.objectid = htole64(ref->dirid);
885 :
886 0 : if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
887 0 : return -errno;
888 :
889 0 : if (!made_writable) {
890 0 : r = btrfs_subvol_set_read_only_fd(subvol_fd, false);
891 0 : if (r < 0)
892 0 : return r;
893 :
894 0 : made_writable = true;
895 : }
896 :
897 0 : if (isempty(ino_args.name))
898 : /* Subvolume is in the top-level
899 : * directory of the subvolume. */
900 0 : r = subvol_remove_children(subvol_fd, p, sh->objectid, recursive);
901 : else {
902 0 : _cleanup_close_ int child_fd = -1;
903 :
904 : /* Subvolume is somewhere further down,
905 : * hence we need to open the
906 : * containing directory first */
907 :
908 0 : child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
909 0 : if (child_fd < 0)
910 0 : return -errno;
911 :
912 0 : r = subvol_remove_children(child_fd, p, sh->objectid, recursive);
913 : }
914 0 : if (r < 0)
915 0 : return r;
916 : }
917 :
918 : /* Increase search key by one, to read the next item, if we can. */
919 0 : if (!btrfs_ioctl_search_args_inc(&args))
920 0 : break;
921 : }
922 :
923 : /* OK, the child subvolumes should all be gone now, let's try
924 : * again to remove the subvolume */
925 0 : if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0)
926 0 : return -errno;
927 :
928 0 : return 0;
929 : }
930 :
931 0 : int btrfs_subvol_remove(const char *path, bool recursive) {
932 0 : _cleanup_close_ int fd = -1;
933 : const char *subvolume;
934 : int r;
935 :
936 0 : assert(path);
937 :
938 0 : r = extract_subvolume_name(path, &subvolume);
939 0 : if (r < 0)
940 0 : return r;
941 :
942 0 : fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
943 0 : if (fd < 0)
944 0 : return fd;
945 :
946 0 : return subvol_remove_children(fd, subvolume, 0, recursive);
947 : }
948 :
949 0 : int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive) {
950 0 : return subvol_remove_children(fd, subvolume, 0, recursive);
951 : }
952 :
953 0 : static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t old_subvol_id, BtrfsSnapshotFlags flags) {
954 :
955 0 : struct btrfs_ioctl_search_args args = {
956 : .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
957 :
958 : .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
959 : .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
960 :
961 : .key.min_type = BTRFS_ROOT_BACKREF_KEY,
962 : .key.max_type = BTRFS_ROOT_BACKREF_KEY,
963 :
964 : .key.min_transid = 0,
965 : .key.max_transid = (uint64_t) -1,
966 : };
967 :
968 0 : struct btrfs_ioctl_vol_args_v2 vol_args = {
969 0 : .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0,
970 : .fd = old_fd,
971 : };
972 0 : _cleanup_close_ int subvolume_fd = -1;
973 : uint64_t new_subvol_id;
974 : int r;
975 :
976 0 : assert(old_fd >= 0);
977 0 : assert(new_fd >= 0);
978 0 : assert(subvolume);
979 :
980 0 : strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
981 0 : vol_args.fd = old_fd;
982 :
983 0 : if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0)
984 0 : return -errno;
985 :
986 0 : if (!(flags & BTRFS_SNAPSHOT_RECURSIVE))
987 0 : return 0;
988 :
989 0 : if (old_subvol_id == 0) {
990 0 : r = btrfs_subvol_get_id_fd(old_fd, &old_subvol_id);
991 0 : if (r < 0)
992 0 : return r;
993 : }
994 :
995 0 : r = btrfs_subvol_get_id(new_fd, vol_args.name, &new_subvol_id);
996 0 : if (r < 0)
997 0 : return r;
998 :
999 0 : args.key.min_offset = args.key.max_offset = old_subvol_id;
1000 :
1001 0 : while (btrfs_ioctl_search_args_compare(&args) <= 0) {
1002 : const struct btrfs_ioctl_search_header *sh;
1003 : unsigned i;
1004 :
1005 0 : args.key.nr_items = 256;
1006 0 : if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
1007 0 : return -errno;
1008 :
1009 0 : if (args.key.nr_items <= 0)
1010 0 : break;
1011 :
1012 0 : FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
1013 0 : _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL;
1014 : struct btrfs_ioctl_ino_lookup_args ino_args;
1015 : const struct btrfs_root_ref *ref;
1016 0 : _cleanup_close_ int old_child_fd = -1, new_child_fd = -1;
1017 :
1018 0 : btrfs_ioctl_search_args_set(&args, sh);
1019 :
1020 0 : if (sh->type != BTRFS_ROOT_BACKREF_KEY)
1021 0 : continue;
1022 :
1023 : /* Avoid finding the source subvolume a second
1024 : * time */
1025 0 : if (sh->offset != old_subvol_id)
1026 0 : continue;
1027 :
1028 : /* Avoid running into loops if the new
1029 : * subvolume is below the old one. */
1030 0 : if (sh->objectid == new_subvol_id)
1031 0 : continue;
1032 :
1033 0 : ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
1034 0 : p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
1035 0 : if (!p)
1036 0 : return -ENOMEM;
1037 :
1038 0 : zero(ino_args);
1039 0 : ino_args.treeid = old_subvol_id;
1040 0 : ino_args.objectid = htole64(ref->dirid);
1041 :
1042 0 : if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
1043 0 : return -errno;
1044 :
1045 : /* The kernel returns an empty name if the
1046 : * subvolume is in the top-level directory,
1047 : * and otherwise appends a slash, so that we
1048 : * can just concatenate easily here, without
1049 : * adding a slash. */
1050 0 : c = strappend(ino_args.name, p);
1051 0 : if (!c)
1052 0 : return -ENOMEM;
1053 :
1054 0 : old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1055 0 : if (old_child_fd < 0)
1056 0 : return -errno;
1057 :
1058 0 : np = strjoin(subvolume, "/", ino_args.name, NULL);
1059 0 : if (!np)
1060 0 : return -ENOMEM;
1061 :
1062 0 : new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1063 0 : if (new_child_fd < 0)
1064 0 : return -errno;
1065 :
1066 0 : if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1067 : /* If the snapshot is read-only we
1068 : * need to mark it writable
1069 : * temporarily, to put the subsnapshot
1070 : * into place. */
1071 :
1072 0 : if (subvolume_fd < 0) {
1073 0 : subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1074 0 : if (subvolume_fd < 0)
1075 0 : return -errno;
1076 : }
1077 :
1078 0 : r = btrfs_subvol_set_read_only_fd(subvolume_fd, false);
1079 0 : if (r < 0)
1080 0 : return r;
1081 : }
1082 :
1083 : /* When btrfs clones the subvolumes, child
1084 : * subvolumes appear as empty directories. Remove
1085 : * them, so that we can create a new snapshot
1086 : * in their place */
1087 0 : if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0) {
1088 0 : int k = -errno;
1089 :
1090 0 : if (flags & BTRFS_SNAPSHOT_READ_ONLY)
1091 0 : (void) btrfs_subvol_set_read_only_fd(subvolume_fd, true);
1092 :
1093 0 : return k;
1094 : }
1095 :
1096 0 : r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
1097 :
1098 : /* Restore the readonly flag */
1099 0 : if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1100 : int k;
1101 :
1102 0 : k = btrfs_subvol_set_read_only_fd(subvolume_fd, true);
1103 0 : if (r >= 0 && k < 0)
1104 0 : return k;
1105 : }
1106 :
1107 0 : if (r < 0)
1108 0 : return r;
1109 : }
1110 :
1111 : /* Increase search key by one, to read the next item, if we can. */
1112 0 : if (!btrfs_ioctl_search_args_inc(&args))
1113 0 : break;
1114 : }
1115 :
1116 0 : return 0;
1117 : }
1118 :
1119 0 : int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
1120 0 : _cleanup_close_ int new_fd = -1;
1121 : const char *subvolume;
1122 : int r;
1123 :
1124 0 : assert(old_fd >= 0);
1125 0 : assert(new_path);
1126 :
1127 0 : r = btrfs_is_subvol(old_fd);
1128 0 : if (r < 0)
1129 0 : return r;
1130 0 : if (r == 0) {
1131 0 : if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
1132 0 : return -EISDIR;
1133 :
1134 0 : r = btrfs_subvol_make(new_path);
1135 0 : if (r < 0)
1136 0 : return r;
1137 :
1138 0 : r = copy_directory_fd(old_fd, new_path, true);
1139 0 : if (r < 0) {
1140 0 : btrfs_subvol_remove(new_path, false);
1141 0 : return r;
1142 : }
1143 :
1144 0 : if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1145 0 : r = btrfs_subvol_set_read_only(new_path, true);
1146 0 : if (r < 0) {
1147 0 : btrfs_subvol_remove(new_path, false);
1148 0 : return r;
1149 : }
1150 : }
1151 :
1152 0 : return 0;
1153 : }
1154 :
1155 0 : r = extract_subvolume_name(new_path, &subvolume);
1156 0 : if (r < 0)
1157 0 : return r;
1158 :
1159 0 : new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1160 0 : if (new_fd < 0)
1161 0 : return new_fd;
1162 :
1163 0 : return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
1164 : }
1165 :
1166 0 : int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
1167 0 : _cleanup_close_ int old_fd = -1;
1168 :
1169 0 : assert(old_path);
1170 0 : assert(new_path);
1171 :
1172 0 : old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1173 0 : if (old_fd < 0)
1174 0 : return -errno;
1175 :
1176 0 : return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);
1177 : }
|