]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/btrfs-util.c
fix gcc warnings about uninitialized variables
[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
587fec42 104int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, bool read_only, bool fallback_copy) {
d7c7c334
LP
105 struct btrfs_ioctl_vol_args_v2 args = {
106 .flags = read_only ? BTRFS_SUBVOL_RDONLY : 0,
107 };
587fec42 108 _cleanup_close_ int new_fd = -1;
d7c7c334
LP
109 const char *subvolume;
110 int r;
111
587fec42 112 assert(new_path);
d7c7c334
LP
113
114 r = btrfs_is_snapshot(old_fd);
115 if (r < 0)
116 return r;
117 if (r == 0) {
587fec42
LP
118 if (!fallback_copy)
119 return -EISDIR;
d7c7c334 120
587fec42
LP
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 }
d7c7c334 130
587fec42
LP
131 if (read_only) {
132 r = btrfs_subvol_set_read_only(new_path, true);
d7c7c334
LP
133 if (r < 0) {
134 btrfs_subvol_remove(new_path);
135 return r;
136 }
d7c7c334
LP
137 }
138
587fec42 139 return 0;
d7c7c334
LP
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
587fec42
LP
159int 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
d7c7c334
LP
172int 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
d7b8eec7
LP
196int 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
d7c7c334
LP
214int 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
0d6e763b 238int btrfs_subvol_set_read_only_fd(int fd, bool b) {
d7c7c334 239 uint64_t flags, nflags;
0d6e763b 240 struct stat st;
d7c7c334 241
0d6e763b
LP
242 assert(fd >= 0);
243
244 if (fstat(fd, &st) < 0)
d7c7c334
LP
245 return -errno;
246
0d6e763b
LP
247 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
248 return -EINVAL;
249
d7c7c334
LP
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
0d6e763b
LP
267int 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
10f9c755 277int btrfs_subvol_get_read_only_fd(int fd) {
cd61c3bf
LP
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
d7c7c334
LP
286int 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
1c7dd825
LP
299int 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
efe02862 319int btrfs_get_block_device_fd(int fd, dev_t *dev) {
d7c7c334 320 struct btrfs_ioctl_fs_info_args fsi = {};
d7c7c334
LP
321 uint64_t id;
322
efe02862 323 assert(fd >= 0);
d7c7c334
LP
324 assert(dev);
325
d7c7c334
LP
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}
10f9c755 361
efe02862
LP
362int 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
10f9c755
LP
375int 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
5743a585
LP
390static 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
419static 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
428static 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
10f9c755
LP
461int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
462 struct btrfs_ioctl_search_args args = {
463 /* Tree of tree roots */
b6b18498 464 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
10f9c755
LP
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
10f9c755
LP
470 .key.min_offset = 0,
471 .key.max_offset = (uint64_t) -1,
5743a585
LP
472
473 /* No restrictions on the other components */
10f9c755
LP
474 .key.min_transid = 0,
475 .key.max_transid = (uint64_t) -1,
10f9c755
LP
476 };
477
10f9c755 478 uint64_t subvol_id;
b6b18498 479 bool found = false;
10f9c755
LP
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;
10f9c755 490
5743a585 491 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
b6b18498
LP
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;
10f9c755 501
5743a585 502 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
10f9c755 503
b6b18498
LP
504 const struct btrfs_root_item *ri;
505
5743a585
LP
506 /* Make sure we start the next search at least from this entry */
507 btrfs_ioctl_search_args_set(&args, sh);
508
b6b18498
LP
509 if (sh->objectid != subvol_id)
510 continue;
511 if (sh->type != BTRFS_ROOT_ITEM_KEY)
512 continue;
5743a585
LP
513
514 /* Older versions of the struct lacked the otime setting */
b6b18498
LP
515 if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
516 continue;
10f9c755 517
5743a585 518 ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
10f9c755 519
b6b18498
LP
520 ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
521 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
10f9c755 522
b6b18498
LP
523 ret->subvol_id = subvol_id;
524 ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
10f9c755 525
b6b18498
LP
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
5743a585
LP
534 /* Increase search key by one, to read the next item, if we can. */
535 if (!btrfs_ioctl_search_args_inc(&args))
b6b18498
LP
536 break;
537 }
538
539finish:
540 if (!found)
541 return -ENODATA;
542
543 return 0;
544}
545
546int 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
5743a585
LP
552 /* The object ID is always 0 */
553 .key.min_objectid = 0,
554 .key.max_objectid = 0,
555
b6b18498
LP
556 /* Look precisely for the quota items */
557 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
558 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
559
b6b18498
LP
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
5743a585 578 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
b6b18498
LP
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
5743a585 589 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
b6b18498 590
5743a585
LP
591 /* Make sure we start the next search at least from this entry */
592 btrfs_ioctl_search_args_set(&args, sh);
b6b18498
LP
593
594 if (sh->objectid != 0)
595 continue;
596 if (sh->offset != subvol_id)
597 continue;
598
b6b18498 599 if (sh->type == BTRFS_QGROUP_INFO_KEY) {
5743a585 600 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
b6b18498 601
cb81cd80 602 ret->referenced = le64toh(qii->rfer);
b6b18498
LP
603 ret->exclusive = le64toh(qii->excl);
604
605 found_info = true;
606
607 } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
5743a585 608 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
b6b18498 609
cb81cd80 610 ret->referenced_max = le64toh(qli->max_rfer);
b6b18498
LP
611 ret->exclusive_max = le64toh(qli->max_excl);
612
cb81cd80
LP
613 if (ret->referenced_max == 0)
614 ret->referenced_max = (uint64_t) -1;
b6b18498
LP
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
5743a585
LP
625 /* Increase search key by one, to read the next item, if we can. */
626 if (!btrfs_ioctl_search_args_inc(&args))
b6b18498
LP
627 break;
628 }
629
630finish:
631 if (!found_limit && !found_info)
632 return -ENODATA;
633
634 if (!found_info) {
cb81cd80 635 ret->referenced = (uint64_t) -1;
b6b18498
LP
636 ret->exclusive = (uint64_t) -1;
637 }
638
639 if (!found_limit) {
cb81cd80 640 ret->referenced_max = (uint64_t) -1;
b6b18498
LP
641 ret->exclusive_max = (uint64_t) -1;
642 }
10f9c755
LP
643
644 return 0;
645}
f27a3864
LP
646
647int 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
656int 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}
754061ce
LP
665
666int 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
679int 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}
d6ce17c7 688
cb81cd80 689int btrfs_quota_limit_fd(int fd, uint64_t referenced_max) {
d6ce17c7
LP
690 struct btrfs_ioctl_qgroup_limit_args args = {
691 .lim.max_rfer =
cb81cd80
LP
692 referenced_max == (uint64_t) -1 ? 0 :
693 referenced_max == 0 ? 1 : referenced_max,
d6ce17c7
LP
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
cb81cd80 705int btrfs_quota_limit(const char *path, uint64_t referenced_max) {
d6ce17c7
LP
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
cb81cd80 712 return btrfs_quota_limit_fd(fd, referenced_max);
d6ce17c7 713}
efe02862 714
26166c88 715int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
efe02862
LP
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;
a7f7d1bd 720 dev_t dev = 0;
efe02862
LP
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);
26166c88
LP
736 if (r == -ENOENT)
737 return -ENODEV;
efe02862
LP
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
26166c88
LP
755 if (grow_only && new_size < (uint64_t) st.st_size)
756 return -EINVAL;
757
efe02862
LP
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
26166c88
LP
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;
efe02862
LP
790}
791
26166c88 792int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
efe02862
LP
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
26166c88 799 return btrfs_resize_loopback_fd(fd, new_size, grow_only);
efe02862 800}