chiark / gitweb /
256c5a6995ddf6f1796989ee771891464d2140d1
[elogind.git] / src / shared / btrfs-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdlib.h>
23 #include <sys/vfs.h>
24 #include <sys/stat.h>
25
26 #ifdef HAVE_LINUX_BTRFS_H
27 #include <linux/btrfs.h>
28 #endif
29
30 #include "missing.h"
31 #include "util.h"
32 #include "path-util.h"
33 #include "macro.h"
34 #include "copy.h"
35 #include "selinux-util.h"
36 #include "smack-util.h"
37 #include "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(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_fd(int fd, dev_t *dev) {
313         struct btrfs_ioctl_fs_info_args fsi = {};
314         uint64_t id;
315
316         assert(fd >= 0);
317         assert(dev);
318
319         if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
320                 return -errno;
321
322         /* We won't do this for btrfs RAID */
323         if (fsi.num_devices != 1)
324                 return 0;
325
326         for (id = 1; id <= fsi.max_id; id++) {
327                 struct btrfs_ioctl_dev_info_args di = {
328                         .devid = id,
329                 };
330                 struct stat st;
331
332                 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
333                         if (errno == ENODEV)
334                                 continue;
335
336                         return -errno;
337                 }
338
339                 if (stat((char*) di.path, &st) < 0)
340                         return -errno;
341
342                 if (!S_ISBLK(st.st_mode))
343                         return -ENODEV;
344
345                 if (major(st.st_rdev) == 0)
346                         return -ENODEV;
347
348                 *dev = st.st_rdev;
349                 return 1;
350         }
351
352         return -ENODEV;
353 }
354
355 int btrfs_get_block_device(const char *path, dev_t *dev) {
356         _cleanup_close_ int fd = -1;
357
358         assert(path);
359         assert(dev);
360
361         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
362         if (fd < 0)
363                 return -errno;
364
365         return btrfs_get_block_device_fd(fd, dev);
366 }
367
368 int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
369         struct btrfs_ioctl_ino_lookup_args args = {
370                 .objectid = BTRFS_FIRST_FREE_OBJECTID
371         };
372
373         assert(fd >= 0);
374         assert(ret);
375
376         if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
377                 return -errno;
378
379         *ret = args.treeid;
380         return 0;
381 }
382
383 static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
384         assert(args);
385
386         /* the objectid, type, offset together make up the btrfs key,
387          * which is considered a single 136byte integer when
388          * comparing. This call increases the counter by one, dealing
389          * with the overflow between the overflows */
390
391         if (args->key.min_offset < (uint64_t) -1) {
392                 args->key.min_offset++;
393                 return true;
394         }
395
396         if (args->key.min_type < (uint8_t) -1) {
397                 args->key.min_type++;
398                 args->key.min_offset = 0;
399                 return true;
400         }
401
402         if (args->key.min_objectid < (uint64_t) -1) {
403                 args->key.min_objectid++;
404                 args->key.min_offset = 0;
405                 args->key.min_type = 0;
406                 return true;
407         }
408
409         return 0;
410 }
411
412 static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
413         assert(args);
414         assert(h);
415
416         args->key.min_objectid = h->objectid;
417         args->key.min_type = h->type;
418         args->key.min_offset = h->offset;
419 }
420
421 static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
422         assert(args);
423
424         /* Compare min and max */
425
426         if (args->key.min_objectid < args->key.max_objectid)
427                 return -1;
428         if (args->key.min_objectid > args->key.max_objectid)
429                 return 1;
430
431         if (args->key.min_type < args->key.max_type)
432                 return -1;
433         if (args->key.min_type > args->key.max_type)
434                 return 1;
435
436         if (args->key.min_offset < args->key.max_offset)
437                 return -1;
438         if (args->key.min_offset > args->key.max_offset)
439                 return 1;
440
441         return 0;
442 }
443
444 #define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args)                  \
445         for ((i) = 0,                                                   \
446              (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
447              (i) < (args).key.nr_items;                                 \
448              (i)++,                                                     \
449              (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
450
451 #define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh)                              \
452         ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
453
454 int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
455         struct btrfs_ioctl_search_args args = {
456                 /* Tree of tree roots */
457                 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
458
459                 /* Look precisely for the subvolume items */
460                 .key.min_type = BTRFS_ROOT_ITEM_KEY,
461                 .key.max_type = BTRFS_ROOT_ITEM_KEY,
462
463                 .key.min_offset = 0,
464                 .key.max_offset = (uint64_t) -1,
465
466                 /* No restrictions on the other components */
467                 .key.min_transid = 0,
468                 .key.max_transid = (uint64_t) -1,
469         };
470
471         uint64_t subvol_id;
472         bool found = false;
473         int r;
474
475         assert(fd >= 0);
476         assert(ret);
477
478         r = btrfs_subvol_get_id_fd(fd, &subvol_id);
479         if (r < 0)
480                 return r;
481
482         args.key.min_objectid = args.key.max_objectid = subvol_id;
483
484         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
485                 const struct btrfs_ioctl_search_header *sh;
486                 unsigned i;
487
488                 args.key.nr_items = 256;
489                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
490                         return -errno;
491
492                 if (args.key.nr_items <= 0)
493                         break;
494
495                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
496
497                         const struct btrfs_root_item *ri;
498
499                         /* Make sure we start the next search at least from this entry */
500                         btrfs_ioctl_search_args_set(&args, sh);
501
502                         if (sh->objectid != subvol_id)
503                                 continue;
504                         if (sh->type != BTRFS_ROOT_ITEM_KEY)
505                                 continue;
506
507                         /* Older versions of the struct lacked the otime setting */
508                         if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
509                                 continue;
510
511                         ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
512
513                         ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
514                                 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
515
516                         ret->subvol_id = subvol_id;
517                         ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
518
519                         assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
520                         memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
521                         memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
522
523                         found = true;
524                         goto finish;
525                 }
526
527                 /* Increase search key by one, to read the next item, if we can. */
528                 if (!btrfs_ioctl_search_args_inc(&args))
529                         break;
530         }
531
532 finish:
533         if (!found)
534                 return -ENODATA;
535
536         return 0;
537 }
538
539 int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
540
541         struct btrfs_ioctl_search_args args = {
542                 /* Tree of quota items */
543                 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
544
545                 /* The object ID is always 0 */
546                 .key.min_objectid = 0,
547                 .key.max_objectid = 0,
548
549                 /* Look precisely for the quota items */
550                 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
551                 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
552
553                 /* No restrictions on the other components */
554                 .key.min_transid = 0,
555                 .key.max_transid = (uint64_t) -1,
556         };
557
558         uint64_t subvol_id;
559         bool found_info = false, found_limit = false;
560         int r;
561
562         assert(fd >= 0);
563         assert(ret);
564
565         r = btrfs_subvol_get_id_fd(fd, &subvol_id);
566         if (r < 0)
567                 return r;
568
569         args.key.min_offset = args.key.max_offset = subvol_id;
570
571         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
572                 const struct btrfs_ioctl_search_header *sh;
573                 unsigned i;
574
575                 args.key.nr_items = 256;
576                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
577                         return -errno;
578
579                 if (args.key.nr_items <= 0)
580                         break;
581
582                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
583
584                         /* Make sure we start the next search at least from this entry */
585                         btrfs_ioctl_search_args_set(&args, sh);
586
587                         if (sh->objectid != 0)
588                                 continue;
589                         if (sh->offset != subvol_id)
590                                 continue;
591
592                         if (sh->type == BTRFS_QGROUP_INFO_KEY) {
593                                 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
594
595                                 ret->referred = le64toh(qii->rfer);
596                                 ret->exclusive = le64toh(qii->excl);
597
598                                 found_info = true;
599
600                         } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
601                                 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
602
603                                 ret->referred_max = le64toh(qli->max_rfer);
604                                 ret->exclusive_max = le64toh(qli->max_excl);
605
606                                 if (ret->referred_max == 0)
607                                         ret->referred_max = (uint64_t) -1;
608                                 if (ret->exclusive_max == 0)
609                                         ret->exclusive_max = (uint64_t) -1;
610
611                                 found_limit = true;
612                         }
613
614                         if (found_info && found_limit)
615                                 goto finish;
616                 }
617
618                 /* Increase search key by one, to read the next item, if we can. */
619                 if (!btrfs_ioctl_search_args_inc(&args))
620                         break;
621         }
622
623 finish:
624         if (!found_limit && !found_info)
625                 return -ENODATA;
626
627         if (!found_info) {
628                 ret->referred = (uint64_t) -1;
629                 ret->exclusive = (uint64_t) -1;
630         }
631
632         if (!found_limit) {
633                 ret->referred_max = (uint64_t) -1;
634                 ret->exclusive_max = (uint64_t) -1;
635         }
636
637         return 0;
638 }
639
640 int btrfs_defrag_fd(int fd) {
641         assert(fd >= 0);
642
643         if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
644                 return -errno;
645
646         return 0;
647 }
648
649 int btrfs_defrag(const char *p) {
650         _cleanup_close_ int fd = -1;
651
652         fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
653         if (fd < 0)
654                 return -errno;
655
656         return btrfs_defrag_fd(fd);
657 }
658
659 int btrfs_quota_enable_fd(int fd, bool b) {
660         struct btrfs_ioctl_quota_ctl_args args = {
661                 .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
662         };
663
664         assert(fd >= 0);
665
666         if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
667                 return -errno;
668
669         return 0;
670 }
671
672 int btrfs_quota_enable(const char *path, bool b) {
673         _cleanup_close_ int fd = -1;
674
675         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
676         if (fd < 0)
677                 return -errno;
678
679         return btrfs_quota_enable_fd(fd, b);
680 }
681
682 int btrfs_quota_limit_fd(int fd, uint64_t referred_max) {
683         struct btrfs_ioctl_qgroup_limit_args args = {
684                 .lim.max_rfer =
685                         referred_max == (uint64_t) -1 ? 0 :
686                         referred_max == 0 ? 1 : referred_max,
687                 .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
688         };
689
690         assert(fd >= 0);
691
692         if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0)
693                 return -errno;
694
695         return 0;
696 }
697
698 int btrfs_quota_limit(const char *path, uint64_t referred_max) {
699         _cleanup_close_ int fd = -1;
700
701         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
702         if (fd < 0)
703                 return -errno;
704
705         return btrfs_quota_limit_fd(fd, referred_max);
706 }
707
708 int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
709         struct btrfs_ioctl_vol_args args = {};
710         _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
711         _cleanup_close_ int loop_fd = -1, backing_fd = -1;
712         struct stat st;
713         dev_t dev;
714         int r;
715
716         /* btrfs cannot handle file systems < 16M, hence use this as minimum */
717         if (new_size < 16*1024*1024)
718                 new_size = 16*1024*1024;
719
720         r = btrfs_get_block_device_fd(fd, &dev);
721         if (r < 0)
722                 return r;
723         if (r == 0)
724                 return -ENODEV;
725
726         if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
727                 return -ENOMEM;
728         r = read_one_line_file(p, &backing);
729         if (r == -ENOENT)
730                 return -ENODEV;
731         if (r < 0)
732                 return r;
733         if (isempty(backing) || !path_is_absolute(backing))
734                 return -ENODEV;
735
736         backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
737         if (backing_fd < 0)
738                 return -errno;
739
740         if (fstat(backing_fd, &st) < 0)
741                 return -errno;
742         if (!S_ISREG(st.st_mode))
743                 return -ENODEV;
744
745         if (new_size == (uint64_t) st.st_size)
746                 return 0;
747
748         if (grow_only && new_size < (uint64_t) st.st_size)
749                 return -EINVAL;
750
751         if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
752                 return -ENOMEM;
753         loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
754         if (loop_fd < 0)
755                 return -errno;
756
757         if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
758                 return -EINVAL;
759
760         if (new_size < (uint64_t) st.st_size) {
761                 /* Decrease size: first decrease btrfs size, then shorten loopback */
762                 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
763                         return -errno;
764         }
765
766         if (ftruncate(backing_fd, new_size) < 0)
767                 return -errno;
768
769         if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
770                 return -errno;
771
772         if (new_size > (uint64_t) st.st_size) {
773                 /* Increase size: first enlarge loopback, then increase btrfs size */
774                 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
775                         return -errno;
776         }
777
778         /* Make sure the free disk space is correctly updated for both file systems */
779         (void) fsync(fd);
780         (void) fsync(backing_fd);
781
782         return 1;
783 }
784
785 int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
786         _cleanup_close_ int fd = -1;
787
788         fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
789         if (fd < 0)
790                 return -errno;
791
792         return btrfs_resize_loopback_fd(fd, new_size, grow_only);
793 }