]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/btrfs-util.c
fix gcc warnings about uninitialized variables
[thirdparty/systemd.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 "fileio.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_fd(int old_fd, 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 new_fd = -1;
109 const char *subvolume;
110 int r;
111
112 assert(new_path);
113
114 r = btrfs_is_snapshot(old_fd);
115 if (r < 0)
116 return r;
117 if (r == 0) {
118 if (!fallback_copy)
119 return -EISDIR;
120
121 r = btrfs_subvol_make(new_path);
122 if (r < 0)
123 return r;
124
125 r = copy_directory_fd(old_fd, new_path, true);
126 if (r < 0) {
127 btrfs_subvol_remove(new_path);
128 return r;
129 }
130
131 if (read_only) {
132 r = btrfs_subvol_set_read_only(new_path, true);
133 if (r < 0) {
134 btrfs_subvol_remove(new_path);
135 return r;
136 }
137 }
138
139 return 0;
140 }
141
142 r = extract_subvolume_name(new_path, &subvolume);
143 if (r < 0)
144 return r;
145
146 new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
147 if (new_fd < 0)
148 return new_fd;
149
150 strncpy(args.name, subvolume, sizeof(args.name)-1);
151 args.fd = old_fd;
152
153 if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &args) < 0)
154 return -errno;
155
156 return 0;
157 }
158
159 int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_only, bool fallback_copy) {
160 _cleanup_close_ int old_fd = -1;
161
162 assert(old_path);
163 assert(new_path);
164
165 old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
166 if (old_fd < 0)
167 return -errno;
168
169 return btrfs_subvol_snapshot_fd(old_fd, new_path, read_only, fallback_copy);
170 }
171
172 int btrfs_subvol_make(const char *path) {
173 struct btrfs_ioctl_vol_args args = {};
174 _cleanup_close_ int fd = -1;
175 const char *subvolume;
176 int r;
177
178 assert(path);
179
180 r = extract_subvolume_name(path, &subvolume);
181 if (r < 0)
182 return r;
183
184 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
185 if (fd < 0)
186 return fd;
187
188 strncpy(args.name, subvolume, sizeof(args.name)-1);
189
190 if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
191 return -errno;
192
193 return 0;
194 }
195
196 int btrfs_subvol_make_label(const char *path) {
197 int r;
198
199 assert(path);
200
201 r = mac_selinux_create_file_prepare(path, S_IFDIR);
202 if (r < 0)
203 return r;
204
205 r = btrfs_subvol_make(path);
206 mac_selinux_create_file_clear();
207
208 if (r < 0)
209 return r;
210
211 return mac_smack_fix(path, false, false);
212 }
213
214 int btrfs_subvol_remove(const char *path) {
215 struct btrfs_ioctl_vol_args args = {};
216 _cleanup_close_ int fd = -1;
217 const char *subvolume;
218 int r;
219
220 assert(path);
221
222 r = extract_subvolume_name(path, &subvolume);
223 if (r < 0)
224 return r;
225
226 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
227 if (fd < 0)
228 return fd;
229
230 strncpy(args.name, subvolume, sizeof(args.name)-1);
231
232 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args) < 0)
233 return -errno;
234
235 return 0;
236 }
237
238 int btrfs_subvol_set_read_only_fd(int fd, bool b) {
239 uint64_t flags, nflags;
240 struct stat st;
241
242 assert(fd >= 0);
243
244 if (fstat(fd, &st) < 0)
245 return -errno;
246
247 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
248 return -EINVAL;
249
250 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
251 return -errno;
252
253 if (b)
254 nflags = flags | BTRFS_SUBVOL_RDONLY;
255 else
256 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
257
258 if (flags == nflags)
259 return 0;
260
261 if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
262 return -errno;
263
264 return 0;
265 }
266
267 int btrfs_subvol_set_read_only(const char *path, bool b) {
268 _cleanup_close_ int fd = -1;
269
270 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
271 if (fd < 0)
272 return -errno;
273
274 return btrfs_subvol_set_read_only_fd(fd, b);
275 }
276
277 int btrfs_subvol_get_read_only_fd(int fd) {
278 uint64_t flags;
279
280 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
281 return -errno;
282
283 return !!(flags & BTRFS_SUBVOL_RDONLY);
284 }
285
286 int btrfs_reflink(int infd, int outfd) {
287 int r;
288
289 assert(infd >= 0);
290 assert(outfd >= 0);
291
292 r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
293 if (r < 0)
294 return -errno;
295
296 return 0;
297 }
298
299 int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
300 struct btrfs_ioctl_clone_range_args args = {
301 .src_fd = infd,
302 .src_offset = in_offset,
303 .src_length = sz,
304 .dest_offset = out_offset,
305 };
306 int r;
307
308 assert(infd >= 0);
309 assert(outfd >= 0);
310 assert(sz > 0);
311
312 r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
313 if (r < 0)
314 return -errno;
315
316 return 0;
317 }
318
319 int btrfs_get_block_device_fd(int fd, dev_t *dev) {
320 struct btrfs_ioctl_fs_info_args fsi = {};
321 uint64_t id;
322
323 assert(fd >= 0);
324 assert(dev);
325
326 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
327 return -errno;
328
329 /* We won't do this for btrfs RAID */
330 if (fsi.num_devices != 1)
331 return 0;
332
333 for (id = 1; id <= fsi.max_id; id++) {
334 struct btrfs_ioctl_dev_info_args di = {
335 .devid = id,
336 };
337 struct stat st;
338
339 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
340 if (errno == ENODEV)
341 continue;
342
343 return -errno;
344 }
345
346 if (stat((char*) di.path, &st) < 0)
347 return -errno;
348
349 if (!S_ISBLK(st.st_mode))
350 return -ENODEV;
351
352 if (major(st.st_rdev) == 0)
353 return -ENODEV;
354
355 *dev = st.st_rdev;
356 return 1;
357 }
358
359 return -ENODEV;
360 }
361
362 int btrfs_get_block_device(const char *path, dev_t *dev) {
363 _cleanup_close_ int fd = -1;
364
365 assert(path);
366 assert(dev);
367
368 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
369 if (fd < 0)
370 return -errno;
371
372 return btrfs_get_block_device_fd(fd, dev);
373 }
374
375 int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
376 struct btrfs_ioctl_ino_lookup_args args = {
377 .objectid = BTRFS_FIRST_FREE_OBJECTID
378 };
379
380 assert(fd >= 0);
381 assert(ret);
382
383 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
384 return -errno;
385
386 *ret = args.treeid;
387 return 0;
388 }
389
390 static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
391 assert(args);
392
393 /* the objectid, type, offset together make up the btrfs key,
394 * which is considered a single 136byte integer when
395 * comparing. This call increases the counter by one, dealing
396 * with the overflow between the overflows */
397
398 if (args->key.min_offset < (uint64_t) -1) {
399 args->key.min_offset++;
400 return true;
401 }
402
403 if (args->key.min_type < (uint8_t) -1) {
404 args->key.min_type++;
405 args->key.min_offset = 0;
406 return true;
407 }
408
409 if (args->key.min_objectid < (uint64_t) -1) {
410 args->key.min_objectid++;
411 args->key.min_offset = 0;
412 args->key.min_type = 0;
413 return true;
414 }
415
416 return 0;
417 }
418
419 static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
420 assert(args);
421 assert(h);
422
423 args->key.min_objectid = h->objectid;
424 args->key.min_type = h->type;
425 args->key.min_offset = h->offset;
426 }
427
428 static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
429 assert(args);
430
431 /* Compare min and max */
432
433 if (args->key.min_objectid < args->key.max_objectid)
434 return -1;
435 if (args->key.min_objectid > args->key.max_objectid)
436 return 1;
437
438 if (args->key.min_type < args->key.max_type)
439 return -1;
440 if (args->key.min_type > args->key.max_type)
441 return 1;
442
443 if (args->key.min_offset < args->key.max_offset)
444 return -1;
445 if (args->key.min_offset > args->key.max_offset)
446 return 1;
447
448 return 0;
449 }
450
451 #define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \
452 for ((i) = 0, \
453 (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
454 (i) < (args).key.nr_items; \
455 (i)++, \
456 (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
457
458 #define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh) \
459 ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
460
461 int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
462 struct btrfs_ioctl_search_args args = {
463 /* Tree of tree roots */
464 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
465
466 /* Look precisely for the subvolume items */
467 .key.min_type = BTRFS_ROOT_ITEM_KEY,
468 .key.max_type = BTRFS_ROOT_ITEM_KEY,
469
470 .key.min_offset = 0,
471 .key.max_offset = (uint64_t) -1,
472
473 /* No restrictions on the other components */
474 .key.min_transid = 0,
475 .key.max_transid = (uint64_t) -1,
476 };
477
478 uint64_t subvol_id;
479 bool found = false;
480 int r;
481
482 assert(fd >= 0);
483 assert(ret);
484
485 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
486 if (r < 0)
487 return r;
488
489 args.key.min_objectid = args.key.max_objectid = subvol_id;
490
491 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
492 const struct btrfs_ioctl_search_header *sh;
493 unsigned i;
494
495 args.key.nr_items = 256;
496 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
497 return -errno;
498
499 if (args.key.nr_items <= 0)
500 break;
501
502 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
503
504 const struct btrfs_root_item *ri;
505
506 /* Make sure we start the next search at least from this entry */
507 btrfs_ioctl_search_args_set(&args, sh);
508
509 if (sh->objectid != subvol_id)
510 continue;
511 if (sh->type != BTRFS_ROOT_ITEM_KEY)
512 continue;
513
514 /* Older versions of the struct lacked the otime setting */
515 if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
516 continue;
517
518 ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
519
520 ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
521 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
522
523 ret->subvol_id = subvol_id;
524 ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
525
526 assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
527 memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
528 memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
529
530 found = true;
531 goto finish;
532 }
533
534 /* Increase search key by one, to read the next item, if we can. */
535 if (!btrfs_ioctl_search_args_inc(&args))
536 break;
537 }
538
539 finish:
540 if (!found)
541 return -ENODATA;
542
543 return 0;
544 }
545
546 int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
547
548 struct btrfs_ioctl_search_args args = {
549 /* Tree of quota items */
550 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
551
552 /* The object ID is always 0 */
553 .key.min_objectid = 0,
554 .key.max_objectid = 0,
555
556 /* Look precisely for the quota items */
557 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
558 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
559
560 /* No restrictions on the other components */
561 .key.min_transid = 0,
562 .key.max_transid = (uint64_t) -1,
563 };
564
565 uint64_t subvol_id;
566 bool found_info = false, found_limit = false;
567 int r;
568
569 assert(fd >= 0);
570 assert(ret);
571
572 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
573 if (r < 0)
574 return r;
575
576 args.key.min_offset = args.key.max_offset = subvol_id;
577
578 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
579 const struct btrfs_ioctl_search_header *sh;
580 unsigned i;
581
582 args.key.nr_items = 256;
583 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
584 return -errno;
585
586 if (args.key.nr_items <= 0)
587 break;
588
589 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
590
591 /* Make sure we start the next search at least from this entry */
592 btrfs_ioctl_search_args_set(&args, sh);
593
594 if (sh->objectid != 0)
595 continue;
596 if (sh->offset != subvol_id)
597 continue;
598
599 if (sh->type == BTRFS_QGROUP_INFO_KEY) {
600 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
601
602 ret->referenced = le64toh(qii->rfer);
603 ret->exclusive = le64toh(qii->excl);
604
605 found_info = true;
606
607 } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
608 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
609
610 ret->referenced_max = le64toh(qli->max_rfer);
611 ret->exclusive_max = le64toh(qli->max_excl);
612
613 if (ret->referenced_max == 0)
614 ret->referenced_max = (uint64_t) -1;
615 if (ret->exclusive_max == 0)
616 ret->exclusive_max = (uint64_t) -1;
617
618 found_limit = true;
619 }
620
621 if (found_info && found_limit)
622 goto finish;
623 }
624
625 /* Increase search key by one, to read the next item, if we can. */
626 if (!btrfs_ioctl_search_args_inc(&args))
627 break;
628 }
629
630 finish:
631 if (!found_limit && !found_info)
632 return -ENODATA;
633
634 if (!found_info) {
635 ret->referenced = (uint64_t) -1;
636 ret->exclusive = (uint64_t) -1;
637 }
638
639 if (!found_limit) {
640 ret->referenced_max = (uint64_t) -1;
641 ret->exclusive_max = (uint64_t) -1;
642 }
643
644 return 0;
645 }
646
647 int btrfs_defrag_fd(int fd) {
648 assert(fd >= 0);
649
650 if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
651 return -errno;
652
653 return 0;
654 }
655
656 int btrfs_defrag(const char *p) {
657 _cleanup_close_ int fd = -1;
658
659 fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
660 if (fd < 0)
661 return -errno;
662
663 return btrfs_defrag_fd(fd);
664 }
665
666 int btrfs_quota_enable_fd(int fd, bool b) {
667 struct btrfs_ioctl_quota_ctl_args args = {
668 .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
669 };
670
671 assert(fd >= 0);
672
673 if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
674 return -errno;
675
676 return 0;
677 }
678
679 int btrfs_quota_enable(const char *path, bool b) {
680 _cleanup_close_ int fd = -1;
681
682 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
683 if (fd < 0)
684 return -errno;
685
686 return btrfs_quota_enable_fd(fd, b);
687 }
688
689 int btrfs_quota_limit_fd(int fd, uint64_t referenced_max) {
690 struct btrfs_ioctl_qgroup_limit_args args = {
691 .lim.max_rfer =
692 referenced_max == (uint64_t) -1 ? 0 :
693 referenced_max == 0 ? 1 : referenced_max,
694 .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
695 };
696
697 assert(fd >= 0);
698
699 if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0)
700 return -errno;
701
702 return 0;
703 }
704
705 int btrfs_quota_limit(const char *path, uint64_t referenced_max) {
706 _cleanup_close_ int fd = -1;
707
708 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
709 if (fd < 0)
710 return -errno;
711
712 return btrfs_quota_limit_fd(fd, referenced_max);
713 }
714
715 int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
716 struct btrfs_ioctl_vol_args args = {};
717 _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
718 _cleanup_close_ int loop_fd = -1, backing_fd = -1;
719 struct stat st;
720 dev_t dev = 0;
721 int r;
722
723 /* btrfs cannot handle file systems < 16M, hence use this as minimum */
724 if (new_size < 16*1024*1024)
725 new_size = 16*1024*1024;
726
727 r = btrfs_get_block_device_fd(fd, &dev);
728 if (r < 0)
729 return r;
730 if (r == 0)
731 return -ENODEV;
732
733 if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
734 return -ENOMEM;
735 r = read_one_line_file(p, &backing);
736 if (r == -ENOENT)
737 return -ENODEV;
738 if (r < 0)
739 return r;
740 if (isempty(backing) || !path_is_absolute(backing))
741 return -ENODEV;
742
743 backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
744 if (backing_fd < 0)
745 return -errno;
746
747 if (fstat(backing_fd, &st) < 0)
748 return -errno;
749 if (!S_ISREG(st.st_mode))
750 return -ENODEV;
751
752 if (new_size == (uint64_t) st.st_size)
753 return 0;
754
755 if (grow_only && new_size < (uint64_t) st.st_size)
756 return -EINVAL;
757
758 if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
759 return -ENOMEM;
760 loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
761 if (loop_fd < 0)
762 return -errno;
763
764 if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
765 return -EINVAL;
766
767 if (new_size < (uint64_t) st.st_size) {
768 /* Decrease size: first decrease btrfs size, then shorten loopback */
769 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
770 return -errno;
771 }
772
773 if (ftruncate(backing_fd, new_size) < 0)
774 return -errno;
775
776 if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
777 return -errno;
778
779 if (new_size > (uint64_t) st.st_size) {
780 /* Increase size: first enlarge loopback, then increase btrfs size */
781 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
782 return -errno;
783 }
784
785 /* Make sure the free disk space is correctly updated for both file systems */
786 (void) fsync(fd);
787 (void) fsync(backing_fd);
788
789 return 1;
790 }
791
792 int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
793 _cleanup_close_ int fd = -1;
794
795 fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
796 if (fd < 0)
797 return -errno;
798
799 return btrfs_resize_loopback_fd(fd, new_size, grow_only);
800 }