chiark / gitweb /
shared: the btrfs quota field is called "referenced" not "referred"
[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_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;
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 }