]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/btrfs-util.c
man: fix typo
[thirdparty/systemd.git] / src / shared / btrfs-util.c
CommitLineData
d7c7c334
LP
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"
d7c7c334 34#include "copy.h"
d7b8eec7
LP
35#include "selinux-util.h"
36#include "smack-util.h"
efe02862 37#include "fileio.h"
10f9c755 38#include "btrfs-ctree.h"
d7c7c334
LP
39#include "btrfs-util.h"
40
41static 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
52static 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
69static 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
86int btrfs_is_snapshot(int fd) {
87 struct stat st;
88 struct statfs sfs;
89
cd61c3bf
LP
90 /* On btrfs subvolumes always have the inode 256 */
91
92 if (fstat(fd, &st) < 0)
d7c7c334
LP
93 return -errno;
94
cd61c3bf 95 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
d7c7c334
LP
96 return 0;
97
cd61c3bf 98 if (fstatfs(fd, &sfs) < 0)
d7c7c334
LP
99 return -errno;
100
cd61c3bf 101 return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
d7c7c334
LP
102}
103
104int 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
f2cbe59e 128 r = copy_directory_fd(old_fd, new_path, true);
d7c7c334
LP
129 if (r < 0) {
130 btrfs_subvol_remove(new_path);
131 return r;
132 }
133
134 if (read_only) {
10f9c755 135 r = btrfs_subvol_set_read_only(new_path, true);
d7c7c334
LP
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
165int 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
d7b8eec7
LP
189int 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
d7c7c334
LP
207int 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
0d6e763b 231int btrfs_subvol_set_read_only_fd(int fd, bool b) {
d7c7c334 232 uint64_t flags, nflags;
0d6e763b 233 struct stat st;
d7c7c334 234
0d6e763b
LP
235 assert(fd >= 0);
236
237 if (fstat(fd, &st) < 0)
d7c7c334
LP
238 return -errno;
239
0d6e763b
LP
240 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
241 return -EINVAL;
242
d7c7c334
LP
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
0d6e763b
LP
260int 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
10f9c755 270int btrfs_subvol_get_read_only_fd(int fd) {
cd61c3bf
LP
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
d7c7c334
LP
279int 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
1c7dd825
LP
292int 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
efe02862 312int btrfs_get_block_device_fd(int fd, dev_t *dev) {
d7c7c334 313 struct btrfs_ioctl_fs_info_args fsi = {};
d7c7c334
LP
314 uint64_t id;
315
efe02862 316 assert(fd >= 0);
d7c7c334
LP
317 assert(dev);
318
d7c7c334
LP
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}
10f9c755 354
efe02862
LP
355int 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
10f9c755
LP
368int 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
5743a585
LP
383static 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
412static 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
421static 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
10f9c755
LP
454int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
455 struct btrfs_ioctl_search_args args = {
456 /* Tree of tree roots */
b6b18498 457 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
10f9c755
LP
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
10f9c755
LP
463 .key.min_offset = 0,
464 .key.max_offset = (uint64_t) -1,
5743a585
LP
465
466 /* No restrictions on the other components */
10f9c755
LP
467 .key.min_transid = 0,
468 .key.max_transid = (uint64_t) -1,
10f9c755
LP
469 };
470
10f9c755 471 uint64_t subvol_id;
b6b18498 472 bool found = false;
10f9c755
LP
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;
10f9c755 483
5743a585 484 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
b6b18498
LP
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;
10f9c755 494
5743a585 495 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
10f9c755 496
b6b18498
LP
497 const struct btrfs_root_item *ri;
498
5743a585
LP
499 /* Make sure we start the next search at least from this entry */
500 btrfs_ioctl_search_args_set(&args, sh);
501
b6b18498
LP
502 if (sh->objectid != subvol_id)
503 continue;
504 if (sh->type != BTRFS_ROOT_ITEM_KEY)
505 continue;
5743a585
LP
506
507 /* Older versions of the struct lacked the otime setting */
b6b18498
LP
508 if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
509 continue;
10f9c755 510
5743a585 511 ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
10f9c755 512
b6b18498
LP
513 ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
514 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
10f9c755 515
b6b18498
LP
516 ret->subvol_id = subvol_id;
517 ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
10f9c755 518
b6b18498
LP
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
5743a585
LP
527 /* Increase search key by one, to read the next item, if we can. */
528 if (!btrfs_ioctl_search_args_inc(&args))
b6b18498
LP
529 break;
530 }
531
532finish:
533 if (!found)
534 return -ENODATA;
535
536 return 0;
537}
538
539int 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
5743a585
LP
545 /* The object ID is always 0 */
546 .key.min_objectid = 0,
547 .key.max_objectid = 0,
548
b6b18498
LP
549 /* Look precisely for the quota items */
550 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
551 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
552
b6b18498
LP
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
5743a585 571 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
b6b18498
LP
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
5743a585 582 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
b6b18498 583
5743a585
LP
584 /* Make sure we start the next search at least from this entry */
585 btrfs_ioctl_search_args_set(&args, sh);
b6b18498
LP
586
587 if (sh->objectid != 0)
588 continue;
589 if (sh->offset != subvol_id)
590 continue;
591
b6b18498 592 if (sh->type == BTRFS_QGROUP_INFO_KEY) {
5743a585 593 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
b6b18498
LP
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) {
5743a585 601 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
b6b18498
LP
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
5743a585
LP
618 /* Increase search key by one, to read the next item, if we can. */
619 if (!btrfs_ioctl_search_args_inc(&args))
b6b18498
LP
620 break;
621 }
622
623finish:
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 }
10f9c755
LP
636
637 return 0;
638}
f27a3864
LP
639
640int 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
649int 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}
754061ce
LP
658
659int 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
672int 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}
d6ce17c7
LP
681
682int 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
698int 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}
efe02862
LP
707
708int btrfs_resize_loopback_fd(int fd, uint64_t new_size) {
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 < 0)
730 return r;
731 if (isempty(backing) || !path_is_absolute(backing))
732 return -ENODEV;
733
734 backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
735 if (backing_fd < 0)
736 return -errno;
737
738 if (fstat(backing_fd, &st) < 0)
739 return -errno;
740 if (!S_ISREG(st.st_mode))
741 return -ENODEV;
742
743 if (new_size == (uint64_t) st.st_size)
744 return 0;
745
746 if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
747 return -ENOMEM;
748 loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
749 if (loop_fd < 0)
750 return -errno;
751
752 if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
753 return -EINVAL;
754
755 if (new_size < (uint64_t) st.st_size) {
756 /* Decrease size: first decrease btrfs size, then shorten loopback */
757 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
758 return -errno;
759 }
760
761 if (ftruncate(backing_fd, new_size) < 0)
762 return -errno;
763
764 if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
765 return -errno;
766
767 if (new_size > (uint64_t) st.st_size) {
768 /* Increase size: first enlarge loopback, then increase btrfs size */
769 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
770 return -errno;
771 }
772
773 return 0;
774}
775
776int btrfs_resize_loopback(const char *p, uint64_t new_size) {
777 _cleanup_close_ int fd = -1;
778
779 fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
780 if (fd < 0)
781 return -errno;
782
783 return btrfs_resize_loopback_fd(fd, new_size);
784}