LCOV - code coverage report
Current view: top level - basic - btrfs-util.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 26 973 2.7 %
Date: 2019-08-22 15:41:25 Functions: 4 58 6.9 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <errno.h>
       4             : #include <fcntl.h>
       5             : #include <inttypes.h>
       6             : #include <linux/btrfs_tree.h>
       7             : #include <linux/fs.h>
       8             : #include <linux/loop.h>
       9             : #include <linux/magic.h>
      10             : #include <stddef.h>
      11             : #include <stdio.h>
      12             : #include <stdlib.h>
      13             : #include <string.h>
      14             : #include <sys/ioctl.h>
      15             : #include <sys/stat.h>
      16             : #include <sys/statfs.h>
      17             : #include <sys/sysmacros.h>
      18             : #include <unistd.h>
      19             : 
      20             : #include "alloc-util.h"
      21             : #include "blockdev-util.h"
      22             : #include "btrfs-util.h"
      23             : #include "chattr-util.h"
      24             : #include "copy.h"
      25             : #include "device-nodes.h"
      26             : #include "fd-util.h"
      27             : #include "fileio.h"
      28             : #include "fs-util.h"
      29             : #include "io-util.h"
      30             : #include "macro.h"
      31             : #include "path-util.h"
      32             : #include "rm-rf.h"
      33             : #include "smack-util.h"
      34             : #include "sparse-endian.h"
      35             : #include "stat-util.h"
      36             : #include "string-util.h"
      37             : #include "time-util.h"
      38             : #include "util.h"
      39             : 
      40             : /* WARNING: Be careful with file system ioctls! When we get an fd, we
      41             :  * need to make sure it either refers to only a regular file or
      42             :  * directory, or that it is located on btrfs, before invoking any
      43             :  * btrfs ioctls. The ioctl numbers are reused by some device drivers
      44             :  * (such as DRM), and hence might have bad effects when invoked on
      45             :  * device nodes (that reference drivers) rather than fds to normal
      46             :  * files or directories. */
      47             : 
      48           0 : static int validate_subvolume_name(const char *name) {
      49             : 
      50           0 :         if (!filename_is_valid(name))
      51           0 :                 return -EINVAL;
      52             : 
      53           0 :         if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
      54           0 :                 return -E2BIG;
      55             : 
      56           0 :         return 0;
      57             : }
      58             : 
      59           0 : static int extract_subvolume_name(const char *path, const char **subvolume) {
      60             :         const char *fn;
      61             :         int r;
      62             : 
      63           0 :         assert(path);
      64           0 :         assert(subvolume);
      65             : 
      66           0 :         fn = basename(path);
      67             : 
      68           0 :         r = validate_subvolume_name(fn);
      69           0 :         if (r < 0)
      70           0 :                 return r;
      71             : 
      72           0 :         *subvolume = fn;
      73           0 :         return 0;
      74             : }
      75             : 
      76          25 : int btrfs_is_filesystem(int fd) {
      77             :         struct statfs sfs;
      78             : 
      79          25 :         assert(fd >= 0);
      80             : 
      81          25 :         if (fstatfs(fd, &sfs) < 0)
      82           0 :                 return -errno;
      83             : 
      84          25 :         return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
      85             : }
      86             : 
      87           0 : int btrfs_is_subvol_fd(int fd) {
      88             :         struct stat st;
      89             : 
      90           0 :         assert(fd >= 0);
      91             : 
      92             :         /* On btrfs subvolumes always have the inode 256 */
      93             : 
      94           0 :         if (fstat(fd, &st) < 0)
      95           0 :                 return -errno;
      96             : 
      97           0 :         if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
      98           0 :                 return 0;
      99             : 
     100           0 :         return btrfs_is_filesystem(fd);
     101             : }
     102             : 
     103           0 : int btrfs_is_subvol(const char *path) {
     104           0 :         _cleanup_close_ int fd = -1;
     105             : 
     106           0 :         assert(path);
     107             : 
     108           0 :         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
     109           0 :         if (fd < 0)
     110           0 :                 return -errno;
     111             : 
     112           0 :         return btrfs_is_subvol_fd(fd);
     113             : }
     114             : 
     115           0 : int btrfs_subvol_make_fd(int fd, const char *subvolume) {
     116           0 :         struct btrfs_ioctl_vol_args args = {};
     117           0 :         _cleanup_close_ int real_fd = -1;
     118             :         int r;
     119             : 
     120           0 :         assert(subvolume);
     121             : 
     122           0 :         r = validate_subvolume_name(subvolume);
     123           0 :         if (r < 0)
     124           0 :                 return r;
     125             : 
     126           0 :         r = fcntl(fd, F_GETFL);
     127           0 :         if (r < 0)
     128           0 :                 return -errno;
     129           0 :         if (FLAGS_SET(r, O_PATH)) {
     130             :                 /* An O_PATH fd was specified, let's convert here to a proper one, as btrfs ioctl's can't deal with
     131             :                  * O_PATH. */
     132             : 
     133           0 :                 real_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
     134           0 :                 if (real_fd < 0)
     135           0 :                         return real_fd;
     136             : 
     137           0 :                 fd = real_fd;
     138             :         }
     139             : 
     140           0 :         strncpy(args.name, subvolume, sizeof(args.name)-1);
     141             : 
     142           0 :         if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
     143           0 :                 return -errno;
     144             : 
     145           0 :         return 0;
     146             : }
     147             : 
     148           0 : int btrfs_subvol_make(const char *path) {
     149           0 :         _cleanup_close_ int fd = -1;
     150             :         const char *subvolume;
     151             :         int r;
     152             : 
     153           0 :         assert(path);
     154             : 
     155           0 :         r = extract_subvolume_name(path, &subvolume);
     156           0 :         if (r < 0)
     157           0 :                 return r;
     158             : 
     159           0 :         fd = open_parent(path, O_CLOEXEC, 0);
     160           0 :         if (fd < 0)
     161           0 :                 return fd;
     162             : 
     163           0 :         return btrfs_subvol_make_fd(fd, subvolume);
     164             : }
     165             : 
     166           0 : int btrfs_subvol_set_read_only_fd(int fd, bool b) {
     167             :         uint64_t flags, nflags;
     168             :         struct stat st;
     169             : 
     170           0 :         assert(fd >= 0);
     171             : 
     172           0 :         if (fstat(fd, &st) < 0)
     173           0 :                 return -errno;
     174             : 
     175           0 :         if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
     176           0 :                 return -EINVAL;
     177             : 
     178           0 :         if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
     179           0 :                 return -errno;
     180             : 
     181           0 :         if (b)
     182           0 :                 nflags = flags | BTRFS_SUBVOL_RDONLY;
     183             :         else
     184           0 :                 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
     185             : 
     186           0 :         if (flags == nflags)
     187           0 :                 return 0;
     188             : 
     189           0 :         if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
     190           0 :                 return -errno;
     191             : 
     192           0 :         return 0;
     193             : }
     194             : 
     195           0 : int btrfs_subvol_set_read_only(const char *path, bool b) {
     196           0 :         _cleanup_close_ int fd = -1;
     197             : 
     198           0 :         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
     199           0 :         if (fd < 0)
     200           0 :                 return -errno;
     201             : 
     202           0 :         return btrfs_subvol_set_read_only_fd(fd, b);
     203             : }
     204             : 
     205           0 : int btrfs_subvol_get_read_only_fd(int fd) {
     206             :         uint64_t flags;
     207             :         struct stat st;
     208             : 
     209           0 :         assert(fd >= 0);
     210             : 
     211           0 :         if (fstat(fd, &st) < 0)
     212           0 :                 return -errno;
     213             : 
     214           0 :         if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
     215           0 :                 return -EINVAL;
     216             : 
     217           0 :         if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
     218           0 :                 return -errno;
     219             : 
     220           0 :         return !!(flags & BTRFS_SUBVOL_RDONLY);
     221             : }
     222             : 
     223          11 : int btrfs_reflink(int infd, int outfd) {
     224             :         int r;
     225             : 
     226          11 :         assert(infd >= 0);
     227          11 :         assert(outfd >= 0);
     228             : 
     229             :         /* Make sure we invoke the ioctl on a regular file, so that no device driver accidentally gets it. */
     230             : 
     231          11 :         r = fd_verify_regular(outfd);
     232          11 :         if (r < 0)
     233           0 :                 return r;
     234             : 
     235          11 :         if (ioctl(outfd, BTRFS_IOC_CLONE, infd) < 0)
     236          11 :                 return -errno;
     237             : 
     238           0 :         return 0;
     239             : }
     240             : 
     241           4 : int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
     242           4 :         struct btrfs_ioctl_clone_range_args args = {
     243             :                 .src_fd = infd,
     244             :                 .src_offset = in_offset,
     245             :                 .src_length = sz,
     246             :                 .dest_offset = out_offset,
     247             :         };
     248             :         int r;
     249             : 
     250           4 :         assert(infd >= 0);
     251           4 :         assert(outfd >= 0);
     252           4 :         assert(sz > 0);
     253             : 
     254           4 :         r = fd_verify_regular(outfd);
     255           4 :         if (r < 0)
     256           0 :                 return r;
     257             : 
     258           4 :         if (ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args) < 0)
     259           4 :                 return -errno;
     260             : 
     261           0 :         return 0;
     262             : }
     263             : 
     264           0 : int btrfs_get_block_device_fd(int fd, dev_t *dev) {
     265           0 :         struct btrfs_ioctl_fs_info_args fsi = {};
     266             :         uint64_t id;
     267             :         int r;
     268             : 
     269           0 :         assert(fd >= 0);
     270           0 :         assert(dev);
     271             : 
     272           0 :         r = btrfs_is_filesystem(fd);
     273           0 :         if (r < 0)
     274           0 :                 return r;
     275           0 :         if (!r)
     276           0 :                 return -ENOTTY;
     277             : 
     278           0 :         if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
     279           0 :                 return -errno;
     280             : 
     281             :         /* We won't do this for btrfs RAID */
     282           0 :         if (fsi.num_devices != 1) {
     283           0 :                 *dev = 0;
     284           0 :                 return 0;
     285             :         }
     286             : 
     287           0 :         for (id = 1; id <= fsi.max_id; id++) {
     288           0 :                 struct btrfs_ioctl_dev_info_args di = {
     289             :                         .devid = id,
     290             :                 };
     291             :                 struct stat st;
     292             : 
     293           0 :                 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
     294           0 :                         if (errno == ENODEV)
     295           0 :                                 continue;
     296             : 
     297           0 :                         return -errno;
     298             :                 }
     299             : 
     300           0 :                 if (stat((char*) di.path, &st) < 0)
     301           0 :                         return -errno;
     302             : 
     303           0 :                 if (!S_ISBLK(st.st_mode))
     304           0 :                         return -ENODEV;
     305             : 
     306           0 :                 if (major(st.st_rdev) == 0)
     307           0 :                         return -ENODEV;
     308             : 
     309           0 :                 *dev = st.st_rdev;
     310           0 :                 return 1;
     311             :         }
     312             : 
     313           0 :         return -ENODEV;
     314             : }
     315             : 
     316           0 : int btrfs_get_block_device(const char *path, dev_t *dev) {
     317           0 :         _cleanup_close_ int fd = -1;
     318             : 
     319           0 :         assert(path);
     320           0 :         assert(dev);
     321             : 
     322           0 :         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
     323           0 :         if (fd < 0)
     324           0 :                 return -errno;
     325             : 
     326           0 :         return btrfs_get_block_device_fd(fd, dev);
     327             : }
     328             : 
     329           0 : int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
     330           0 :         struct btrfs_ioctl_ino_lookup_args args = {
     331             :                 .objectid = BTRFS_FIRST_FREE_OBJECTID
     332             :         };
     333             :         int r;
     334             : 
     335           0 :         assert(fd >= 0);
     336           0 :         assert(ret);
     337             : 
     338           0 :         r = btrfs_is_filesystem(fd);
     339           0 :         if (r < 0)
     340           0 :                 return r;
     341           0 :         if (!r)
     342           0 :                 return -ENOTTY;
     343             : 
     344           0 :         if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
     345           0 :                 return -errno;
     346             : 
     347           0 :         *ret = args.treeid;
     348           0 :         return 0;
     349             : }
     350             : 
     351           0 : int btrfs_subvol_get_id(int fd, const char *subvol, uint64_t *ret) {
     352           0 :         _cleanup_close_ int subvol_fd = -1;
     353             : 
     354           0 :         assert(fd >= 0);
     355           0 :         assert(ret);
     356             : 
     357           0 :         subvol_fd = openat(fd, subvol, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
     358           0 :         if (subvol_fd < 0)
     359           0 :                 return -errno;
     360             : 
     361           0 :         return btrfs_subvol_get_id_fd(subvol_fd, ret);
     362             : }
     363             : 
     364           0 : static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
     365           0 :         assert(args);
     366             : 
     367             :         /* the objectid, type, offset together make up the btrfs key,
     368             :          * which is considered a single 136byte integer when
     369             :          * comparing. This call increases the counter by one, dealing
     370             :          * with the overflow between the overflows */
     371             : 
     372           0 :         if (args->key.min_offset < (uint64_t) -1) {
     373           0 :                 args->key.min_offset++;
     374           0 :                 return true;
     375             :         }
     376             : 
     377           0 :         if (args->key.min_type < (uint8_t) -1) {
     378           0 :                 args->key.min_type++;
     379           0 :                 args->key.min_offset = 0;
     380           0 :                 return true;
     381             :         }
     382             : 
     383           0 :         if (args->key.min_objectid < (uint64_t) -1) {
     384           0 :                 args->key.min_objectid++;
     385           0 :                 args->key.min_offset = 0;
     386           0 :                 args->key.min_type = 0;
     387           0 :                 return true;
     388             :         }
     389             : 
     390           0 :         return 0;
     391             : }
     392             : 
     393           0 : static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
     394           0 :         assert(args);
     395           0 :         assert(h);
     396             : 
     397           0 :         args->key.min_objectid = h->objectid;
     398           0 :         args->key.min_type = h->type;
     399           0 :         args->key.min_offset = h->offset;
     400           0 : }
     401             : 
     402           0 : static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
     403             :         int r;
     404             : 
     405           0 :         assert(args);
     406             : 
     407             :         /* Compare min and max */
     408             : 
     409           0 :         r = CMP(args->key.min_objectid, args->key.max_objectid);
     410           0 :         if (r != 0)
     411           0 :                 return r;
     412             : 
     413           0 :         r = CMP(args->key.min_type, args->key.max_type);
     414           0 :         if (r != 0)
     415           0 :                 return r;
     416             : 
     417           0 :         return CMP(args->key.min_offset, args->key.max_offset);
     418             : }
     419             : 
     420             : #define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args)                  \
     421             :         for ((i) = 0,                                                   \
     422             :              (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
     423             :              (i) < (args).key.nr_items;                                 \
     424             :              (i)++,                                                     \
     425             :              (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
     426             : 
     427             : #define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh)                              \
     428             :         ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
     429             : 
     430           0 : int btrfs_subvol_get_info_fd(int fd, uint64_t subvol_id, BtrfsSubvolInfo *ret) {
     431           0 :         struct btrfs_ioctl_search_args args = {
     432             :                 /* Tree of tree roots */
     433             :                 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
     434             : 
     435             :                 /* Look precisely for the subvolume items */
     436             :                 .key.min_type = BTRFS_ROOT_ITEM_KEY,
     437             :                 .key.max_type = BTRFS_ROOT_ITEM_KEY,
     438             : 
     439             :                 .key.min_offset = 0,
     440             :                 .key.max_offset = (uint64_t) -1,
     441             : 
     442             :                 /* No restrictions on the other components */
     443             :                 .key.min_transid = 0,
     444             :                 .key.max_transid = (uint64_t) -1,
     445             :         };
     446             : 
     447           0 :         bool found = false;
     448             :         int r;
     449             : 
     450           0 :         assert(fd >= 0);
     451           0 :         assert(ret);
     452             : 
     453           0 :         if (subvol_id == 0) {
     454           0 :                 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
     455           0 :                 if (r < 0)
     456           0 :                         return r;
     457             :         } else {
     458           0 :                 r = btrfs_is_filesystem(fd);
     459           0 :                 if (r < 0)
     460           0 :                         return r;
     461           0 :                 if (!r)
     462           0 :                         return -ENOTTY;
     463             :         }
     464             : 
     465           0 :         args.key.min_objectid = args.key.max_objectid = subvol_id;
     466             : 
     467           0 :         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
     468             :                 const struct btrfs_ioctl_search_header *sh;
     469             :                 unsigned i;
     470             : 
     471           0 :                 args.key.nr_items = 256;
     472           0 :                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
     473           0 :                         return -errno;
     474             : 
     475           0 :                 if (args.key.nr_items <= 0)
     476           0 :                         break;
     477             : 
     478           0 :                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
     479             : 
     480             :                         const struct btrfs_root_item *ri;
     481             : 
     482             :                         /* Make sure we start the next search at least from this entry */
     483           0 :                         btrfs_ioctl_search_args_set(&args, sh);
     484             : 
     485           0 :                         if (sh->objectid != subvol_id)
     486           0 :                                 continue;
     487           0 :                         if (sh->type != BTRFS_ROOT_ITEM_KEY)
     488           0 :                                 continue;
     489             : 
     490             :                         /* Older versions of the struct lacked the otime setting */
     491           0 :                         if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
     492           0 :                                 continue;
     493             : 
     494           0 :                         ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
     495             : 
     496           0 :                         ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
     497           0 :                                 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
     498             : 
     499           0 :                         ret->subvol_id = subvol_id;
     500           0 :                         ret->read_only = le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY;
     501             : 
     502             :                         assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
     503           0 :                         memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
     504           0 :                         memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
     505             : 
     506           0 :                         found = true;
     507           0 :                         goto finish;
     508             :                 }
     509             : 
     510             :                 /* Increase search key by one, to read the next item, if we can. */
     511           0 :                 if (!btrfs_ioctl_search_args_inc(&args))
     512           0 :                         break;
     513             :         }
     514             : 
     515           0 : finish:
     516           0 :         if (!found)
     517           0 :                 return -ENODATA;
     518             : 
     519           0 :         return 0;
     520             : }
     521             : 
     522           0 : int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *ret) {
     523             : 
     524           0 :         struct btrfs_ioctl_search_args args = {
     525             :                 /* Tree of quota items */
     526             :                 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
     527             : 
     528             :                 /* The object ID is always 0 */
     529             :                 .key.min_objectid = 0,
     530             :                 .key.max_objectid = 0,
     531             : 
     532             :                 /* Look precisely for the quota items */
     533             :                 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
     534             :                 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
     535             : 
     536             :                 /* No restrictions on the other components */
     537             :                 .key.min_transid = 0,
     538             :                 .key.max_transid = (uint64_t) -1,
     539             :         };
     540             : 
     541           0 :         bool found_info = false, found_limit = false;
     542             :         int r;
     543             : 
     544           0 :         assert(fd >= 0);
     545           0 :         assert(ret);
     546             : 
     547           0 :         if (qgroupid == 0) {
     548           0 :                 r = btrfs_subvol_get_id_fd(fd, &qgroupid);
     549           0 :                 if (r < 0)
     550           0 :                         return r;
     551             :         } else {
     552           0 :                 r = btrfs_is_filesystem(fd);
     553           0 :                 if (r < 0)
     554           0 :                         return r;
     555           0 :                 if (!r)
     556           0 :                         return -ENOTTY;
     557             :         }
     558             : 
     559           0 :         args.key.min_offset = args.key.max_offset = qgroupid;
     560             : 
     561           0 :         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
     562             :                 const struct btrfs_ioctl_search_header *sh;
     563             :                 unsigned i;
     564             : 
     565           0 :                 args.key.nr_items = 256;
     566           0 :                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) {
     567           0 :                         if (errno == ENOENT) /* quota tree is missing: quota disabled */
     568           0 :                                 break;
     569             : 
     570           0 :                         return -errno;
     571             :                 }
     572             : 
     573           0 :                 if (args.key.nr_items <= 0)
     574           0 :                         break;
     575             : 
     576           0 :                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
     577             : 
     578             :                         /* Make sure we start the next search at least from this entry */
     579           0 :                         btrfs_ioctl_search_args_set(&args, sh);
     580             : 
     581           0 :                         if (sh->objectid != 0)
     582           0 :                                 continue;
     583           0 :                         if (sh->offset != qgroupid)
     584           0 :                                 continue;
     585             : 
     586           0 :                         if (sh->type == BTRFS_QGROUP_INFO_KEY) {
     587           0 :                                 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
     588             : 
     589           0 :                                 ret->referenced = le64toh(qii->rfer);
     590           0 :                                 ret->exclusive = le64toh(qii->excl);
     591             : 
     592           0 :                                 found_info = true;
     593             : 
     594           0 :                         } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
     595           0 :                                 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
     596             : 
     597           0 :                                 if (le64toh(qli->flags) & BTRFS_QGROUP_LIMIT_MAX_RFER)
     598           0 :                                         ret->referenced_max = le64toh(qli->max_rfer);
     599             :                                 else
     600           0 :                                         ret->referenced_max = (uint64_t) -1;
     601             : 
     602           0 :                                 if (le64toh(qli->flags) & BTRFS_QGROUP_LIMIT_MAX_EXCL)
     603           0 :                                         ret->exclusive_max = le64toh(qli->max_excl);
     604             :                                 else
     605           0 :                                         ret->exclusive_max = (uint64_t) -1;
     606             : 
     607           0 :                                 found_limit = true;
     608             :                         }
     609             : 
     610           0 :                         if (found_info && found_limit)
     611           0 :                                 goto finish;
     612             :                 }
     613             : 
     614             :                 /* Increase search key by one, to read the next item, if we can. */
     615           0 :                 if (!btrfs_ioctl_search_args_inc(&args))
     616           0 :                         break;
     617             :         }
     618             : 
     619           0 : finish:
     620           0 :         if (!found_limit && !found_info)
     621           0 :                 return -ENODATA;
     622             : 
     623           0 :         if (!found_info) {
     624           0 :                 ret->referenced = (uint64_t) -1;
     625           0 :                 ret->exclusive = (uint64_t) -1;
     626             :         }
     627             : 
     628           0 :         if (!found_limit) {
     629           0 :                 ret->referenced_max = (uint64_t) -1;
     630           0 :                 ret->exclusive_max = (uint64_t) -1;
     631             :         }
     632             : 
     633           0 :         return 0;
     634             : }
     635             : 
     636           0 : int btrfs_qgroup_get_quota(const char *path, uint64_t qgroupid, BtrfsQuotaInfo *ret) {
     637           0 :         _cleanup_close_ int fd = -1;
     638             : 
     639           0 :         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
     640           0 :         if (fd < 0)
     641           0 :                 return -errno;
     642             : 
     643           0 :         return btrfs_qgroup_get_quota_fd(fd, qgroupid, ret);
     644             : }
     645             : 
     646           0 : int btrfs_subvol_find_subtree_qgroup(int fd, uint64_t subvol_id, uint64_t *ret) {
     647           0 :         uint64_t level, lowest = (uint64_t) -1, lowest_qgroupid = 0;
     648           0 :         _cleanup_free_ uint64_t *qgroups = NULL;
     649             :         int r, n, i;
     650             : 
     651           0 :         assert(fd >= 0);
     652           0 :         assert(ret);
     653             : 
     654             :         /* This finds the "subtree" qgroup for a specific
     655             :          * subvolume. This only works for subvolumes that have been
     656             :          * prepared with btrfs_subvol_auto_qgroup_fd() with
     657             :          * insert_intermediary_qgroup=true (or equivalent). For others
     658             :          * it will return the leaf qgroup instead. The two cases may
     659             :          * be distuingished via the return value, which is 1 in case
     660             :          * an appropriate "subtree" qgroup was found, and 0
     661             :          * otherwise. */
     662             : 
     663           0 :         if (subvol_id == 0) {
     664           0 :                 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
     665           0 :                 if (r < 0)
     666           0 :                         return r;
     667             :         }
     668             : 
     669           0 :         r = btrfs_qgroupid_split(subvol_id, &level, NULL);
     670           0 :         if (r < 0)
     671           0 :                 return r;
     672           0 :         if (level != 0) /* Input must be a leaf qgroup */
     673           0 :                 return -EINVAL;
     674             : 
     675           0 :         n = btrfs_qgroup_find_parents(fd, subvol_id, &qgroups);
     676           0 :         if (n < 0)
     677           0 :                 return n;
     678             : 
     679           0 :         for (i = 0; i < n; i++) {
     680             :                 uint64_t id;
     681             : 
     682           0 :                 r = btrfs_qgroupid_split(qgroups[i], &level, &id);
     683           0 :                 if (r < 0)
     684           0 :                         return r;
     685             : 
     686           0 :                 if (id != subvol_id)
     687           0 :                         continue;
     688             : 
     689           0 :                 if (lowest == (uint64_t) -1 || level < lowest) {
     690           0 :                         lowest_qgroupid = qgroups[i];
     691           0 :                         lowest = level;
     692             :                 }
     693             :         }
     694             : 
     695           0 :         if (lowest == (uint64_t) -1) {
     696             :                 /* No suitable higher-level qgroup found, let's return
     697             :                  * the leaf qgroup instead, and indicate that with the
     698             :                  * return value. */
     699             : 
     700           0 :                 *ret = subvol_id;
     701           0 :                 return 0;
     702             :         }
     703             : 
     704           0 :         *ret = lowest_qgroupid;
     705           0 :         return 1;
     706             : }
     707             : 
     708           0 : int btrfs_subvol_get_subtree_quota_fd(int fd, uint64_t subvol_id, BtrfsQuotaInfo *ret) {
     709             :         uint64_t qgroupid;
     710             :         int r;
     711             : 
     712           0 :         assert(fd >= 0);
     713           0 :         assert(ret);
     714             : 
     715             :         /* This determines the quota data of the qgroup with the
     716             :          * lowest level, that shares the id part with the specified
     717             :          * subvolume. This is useful for determining the quota data
     718             :          * for entire subvolume subtrees, as long as the subtrees have
     719             :          * been set up with btrfs_qgroup_subvol_auto_fd() or in a
     720             :          * compatible way */
     721             : 
     722           0 :         r = btrfs_subvol_find_subtree_qgroup(fd, subvol_id, &qgroupid);
     723           0 :         if (r < 0)
     724           0 :                 return r;
     725             : 
     726           0 :         return btrfs_qgroup_get_quota_fd(fd, qgroupid, ret);
     727             : }
     728             : 
     729           0 : int btrfs_subvol_get_subtree_quota(const char *path, uint64_t subvol_id, BtrfsQuotaInfo *ret) {
     730           0 :         _cleanup_close_ int fd = -1;
     731             : 
     732           0 :         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
     733           0 :         if (fd < 0)
     734           0 :                 return -errno;
     735             : 
     736           0 :         return btrfs_subvol_get_subtree_quota_fd(fd, subvol_id, ret);
     737             : }
     738             : 
     739           2 : int btrfs_defrag_fd(int fd) {
     740             :         int r;
     741             : 
     742           2 :         assert(fd >= 0);
     743             : 
     744           2 :         r = fd_verify_regular(fd);
     745           2 :         if (r < 0)
     746           0 :                 return r;
     747             : 
     748           2 :         if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
     749           2 :                 return -errno;
     750             : 
     751           0 :         return 0;
     752             : }
     753             : 
     754           0 : int btrfs_defrag(const char *p) {
     755           0 :         _cleanup_close_ int fd = -1;
     756             : 
     757           0 :         fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
     758           0 :         if (fd < 0)
     759           0 :                 return -errno;
     760             : 
     761           0 :         return btrfs_defrag_fd(fd);
     762             : }
     763             : 
     764           0 : int btrfs_quota_enable_fd(int fd, bool b) {
     765           0 :         struct btrfs_ioctl_quota_ctl_args args = {
     766           0 :                 .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
     767             :         };
     768             :         int r;
     769             : 
     770           0 :         assert(fd >= 0);
     771             : 
     772           0 :         r = btrfs_is_filesystem(fd);
     773           0 :         if (r < 0)
     774           0 :                 return r;
     775           0 :         if (!r)
     776           0 :                 return -ENOTTY;
     777             : 
     778           0 :         if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
     779           0 :                 return -errno;
     780             : 
     781           0 :         return 0;
     782             : }
     783             : 
     784           0 : int btrfs_quota_enable(const char *path, bool b) {
     785           0 :         _cleanup_close_ int fd = -1;
     786             : 
     787           0 :         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
     788           0 :         if (fd < 0)
     789           0 :                 return -errno;
     790             : 
     791           0 :         return btrfs_quota_enable_fd(fd, b);
     792             : }
     793             : 
     794           0 : int btrfs_qgroup_set_limit_fd(int fd, uint64_t qgroupid, uint64_t referenced_max) {
     795             : 
     796           0 :         struct btrfs_ioctl_qgroup_limit_args args = {
     797             :                 .lim.max_rfer = referenced_max,
     798             :                 .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
     799             :         };
     800             :         unsigned c;
     801             :         int r;
     802             : 
     803           0 :         assert(fd >= 0);
     804             : 
     805           0 :         if (qgroupid == 0) {
     806           0 :                 r = btrfs_subvol_get_id_fd(fd, &qgroupid);
     807           0 :                 if (r < 0)
     808           0 :                         return r;
     809             :         } else {
     810           0 :                 r = btrfs_is_filesystem(fd);
     811           0 :                 if (r < 0)
     812           0 :                         return r;
     813           0 :                 if (!r)
     814           0 :                         return -ENOTTY;
     815             :         }
     816             : 
     817           0 :         args.qgroupid = qgroupid;
     818             : 
     819           0 :         for (c = 0;; c++) {
     820           0 :                 if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0) {
     821             : 
     822           0 :                         if (errno == EBUSY && c < 10) {
     823           0 :                                 (void) btrfs_quota_scan_wait(fd);
     824           0 :                                 continue;
     825             :                         }
     826             : 
     827           0 :                         return -errno;
     828             :                 }
     829             : 
     830           0 :                 break;
     831             :         }
     832             : 
     833           0 :         return 0;
     834             : }
     835             : 
     836           0 : int btrfs_qgroup_set_limit(const char *path, uint64_t qgroupid, uint64_t referenced_max) {
     837           0 :         _cleanup_close_ int fd = -1;
     838             : 
     839           0 :         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
     840           0 :         if (fd < 0)
     841           0 :                 return -errno;
     842             : 
     843           0 :         return btrfs_qgroup_set_limit_fd(fd, qgroupid, referenced_max);
     844             : }
     845             : 
     846           0 : int btrfs_subvol_set_subtree_quota_limit_fd(int fd, uint64_t subvol_id, uint64_t referenced_max) {
     847             :         uint64_t qgroupid;
     848             :         int r;
     849             : 
     850           0 :         assert(fd >= 0);
     851             : 
     852           0 :         r = btrfs_subvol_find_subtree_qgroup(fd, subvol_id, &qgroupid);
     853           0 :         if (r < 0)
     854           0 :                 return r;
     855             : 
     856           0 :         return btrfs_qgroup_set_limit_fd(fd, qgroupid, referenced_max);
     857             : }
     858             : 
     859           0 : int btrfs_subvol_set_subtree_quota_limit(const char *path, uint64_t subvol_id, uint64_t referenced_max) {
     860           0 :         _cleanup_close_ int fd = -1;
     861             : 
     862           0 :         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
     863           0 :         if (fd < 0)
     864           0 :                 return -errno;
     865             : 
     866           0 :         return btrfs_subvol_set_subtree_quota_limit_fd(fd, subvol_id, referenced_max);
     867             : }
     868             : 
     869           0 : int btrfs_qgroupid_make(uint64_t level, uint64_t id, uint64_t *ret) {
     870           0 :         assert(ret);
     871             : 
     872           0 :         if (level >= (UINT64_C(1) << (64 - BTRFS_QGROUP_LEVEL_SHIFT)))
     873           0 :                 return -EINVAL;
     874             : 
     875           0 :         if (id >= (UINT64_C(1) << BTRFS_QGROUP_LEVEL_SHIFT))
     876           0 :                 return -EINVAL;
     877             : 
     878           0 :         *ret = (level << BTRFS_QGROUP_LEVEL_SHIFT) | id;
     879           0 :         return 0;
     880             : }
     881             : 
     882           0 : int btrfs_qgroupid_split(uint64_t qgroupid, uint64_t *level, uint64_t *id) {
     883           0 :         assert(level || id);
     884             : 
     885           0 :         if (level)
     886           0 :                 *level = qgroupid >> BTRFS_QGROUP_LEVEL_SHIFT;
     887             : 
     888           0 :         if (id)
     889           0 :                 *id = qgroupid & ((UINT64_C(1) << BTRFS_QGROUP_LEVEL_SHIFT) - 1);
     890             : 
     891           0 :         return 0;
     892             : }
     893             : 
     894           0 : static int qgroup_create_or_destroy(int fd, bool b, uint64_t qgroupid) {
     895             : 
     896           0 :         struct btrfs_ioctl_qgroup_create_args args = {
     897             :                 .create = b,
     898             :                 .qgroupid = qgroupid,
     899             :         };
     900             :         unsigned c;
     901             :         int r;
     902             : 
     903           0 :         r = btrfs_is_filesystem(fd);
     904           0 :         if (r < 0)
     905           0 :                 return r;
     906           0 :         if (r == 0)
     907           0 :                 return -ENOTTY;
     908             : 
     909           0 :         for (c = 0;; c++) {
     910           0 :                 if (ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args) < 0) {
     911             : 
     912             :                         /* If quota is not enabled, we get EINVAL. Turn this into a recognizable error */
     913           0 :                         if (errno == EINVAL)
     914           0 :                                 return -ENOPROTOOPT;
     915             : 
     916           0 :                         if (errno == EBUSY && c < 10) {
     917           0 :                                 (void) btrfs_quota_scan_wait(fd);
     918           0 :                                 continue;
     919             :                         }
     920             : 
     921           0 :                         return -errno;
     922             :                 }
     923             : 
     924           0 :                 break;
     925             :         }
     926             : 
     927           0 :         return 0;
     928             : }
     929             : 
     930           0 : int btrfs_qgroup_create(int fd, uint64_t qgroupid) {
     931           0 :         return qgroup_create_or_destroy(fd, true, qgroupid);
     932             : }
     933             : 
     934           0 : int btrfs_qgroup_destroy(int fd, uint64_t qgroupid) {
     935           0 :         return qgroup_create_or_destroy(fd, false, qgroupid);
     936             : }
     937             : 
     938           0 : int btrfs_qgroup_destroy_recursive(int fd, uint64_t qgroupid) {
     939           0 :         _cleanup_free_ uint64_t *qgroups = NULL;
     940             :         uint64_t subvol_id;
     941             :         int i, n, r;
     942             : 
     943             :         /* Destroys the specified qgroup, but unassigns it from all
     944             :          * its parents first. Also, it recursively destroys all
     945             :          * qgroups it is assigned to that have the same id part of the
     946             :          * qgroupid as the specified group. */
     947             : 
     948           0 :         r = btrfs_qgroupid_split(qgroupid, NULL, &subvol_id);
     949           0 :         if (r < 0)
     950           0 :                 return r;
     951             : 
     952           0 :         n = btrfs_qgroup_find_parents(fd, qgroupid, &qgroups);
     953           0 :         if (n < 0)
     954           0 :                 return n;
     955             : 
     956           0 :         for (i = 0; i < n; i++) {
     957             :                 uint64_t id;
     958             : 
     959           0 :                 r = btrfs_qgroupid_split(qgroups[i], NULL, &id);
     960           0 :                 if (r < 0)
     961           0 :                         return r;
     962             : 
     963           0 :                 r = btrfs_qgroup_unassign(fd, qgroupid, qgroups[i]);
     964           0 :                 if (r < 0)
     965           0 :                         return r;
     966             : 
     967           0 :                 if (id != subvol_id)
     968           0 :                         continue;
     969             : 
     970             :                 /* The parent qgroupid shares the same id part with
     971             :                  * us? If so, destroy it too. */
     972             : 
     973           0 :                 (void) btrfs_qgroup_destroy_recursive(fd, qgroups[i]);
     974             :         }
     975             : 
     976           0 :         return btrfs_qgroup_destroy(fd, qgroupid);
     977             : }
     978             : 
     979           0 : int btrfs_quota_scan_start(int fd) {
     980           0 :         struct btrfs_ioctl_quota_rescan_args args = {};
     981             : 
     982           0 :         assert(fd >= 0);
     983             : 
     984           0 :         if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN, &args) < 0)
     985           0 :                 return -errno;
     986             : 
     987           0 :         return 0;
     988             : }
     989             : 
     990           0 : int btrfs_quota_scan_wait(int fd) {
     991           0 :         assert(fd >= 0);
     992             : 
     993           0 :         if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN_WAIT) < 0)
     994           0 :                 return -errno;
     995             : 
     996           0 :         return 0;
     997             : }
     998             : 
     999           0 : int btrfs_quota_scan_ongoing(int fd) {
    1000           0 :         struct btrfs_ioctl_quota_rescan_args args = {};
    1001             : 
    1002           0 :         assert(fd >= 0);
    1003             : 
    1004           0 :         if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN_STATUS, &args) < 0)
    1005           0 :                 return -errno;
    1006             : 
    1007           0 :         return !!args.flags;
    1008             : }
    1009             : 
    1010           0 : static int qgroup_assign_or_unassign(int fd, bool b, uint64_t child, uint64_t parent) {
    1011           0 :         struct btrfs_ioctl_qgroup_assign_args args = {
    1012             :                 .assign = b,
    1013             :                 .src = child,
    1014             :                 .dst = parent,
    1015             :         };
    1016             :         unsigned c;
    1017             :         int r;
    1018             : 
    1019           0 :         r = btrfs_is_filesystem(fd);
    1020           0 :         if (r < 0)
    1021           0 :                 return r;
    1022           0 :         if (r == 0)
    1023           0 :                 return -ENOTTY;
    1024             : 
    1025           0 :         for (c = 0;; c++) {
    1026           0 :                 r = ioctl(fd, BTRFS_IOC_QGROUP_ASSIGN, &args);
    1027           0 :                 if (r < 0) {
    1028           0 :                         if (errno == EBUSY && c < 10) {
    1029           0 :                                 (void) btrfs_quota_scan_wait(fd);
    1030           0 :                                 continue;
    1031             :                         }
    1032             : 
    1033           0 :                         return -errno;
    1034             :                 }
    1035             : 
    1036           0 :                 if (r == 0)
    1037           0 :                         return 0;
    1038             : 
    1039             :                 /* If the return value is > 0, we need to request a rescan */
    1040             : 
    1041           0 :                 (void) btrfs_quota_scan_start(fd);
    1042           0 :                 return 1;
    1043             :         }
    1044             : }
    1045             : 
    1046           0 : int btrfs_qgroup_assign(int fd, uint64_t child, uint64_t parent) {
    1047           0 :         return qgroup_assign_or_unassign(fd, true, child, parent);
    1048             : }
    1049             : 
    1050           0 : int btrfs_qgroup_unassign(int fd, uint64_t child, uint64_t parent) {
    1051           0 :         return qgroup_assign_or_unassign(fd, false, child, parent);
    1052             : }
    1053             : 
    1054           0 : static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, BtrfsRemoveFlags flags) {
    1055           0 :         struct btrfs_ioctl_search_args args = {
    1056             :                 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
    1057             : 
    1058             :                 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
    1059             :                 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
    1060             : 
    1061             :                 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
    1062             :                 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
    1063             : 
    1064             :                 .key.min_transid = 0,
    1065             :                 .key.max_transid = (uint64_t) -1,
    1066             :         };
    1067             : 
    1068           0 :         struct btrfs_ioctl_vol_args vol_args = {};
    1069           0 :         _cleanup_close_ int subvol_fd = -1;
    1070             :         struct stat st;
    1071           0 :         bool made_writable = false;
    1072             :         int r;
    1073             : 
    1074           0 :         assert(fd >= 0);
    1075           0 :         assert(subvolume);
    1076             : 
    1077           0 :         if (fstat(fd, &st) < 0)
    1078           0 :                 return -errno;
    1079             : 
    1080           0 :         if (!S_ISDIR(st.st_mode))
    1081           0 :                 return -EINVAL;
    1082             : 
    1083           0 :         subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
    1084           0 :         if (subvol_fd < 0)
    1085           0 :                 return -errno;
    1086             : 
    1087             :         /* Let's check if this is actually a subvolume. Note that this is mostly redundant, as BTRFS_IOC_SNAP_DESTROY
    1088             :          * would fail anyway if it is not. However, it's a good thing to check this ahead of time so that we can return
    1089             :          * ENOTTY unconditionally in this case. This is different from the ioctl() which will return EPERM/EACCES if we
    1090             :          * don't have the privileges to remove subvolumes, regardless if the specified directory is actually a
    1091             :          * subvolume or not. In order to make it easy for callers to cover the "this is not a btrfs subvolume" case
    1092             :          * let's prefer ENOTTY over EPERM/EACCES though. */
    1093           0 :         r = btrfs_is_subvol_fd(subvol_fd);
    1094           0 :         if (r < 0)
    1095           0 :                 return r;
    1096           0 :         if (r == 0) /* Not a btrfs subvolume */
    1097           0 :                 return -ENOTTY;
    1098             : 
    1099           0 :         if (subvol_id == 0) {
    1100           0 :                 r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
    1101           0 :                 if (r < 0)
    1102           0 :                         return r;
    1103             :         }
    1104             : 
    1105             :         /* First, try to remove the subvolume. If it happens to be
    1106             :          * already empty, this will just work. */
    1107           0 :         strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
    1108           0 :         if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0) {
    1109           0 :                 (void) btrfs_qgroup_destroy_recursive(fd, subvol_id); /* for the leaf subvolumes, the qgroup id is identical to the subvol id */
    1110           0 :                 return 0;
    1111             :         }
    1112           0 :         if (!(flags & BTRFS_REMOVE_RECURSIVE) || errno != ENOTEMPTY)
    1113           0 :                 return -errno;
    1114             : 
    1115             :         /* OK, the subvolume is not empty, let's look for child
    1116             :          * subvolumes, and remove them, first */
    1117             : 
    1118           0 :         args.key.min_offset = args.key.max_offset = subvol_id;
    1119             : 
    1120           0 :         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
    1121             :                 const struct btrfs_ioctl_search_header *sh;
    1122             :                 unsigned i;
    1123             : 
    1124           0 :                 args.key.nr_items = 256;
    1125           0 :                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
    1126           0 :                         return -errno;
    1127             : 
    1128           0 :                 if (args.key.nr_items <= 0)
    1129           0 :                         break;
    1130             : 
    1131           0 :                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
    1132           0 :                         _cleanup_free_ char *p = NULL;
    1133             :                         const struct btrfs_root_ref *ref;
    1134             :                         struct btrfs_ioctl_ino_lookup_args ino_args;
    1135             : 
    1136           0 :                         btrfs_ioctl_search_args_set(&args, sh);
    1137             : 
    1138           0 :                         if (sh->type != BTRFS_ROOT_BACKREF_KEY)
    1139           0 :                                 continue;
    1140           0 :                         if (sh->offset != subvol_id)
    1141           0 :                                 continue;
    1142             : 
    1143           0 :                         ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
    1144             : 
    1145           0 :                         p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
    1146           0 :                         if (!p)
    1147           0 :                                 return -ENOMEM;
    1148             : 
    1149           0 :                         zero(ino_args);
    1150           0 :                         ino_args.treeid = subvol_id;
    1151           0 :                         ino_args.objectid = htole64(ref->dirid);
    1152             : 
    1153           0 :                         if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
    1154           0 :                                 return -errno;
    1155             : 
    1156           0 :                         if (!made_writable) {
    1157           0 :                                 r = btrfs_subvol_set_read_only_fd(subvol_fd, false);
    1158           0 :                                 if (r < 0)
    1159           0 :                                         return r;
    1160             : 
    1161           0 :                                 made_writable = true;
    1162             :                         }
    1163             : 
    1164           0 :                         if (isempty(ino_args.name))
    1165             :                                 /* Subvolume is in the top-level
    1166             :                                  * directory of the subvolume. */
    1167           0 :                                 r = subvol_remove_children(subvol_fd, p, sh->objectid, flags);
    1168             :                         else {
    1169           0 :                                 _cleanup_close_ int child_fd = -1;
    1170             : 
    1171             :                                 /* Subvolume is somewhere further down,
    1172             :                                  * hence we need to open the
    1173             :                                  * containing directory first */
    1174             : 
    1175           0 :                                 child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
    1176           0 :                                 if (child_fd < 0)
    1177           0 :                                         return -errno;
    1178             : 
    1179           0 :                                 r = subvol_remove_children(child_fd, p, sh->objectid, flags);
    1180             :                         }
    1181           0 :                         if (r < 0)
    1182           0 :                                 return r;
    1183             :                 }
    1184             : 
    1185             :                 /* Increase search key by one, to read the next item, if we can. */
    1186           0 :                 if (!btrfs_ioctl_search_args_inc(&args))
    1187           0 :                         break;
    1188             :         }
    1189             : 
    1190             :         /* OK, the child subvolumes should all be gone now, let's try
    1191             :          * again to remove the subvolume */
    1192           0 :         if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0)
    1193           0 :                 return -errno;
    1194             : 
    1195           0 :         (void) btrfs_qgroup_destroy_recursive(fd, subvol_id);
    1196           0 :         return 0;
    1197             : }
    1198             : 
    1199           0 : int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) {
    1200           0 :         _cleanup_close_ int fd = -1;
    1201             :         const char *subvolume;
    1202             :         int r;
    1203             : 
    1204           0 :         assert(path);
    1205             : 
    1206           0 :         r = extract_subvolume_name(path, &subvolume);
    1207           0 :         if (r < 0)
    1208           0 :                 return r;
    1209             : 
    1210           0 :         fd = open_parent(path, O_CLOEXEC, 0);
    1211           0 :         if (fd < 0)
    1212           0 :                 return fd;
    1213             : 
    1214           0 :         return subvol_remove_children(fd, subvolume, 0, flags);
    1215             : }
    1216             : 
    1217           0 : int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags) {
    1218           0 :         return subvol_remove_children(fd, subvolume, 0, flags);
    1219             : }
    1220             : 
    1221           0 : int btrfs_qgroup_copy_limits(int fd, uint64_t old_qgroupid, uint64_t new_qgroupid) {
    1222             : 
    1223           0 :         struct btrfs_ioctl_search_args args = {
    1224             :                 /* Tree of quota items */
    1225             :                 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
    1226             : 
    1227             :                 /* The object ID is always 0 */
    1228             :                 .key.min_objectid = 0,
    1229             :                 .key.max_objectid = 0,
    1230             : 
    1231             :                 /* Look precisely for the quota items */
    1232             :                 .key.min_type = BTRFS_QGROUP_LIMIT_KEY,
    1233             :                 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
    1234             : 
    1235             :                 /* For our qgroup */
    1236             :                 .key.min_offset = old_qgroupid,
    1237             :                 .key.max_offset = old_qgroupid,
    1238             : 
    1239             :                 /* No restrictions on the other components */
    1240             :                 .key.min_transid = 0,
    1241             :                 .key.max_transid = (uint64_t) -1,
    1242             :         };
    1243             : 
    1244             :         int r;
    1245             : 
    1246           0 :         r = btrfs_is_filesystem(fd);
    1247           0 :         if (r < 0)
    1248           0 :                 return r;
    1249           0 :         if (!r)
    1250           0 :                 return -ENOTTY;
    1251             : 
    1252           0 :         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
    1253             :                 const struct btrfs_ioctl_search_header *sh;
    1254             :                 unsigned i;
    1255             : 
    1256           0 :                 args.key.nr_items = 256;
    1257           0 :                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) {
    1258           0 :                         if (errno == ENOENT) /* quota tree missing: quota is not enabled, hence nothing to copy */
    1259           0 :                                 break;
    1260             : 
    1261           0 :                         return -errno;
    1262             :                 }
    1263             : 
    1264           0 :                 if (args.key.nr_items <= 0)
    1265           0 :                         break;
    1266             : 
    1267           0 :                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
    1268           0 :                         const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
    1269             :                         struct btrfs_ioctl_qgroup_limit_args qargs;
    1270             :                         unsigned c;
    1271             : 
    1272             :                         /* Make sure we start the next search at least from this entry */
    1273           0 :                         btrfs_ioctl_search_args_set(&args, sh);
    1274             : 
    1275           0 :                         if (sh->objectid != 0)
    1276           0 :                                 continue;
    1277           0 :                         if (sh->type != BTRFS_QGROUP_LIMIT_KEY)
    1278           0 :                                 continue;
    1279           0 :                         if (sh->offset != old_qgroupid)
    1280           0 :                                 continue;
    1281             : 
    1282             :                         /* We found the entry, now copy things over. */
    1283             : 
    1284           0 :                         qargs = (struct btrfs_ioctl_qgroup_limit_args) {
    1285             :                                 .qgroupid = new_qgroupid,
    1286             : 
    1287           0 :                                 .lim.max_rfer = le64toh(qli->max_rfer),
    1288           0 :                                 .lim.max_excl = le64toh(qli->max_excl),
    1289           0 :                                 .lim.rsv_rfer = le64toh(qli->rsv_rfer),
    1290           0 :                                 .lim.rsv_excl = le64toh(qli->rsv_excl),
    1291             : 
    1292           0 :                                 .lim.flags = le64toh(qli->flags) & (BTRFS_QGROUP_LIMIT_MAX_RFER|
    1293             :                                                                     BTRFS_QGROUP_LIMIT_MAX_EXCL|
    1294             :                                                                     BTRFS_QGROUP_LIMIT_RSV_RFER|
    1295             :                                                                     BTRFS_QGROUP_LIMIT_RSV_EXCL),
    1296             :                         };
    1297             : 
    1298           0 :                         for (c = 0;; c++) {
    1299           0 :                                 if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &qargs) < 0) {
    1300           0 :                                         if (errno == EBUSY && c < 10) {
    1301           0 :                                                 (void) btrfs_quota_scan_wait(fd);
    1302           0 :                                                 continue;
    1303             :                                         }
    1304           0 :                                         return -errno;
    1305             :                                 }
    1306             : 
    1307           0 :                                 break;
    1308             :                         }
    1309             : 
    1310           0 :                         return 1;
    1311             :                 }
    1312             : 
    1313             :                 /* Increase search key by one, to read the next item, if we can. */
    1314           0 :                 if (!btrfs_ioctl_search_args_inc(&args))
    1315           0 :                         break;
    1316             :         }
    1317             : 
    1318           0 :         return 0;
    1319             : }
    1320             : 
    1321           0 : static int copy_quota_hierarchy(int fd, uint64_t old_subvol_id, uint64_t new_subvol_id) {
    1322           0 :         _cleanup_free_ uint64_t *old_qgroups = NULL, *old_parent_qgroups = NULL;
    1323           0 :         bool copy_from_parent = false, insert_intermediary_qgroup = false;
    1324             :         int n_old_qgroups, n_old_parent_qgroups, r, i;
    1325             :         uint64_t old_parent_id;
    1326             : 
    1327           0 :         assert(fd >= 0);
    1328             : 
    1329             :         /* Copies a reduced form of quota information from the old to
    1330             :          * the new subvolume. */
    1331             : 
    1332           0 :         n_old_qgroups = btrfs_qgroup_find_parents(fd, old_subvol_id, &old_qgroups);
    1333           0 :         if (n_old_qgroups <= 0) /* Nothing to copy */
    1334           0 :                 return n_old_qgroups;
    1335             : 
    1336           0 :         r = btrfs_subvol_get_parent(fd, old_subvol_id, &old_parent_id);
    1337           0 :         if (r == -ENXIO)
    1338             :                 /* We have no parent, hence nothing to copy. */
    1339           0 :                 n_old_parent_qgroups = 0;
    1340           0 :         else if (r < 0)
    1341           0 :                 return r;
    1342             :         else {
    1343           0 :                 n_old_parent_qgroups = btrfs_qgroup_find_parents(fd, old_parent_id, &old_parent_qgroups);
    1344           0 :                 if (n_old_parent_qgroups < 0)
    1345           0 :                         return n_old_parent_qgroups;
    1346             :         }
    1347             : 
    1348           0 :         for (i = 0; i < n_old_qgroups; i++) {
    1349             :                 uint64_t id;
    1350             :                 int j;
    1351             : 
    1352           0 :                 r = btrfs_qgroupid_split(old_qgroups[i], NULL, &id);
    1353           0 :                 if (r < 0)
    1354           0 :                         return r;
    1355             : 
    1356           0 :                 if (id == old_subvol_id) {
    1357             :                         /* The old subvolume was member of a qgroup
    1358             :                          * that had the same id, but a different level
    1359             :                          * as it self. Let's set up something similar
    1360             :                          * in the destination. */
    1361           0 :                         insert_intermediary_qgroup = true;
    1362           0 :                         break;
    1363             :                 }
    1364             : 
    1365           0 :                 for (j = 0; j < n_old_parent_qgroups; j++)
    1366           0 :                         if (old_parent_qgroups[j] == old_qgroups[i]) {
    1367             :                                 /* The old subvolume shared a common
    1368             :                                  * parent qgroup with its parent
    1369             :                                  * subvolume. Let's set up something
    1370             :                                  * similar in the destination. */
    1371           0 :                                 copy_from_parent = true;
    1372             :                         }
    1373             :         }
    1374             : 
    1375           0 :         if (!insert_intermediary_qgroup && !copy_from_parent)
    1376           0 :                 return 0;
    1377             : 
    1378           0 :         return btrfs_subvol_auto_qgroup_fd(fd, new_subvol_id, insert_intermediary_qgroup);
    1379             : }
    1380             : 
    1381           0 : static int copy_subtree_quota_limits(int fd, uint64_t old_subvol, uint64_t new_subvol) {
    1382             :         uint64_t old_subtree_qgroup, new_subtree_qgroup;
    1383             :         bool changed;
    1384             :         int r;
    1385             : 
    1386             :         /* First copy the leaf limits */
    1387           0 :         r = btrfs_qgroup_copy_limits(fd, old_subvol, new_subvol);
    1388           0 :         if (r < 0)
    1389           0 :                 return r;
    1390           0 :         changed = r > 0;
    1391             : 
    1392             :         /* Then, try to copy the subtree limits, if there are any. */
    1393           0 :         r = btrfs_subvol_find_subtree_qgroup(fd, old_subvol, &old_subtree_qgroup);
    1394           0 :         if (r < 0)
    1395           0 :                 return r;
    1396           0 :         if (r == 0)
    1397           0 :                 return changed;
    1398             : 
    1399           0 :         r = btrfs_subvol_find_subtree_qgroup(fd, new_subvol, &new_subtree_qgroup);
    1400           0 :         if (r < 0)
    1401           0 :                 return r;
    1402           0 :         if (r == 0)
    1403           0 :                 return changed;
    1404             : 
    1405           0 :         r = btrfs_qgroup_copy_limits(fd, old_subtree_qgroup, new_subtree_qgroup);
    1406           0 :         if (r != 0)
    1407           0 :                 return r;
    1408             : 
    1409           0 :         return changed;
    1410             : }
    1411             : 
    1412           0 : static int subvol_snapshot_children(
    1413             :                 int old_fd,
    1414             :                 int new_fd,
    1415             :                 const char *subvolume,
    1416             :                 uint64_t old_subvol_id,
    1417             :                 BtrfsSnapshotFlags flags) {
    1418             : 
    1419           0 :         struct btrfs_ioctl_search_args args = {
    1420             :                 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
    1421             : 
    1422             :                 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
    1423             :                 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
    1424             : 
    1425             :                 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
    1426             :                 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
    1427             : 
    1428             :                 .key.min_transid = 0,
    1429             :                 .key.max_transid = (uint64_t) -1,
    1430             :         };
    1431             : 
    1432           0 :         struct btrfs_ioctl_vol_args_v2 vol_args = {
    1433           0 :                 .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0,
    1434             :                 .fd = old_fd,
    1435             :         };
    1436           0 :         _cleanup_close_ int subvolume_fd = -1;
    1437             :         uint64_t new_subvol_id;
    1438             :         int r;
    1439             : 
    1440           0 :         assert(old_fd >= 0);
    1441           0 :         assert(new_fd >= 0);
    1442           0 :         assert(subvolume);
    1443             : 
    1444           0 :         strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
    1445             : 
    1446           0 :         if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0)
    1447           0 :                 return -errno;
    1448             : 
    1449           0 :         if (!(flags & BTRFS_SNAPSHOT_RECURSIVE) &&
    1450           0 :             !(flags & BTRFS_SNAPSHOT_QUOTA))
    1451           0 :                 return 0;
    1452             : 
    1453           0 :         if (old_subvol_id == 0) {
    1454           0 :                 r = btrfs_subvol_get_id_fd(old_fd, &old_subvol_id);
    1455           0 :                 if (r < 0)
    1456           0 :                         return r;
    1457             :         }
    1458             : 
    1459           0 :         r = btrfs_subvol_get_id(new_fd, vol_args.name, &new_subvol_id);
    1460           0 :         if (r < 0)
    1461           0 :                 return r;
    1462             : 
    1463           0 :         if (flags & BTRFS_SNAPSHOT_QUOTA)
    1464           0 :                 (void) copy_quota_hierarchy(new_fd, old_subvol_id, new_subvol_id);
    1465             : 
    1466           0 :         if (!(flags & BTRFS_SNAPSHOT_RECURSIVE)) {
    1467             : 
    1468           0 :                 if (flags & BTRFS_SNAPSHOT_QUOTA)
    1469           0 :                         (void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id);
    1470             : 
    1471           0 :                 return 0;
    1472             :         }
    1473             : 
    1474           0 :         args.key.min_offset = args.key.max_offset = old_subvol_id;
    1475             : 
    1476           0 :         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
    1477             :                 const struct btrfs_ioctl_search_header *sh;
    1478             :                 unsigned i;
    1479             : 
    1480           0 :                 args.key.nr_items = 256;
    1481           0 :                 if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
    1482           0 :                         return -errno;
    1483             : 
    1484           0 :                 if (args.key.nr_items <= 0)
    1485           0 :                         break;
    1486             : 
    1487           0 :                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
    1488           0 :                         _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL;
    1489             :                         struct btrfs_ioctl_ino_lookup_args ino_args;
    1490             :                         const struct btrfs_root_ref *ref;
    1491           0 :                         _cleanup_close_ int old_child_fd = -1, new_child_fd = -1;
    1492             : 
    1493           0 :                         btrfs_ioctl_search_args_set(&args, sh);
    1494             : 
    1495           0 :                         if (sh->type != BTRFS_ROOT_BACKREF_KEY)
    1496           0 :                                 continue;
    1497             : 
    1498             :                         /* Avoid finding the source subvolume a second
    1499             :                          * time */
    1500           0 :                         if (sh->offset != old_subvol_id)
    1501           0 :                                 continue;
    1502             : 
    1503             :                         /* Avoid running into loops if the new
    1504             :                          * subvolume is below the old one. */
    1505           0 :                         if (sh->objectid == new_subvol_id)
    1506           0 :                                 continue;
    1507             : 
    1508           0 :                         ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
    1509           0 :                         p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
    1510           0 :                         if (!p)
    1511           0 :                                 return -ENOMEM;
    1512             : 
    1513           0 :                         zero(ino_args);
    1514           0 :                         ino_args.treeid = old_subvol_id;
    1515           0 :                         ino_args.objectid = htole64(ref->dirid);
    1516             : 
    1517           0 :                         if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
    1518           0 :                                 return -errno;
    1519             : 
    1520           0 :                         c = path_join(ino_args.name, p);
    1521           0 :                         if (!c)
    1522           0 :                                 return -ENOMEM;
    1523             : 
    1524           0 :                         old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
    1525           0 :                         if (old_child_fd < 0)
    1526           0 :                                 return -errno;
    1527             : 
    1528           0 :                         np = path_join(subvolume, ino_args.name);
    1529           0 :                         if (!np)
    1530           0 :                                 return -ENOMEM;
    1531             : 
    1532           0 :                         new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
    1533           0 :                         if (new_child_fd < 0)
    1534           0 :                                 return -errno;
    1535             : 
    1536           0 :                         if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
    1537             :                                 /* If the snapshot is read-only we
    1538             :                                  * need to mark it writable
    1539             :                                  * temporarily, to put the subsnapshot
    1540             :                                  * into place. */
    1541             : 
    1542           0 :                                 if (subvolume_fd < 0) {
    1543           0 :                                         subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
    1544           0 :                                         if (subvolume_fd < 0)
    1545           0 :                                                 return -errno;
    1546             :                                 }
    1547             : 
    1548           0 :                                 r = btrfs_subvol_set_read_only_fd(subvolume_fd, false);
    1549           0 :                                 if (r < 0)
    1550           0 :                                         return r;
    1551             :                         }
    1552             : 
    1553             :                         /* When btrfs clones the subvolumes, child
    1554             :                          * subvolumes appear as empty directories. Remove
    1555             :                          * them, so that we can create a new snapshot
    1556             :                          * in their place */
    1557           0 :                         if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0) {
    1558           0 :                                 int k = -errno;
    1559             : 
    1560           0 :                                 if (flags & BTRFS_SNAPSHOT_READ_ONLY)
    1561           0 :                                         (void) btrfs_subvol_set_read_only_fd(subvolume_fd, true);
    1562             : 
    1563           0 :                                 return k;
    1564             :                         }
    1565             : 
    1566           0 :                         r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
    1567             : 
    1568             :                         /* Restore the readonly flag */
    1569           0 :                         if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
    1570             :                                 int k;
    1571             : 
    1572           0 :                                 k = btrfs_subvol_set_read_only_fd(subvolume_fd, true);
    1573           0 :                                 if (r >= 0 && k < 0)
    1574           0 :                                         return k;
    1575             :                         }
    1576             : 
    1577           0 :                         if (r < 0)
    1578           0 :                                 return r;
    1579             :                 }
    1580             : 
    1581             :                 /* Increase search key by one, to read the next item, if we can. */
    1582           0 :                 if (!btrfs_ioctl_search_args_inc(&args))
    1583           0 :                         break;
    1584             :         }
    1585             : 
    1586           0 :         if (flags & BTRFS_SNAPSHOT_QUOTA)
    1587           0 :                 (void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id);
    1588             : 
    1589           0 :         return 0;
    1590             : }
    1591             : 
    1592           0 : int btrfs_subvol_snapshot_fd_full(
    1593             :                 int old_fd,
    1594             :                 const char *new_path,
    1595             :                 BtrfsSnapshotFlags flags,
    1596             :                 copy_progress_path_t progress_path,
    1597             :                 copy_progress_bytes_t progress_bytes,
    1598             :                 void *userdata) {
    1599             : 
    1600           0 :         _cleanup_close_ int new_fd = -1;
    1601             :         const char *subvolume;
    1602             :         int r;
    1603             : 
    1604           0 :         assert(old_fd >= 0);
    1605           0 :         assert(new_path);
    1606             : 
    1607           0 :         r = btrfs_is_subvol_fd(old_fd);
    1608           0 :         if (r < 0)
    1609           0 :                 return r;
    1610           0 :         if (r == 0) {
    1611           0 :                 bool plain_directory = false;
    1612             : 
    1613             :                 /* If the source isn't a proper subvolume, fail unless fallback is requested */
    1614           0 :                 if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
    1615           0 :                         return -EISDIR;
    1616             : 
    1617           0 :                 r = btrfs_subvol_make(new_path);
    1618           0 :                 if (r == -ENOTTY && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) {
    1619             :                         /* If the destination doesn't support subvolumes, then use a plain directory, if that's requested. */
    1620           0 :                         if (mkdir(new_path, 0755) < 0)
    1621           0 :                                 return -errno;
    1622             : 
    1623           0 :                         plain_directory = true;
    1624           0 :                 } else if (r < 0)
    1625           0 :                         return r;
    1626             : 
    1627           0 :                 r = copy_directory_fd_full(old_fd, new_path, COPY_MERGE|COPY_REFLINK|COPY_SAME_MOUNT|(FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGINT) ? COPY_SIGINT : 0), progress_path, progress_bytes, userdata);
    1628           0 :                 if (r < 0)
    1629           0 :                         goto fallback_fail;
    1630             : 
    1631           0 :                 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
    1632             : 
    1633           0 :                         if (plain_directory) {
    1634             :                                 /* Plain directories have no recursive read-only flag, but something pretty close to
    1635             :                                  * it: the IMMUTABLE bit. Let's use this here, if this is requested. */
    1636             : 
    1637           0 :                                 if (flags & BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE)
    1638           0 :                                         (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
    1639             :                         } else {
    1640           0 :                                 r = btrfs_subvol_set_read_only(new_path, true);
    1641           0 :                                 if (r < 0)
    1642           0 :                                         goto fallback_fail;
    1643             :                         }
    1644             :                 }
    1645             : 
    1646           0 :                 return 0;
    1647             : 
    1648           0 :         fallback_fail:
    1649           0 :                 (void) rm_rf(new_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
    1650           0 :                 return r;
    1651             :         }
    1652             : 
    1653           0 :         r = extract_subvolume_name(new_path, &subvolume);
    1654           0 :         if (r < 0)
    1655           0 :                 return r;
    1656             : 
    1657           0 :         new_fd = open_parent(new_path, O_CLOEXEC, 0);
    1658           0 :         if (new_fd < 0)
    1659           0 :                 return new_fd;
    1660             : 
    1661           0 :         return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
    1662             : }
    1663             : 
    1664           0 : int btrfs_subvol_snapshot_full(
    1665             :                 const char *old_path,
    1666             :                 const char *new_path,
    1667             :                 BtrfsSnapshotFlags flags,
    1668             :                 copy_progress_path_t progress_path,
    1669             :                 copy_progress_bytes_t progress_bytes,
    1670             :                 void *userdata) {
    1671             : 
    1672           0 :         _cleanup_close_ int old_fd = -1;
    1673             : 
    1674           0 :         assert(old_path);
    1675           0 :         assert(new_path);
    1676             : 
    1677           0 :         old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
    1678           0 :         if (old_fd < 0)
    1679           0 :                 return -errno;
    1680             : 
    1681           0 :         return btrfs_subvol_snapshot_fd_full(old_fd, new_path, flags, progress_path, progress_bytes, userdata);
    1682             : }
    1683             : 
    1684           0 : int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret) {
    1685             : 
    1686           0 :         struct btrfs_ioctl_search_args args = {
    1687             :                 /* Tree of quota items */
    1688             :                 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
    1689             : 
    1690             :                 /* Look precisely for the quota relation items */
    1691             :                 .key.min_type = BTRFS_QGROUP_RELATION_KEY,
    1692             :                 .key.max_type = BTRFS_QGROUP_RELATION_KEY,
    1693             : 
    1694             :                 /* No restrictions on the other components */
    1695             :                 .key.min_offset = 0,
    1696             :                 .key.max_offset = (uint64_t) -1,
    1697             : 
    1698             :                 .key.min_transid = 0,
    1699             :                 .key.max_transid = (uint64_t) -1,
    1700             :         };
    1701             : 
    1702           0 :         _cleanup_free_ uint64_t *items = NULL;
    1703           0 :         size_t n_items = 0, n_allocated = 0;
    1704             :         int r;
    1705             : 
    1706           0 :         assert(fd >= 0);
    1707           0 :         assert(ret);
    1708             : 
    1709           0 :         if (qgroupid == 0) {
    1710           0 :                 r = btrfs_subvol_get_id_fd(fd, &qgroupid);
    1711           0 :                 if (r < 0)
    1712           0 :                         return r;
    1713             :         } else {
    1714           0 :                 r = btrfs_is_filesystem(fd);
    1715           0 :                 if (r < 0)
    1716           0 :                         return r;
    1717           0 :                 if (!r)
    1718           0 :                         return -ENOTTY;
    1719             :         }
    1720             : 
    1721           0 :         args.key.min_objectid = args.key.max_objectid = qgroupid;
    1722             : 
    1723           0 :         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
    1724             :                 const struct btrfs_ioctl_search_header *sh;
    1725             :                 unsigned i;
    1726             : 
    1727           0 :                 args.key.nr_items = 256;
    1728           0 :                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) {
    1729           0 :                         if (errno == ENOENT) /* quota tree missing: quota is disabled */
    1730           0 :                                 break;
    1731             : 
    1732           0 :                         return -errno;
    1733             :                 }
    1734             : 
    1735           0 :                 if (args.key.nr_items <= 0)
    1736           0 :                         break;
    1737             : 
    1738           0 :                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
    1739             : 
    1740             :                         /* Make sure we start the next search at least from this entry */
    1741           0 :                         btrfs_ioctl_search_args_set(&args, sh);
    1742             : 
    1743           0 :                         if (sh->type != BTRFS_QGROUP_RELATION_KEY)
    1744           0 :                                 continue;
    1745           0 :                         if (sh->offset < sh->objectid)
    1746           0 :                                 continue;
    1747           0 :                         if (sh->objectid != qgroupid)
    1748           0 :                                 continue;
    1749             : 
    1750           0 :                         if (!GREEDY_REALLOC(items, n_allocated, n_items+1))
    1751           0 :                                 return -ENOMEM;
    1752             : 
    1753           0 :                         items[n_items++] = sh->offset;
    1754             :                 }
    1755             : 
    1756             :                 /* Increase search key by one, to read the next item, if we can. */
    1757           0 :                 if (!btrfs_ioctl_search_args_inc(&args))
    1758           0 :                         break;
    1759             :         }
    1760             : 
    1761           0 :         if (n_items <= 0) {
    1762           0 :                 *ret = NULL;
    1763           0 :                 return 0;
    1764             :         }
    1765             : 
    1766           0 :         *ret = TAKE_PTR(items);
    1767             : 
    1768           0 :         return (int) n_items;
    1769             : }
    1770             : 
    1771           0 : int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool insert_intermediary_qgroup) {
    1772           0 :         _cleanup_free_ uint64_t *qgroups = NULL;
    1773             :         uint64_t parent_subvol;
    1774           0 :         bool changed = false;
    1775           0 :         int n = 0, r;
    1776             : 
    1777           0 :         assert(fd >= 0);
    1778             : 
    1779             :         /*
    1780             :          * Sets up the specified subvolume's qgroup automatically in
    1781             :          * one of two ways:
    1782             :          *
    1783             :          * If insert_intermediary_qgroup is false, the subvolume's
    1784             :          * leaf qgroup will be assigned to the same parent qgroups as
    1785             :          * the subvolume's parent subvolume.
    1786             :          *
    1787             :          * If insert_intermediary_qgroup is true a new intermediary
    1788             :          * higher-level qgroup is created, with a higher level number,
    1789             :          * but reusing the id of the subvolume. The level number is
    1790             :          * picked as one smaller than the lowest level qgroup the
    1791             :          * parent subvolume is a member of. If the parent subvolume's
    1792             :          * leaf qgroup is assigned to no higher-level qgroup a new
    1793             :          * qgroup of level 255 is created instead. Either way, the new
    1794             :          * qgroup is then assigned to the parent's higher-level
    1795             :          * qgroup, and the subvolume itself is assigned to it.
    1796             :          *
    1797             :          * If the subvolume is already assigned to a higher level
    1798             :          * qgroup, no operation is executed.
    1799             :          *
    1800             :          * Effectively this means: regardless if
    1801             :          * insert_intermediary_qgroup is true or not, after this
    1802             :          * function is invoked the subvolume will be accounted within
    1803             :          * the same qgroups as the parent. However, if it is true, it
    1804             :          * will also get its own higher-level qgroup, which may in
    1805             :          * turn be used by subvolumes created beneath this subvolume
    1806             :          * later on.
    1807             :          *
    1808             :          * This hence defines a simple default qgroup setup for
    1809             :          * subvolumes, as long as this function is invoked on each
    1810             :          * created subvolume: each subvolume is always accounting
    1811             :          * together with its immediate parents. Optionally, if
    1812             :          * insert_intermediary_qgroup is true, it will also get a
    1813             :          * qgroup that then includes all its own child subvolumes.
    1814             :          */
    1815             : 
    1816           0 :         if (subvol_id == 0) {
    1817           0 :                 r = btrfs_is_subvol_fd(fd);
    1818           0 :                 if (r < 0)
    1819           0 :                         return r;
    1820           0 :                 if (!r)
    1821           0 :                         return -ENOTTY;
    1822             : 
    1823           0 :                 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
    1824           0 :                 if (r < 0)
    1825           0 :                         return r;
    1826             :         }
    1827             : 
    1828           0 :         n = btrfs_qgroup_find_parents(fd, subvol_id, &qgroups);
    1829           0 :         if (n < 0)
    1830           0 :                 return n;
    1831           0 :         if (n > 0) /* already parent qgroups set up, let's bail */
    1832           0 :                 return 0;
    1833             : 
    1834           0 :         qgroups = mfree(qgroups);
    1835             : 
    1836           0 :         r = btrfs_subvol_get_parent(fd, subvol_id, &parent_subvol);
    1837           0 :         if (r == -ENXIO)
    1838             :                 /* No parent, hence no qgroup memberships */
    1839           0 :                 n = 0;
    1840           0 :         else if (r < 0)
    1841           0 :                 return r;
    1842             :         else {
    1843           0 :                 n = btrfs_qgroup_find_parents(fd, parent_subvol, &qgroups);
    1844           0 :                 if (n < 0)
    1845           0 :                         return n;
    1846             :         }
    1847             : 
    1848           0 :         if (insert_intermediary_qgroup) {
    1849           0 :                 uint64_t lowest = 256, new_qgroupid;
    1850           0 :                 bool created = false;
    1851             :                 int i;
    1852             : 
    1853             :                 /* Determine the lowest qgroup that the parent
    1854             :                  * subvolume is assigned to. */
    1855             : 
    1856           0 :                 for (i = 0; i < n; i++) {
    1857             :                         uint64_t level;
    1858             : 
    1859           0 :                         r = btrfs_qgroupid_split(qgroups[i], &level, NULL);
    1860           0 :                         if (r < 0)
    1861           0 :                                 return r;
    1862             : 
    1863           0 :                         if (level < lowest)
    1864           0 :                                 lowest = level;
    1865             :                 }
    1866             : 
    1867           0 :                 if (lowest <= 1) /* There are no levels left we could use insert an intermediary qgroup at */
    1868           0 :                         return -EBUSY;
    1869             : 
    1870           0 :                 r = btrfs_qgroupid_make(lowest - 1, subvol_id, &new_qgroupid);
    1871           0 :                 if (r < 0)
    1872           0 :                         return r;
    1873             : 
    1874             :                 /* Create the new intermediary group, unless it already exists */
    1875           0 :                 r = btrfs_qgroup_create(fd, new_qgroupid);
    1876           0 :                 if (r < 0 && r != -EEXIST)
    1877           0 :                         return r;
    1878           0 :                 if (r >= 0)
    1879           0 :                         changed = created = true;
    1880             : 
    1881           0 :                 for (i = 0; i < n; i++) {
    1882           0 :                         r = btrfs_qgroup_assign(fd, new_qgroupid, qgroups[i]);
    1883           0 :                         if (r < 0 && r != -EEXIST) {
    1884           0 :                                 if (created)
    1885           0 :                                         (void) btrfs_qgroup_destroy_recursive(fd, new_qgroupid);
    1886             : 
    1887           0 :                                 return r;
    1888             :                         }
    1889           0 :                         if (r >= 0)
    1890           0 :                                 changed = true;
    1891             :                 }
    1892             : 
    1893           0 :                 r = btrfs_qgroup_assign(fd, subvol_id, new_qgroupid);
    1894           0 :                 if (r < 0 && r != -EEXIST) {
    1895           0 :                         if (created)
    1896           0 :                                 (void) btrfs_qgroup_destroy_recursive(fd, new_qgroupid);
    1897           0 :                         return r;
    1898             :                 }
    1899           0 :                 if (r >= 0)
    1900           0 :                         changed = true;
    1901             : 
    1902             :         } else {
    1903             :                 int i;
    1904             : 
    1905             :                 /* Assign our subvolume to all the same qgroups as the parent */
    1906             : 
    1907           0 :                 for (i = 0; i < n; i++) {
    1908           0 :                         r = btrfs_qgroup_assign(fd, subvol_id, qgroups[i]);
    1909           0 :                         if (r < 0 && r != -EEXIST)
    1910           0 :                                 return r;
    1911           0 :                         if (r >= 0)
    1912           0 :                                 changed = true;
    1913             :                 }
    1914             :         }
    1915             : 
    1916           0 :         return changed;
    1917             : }
    1918             : 
    1919           0 : int btrfs_subvol_auto_qgroup(const char *path, uint64_t subvol_id, bool create_intermediary_qgroup) {
    1920           0 :         _cleanup_close_ int fd = -1;
    1921             : 
    1922           0 :         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
    1923           0 :         if (fd < 0)
    1924           0 :                 return -errno;
    1925             : 
    1926           0 :         return btrfs_subvol_auto_qgroup_fd(fd, subvol_id, create_intermediary_qgroup);
    1927             : }
    1928             : 
    1929           0 : int btrfs_subvol_get_parent(int fd, uint64_t subvol_id, uint64_t *ret) {
    1930             : 
    1931           0 :         struct btrfs_ioctl_search_args args = {
    1932             :                 /* Tree of tree roots */
    1933             :                 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
    1934             : 
    1935             :                 /* Look precisely for the subvolume items */
    1936             :                 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
    1937             :                 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
    1938             : 
    1939             :                 /* No restrictions on the other components */
    1940             :                 .key.min_offset = 0,
    1941             :                 .key.max_offset = (uint64_t) -1,
    1942             : 
    1943             :                 .key.min_transid = 0,
    1944             :                 .key.max_transid = (uint64_t) -1,
    1945             :         };
    1946             :         int r;
    1947             : 
    1948           0 :         assert(fd >= 0);
    1949           0 :         assert(ret);
    1950             : 
    1951           0 :         if (subvol_id == 0) {
    1952           0 :                 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
    1953           0 :                 if (r < 0)
    1954           0 :                         return r;
    1955             :         } else {
    1956           0 :                 r = btrfs_is_filesystem(fd);
    1957           0 :                 if (r < 0)
    1958           0 :                         return r;
    1959           0 :                 if (!r)
    1960           0 :                         return -ENOTTY;
    1961             :         }
    1962             : 
    1963           0 :         args.key.min_objectid = args.key.max_objectid = subvol_id;
    1964             : 
    1965           0 :         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
    1966             :                 const struct btrfs_ioctl_search_header *sh;
    1967             :                 unsigned i;
    1968             : 
    1969           0 :                 args.key.nr_items = 256;
    1970           0 :                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
    1971           0 :                         return negative_errno();
    1972             : 
    1973           0 :                 if (args.key.nr_items <= 0)
    1974           0 :                         break;
    1975             : 
    1976           0 :                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
    1977             : 
    1978           0 :                         if (sh->type != BTRFS_ROOT_BACKREF_KEY)
    1979           0 :                                 continue;
    1980           0 :                         if (sh->objectid != subvol_id)
    1981           0 :                                 continue;
    1982             : 
    1983           0 :                         *ret = sh->offset;
    1984           0 :                         return 0;
    1985             :                 }
    1986             :         }
    1987             : 
    1988           0 :         return -ENXIO;
    1989             : }

Generated by: LCOV version 1.14