LCOV - code coverage report
Current view: top level - basic - btrfs-util.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 18 599 3.0 %
Date: 2015-07-29 18:47:03 Functions: 3 35 8.6 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright 2014 Lennart Poettering
       7             : 
       8             :   systemd is free software; you can redistribute it and/or modify it
       9             :   under the terms of the GNU Lesser General Public License as published by
      10             :   the Free Software Foundation; either version 2.1 of the License, or
      11             :   (at your option) any later version.
      12             : 
      13             :   systemd is distributed in the hope that it will be useful, but
      14             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      16             :   Lesser General Public License for more details.
      17             : 
      18             :   You should have received a copy of the GNU Lesser General Public License
      19             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      20             : ***/
      21             : 
      22             : #include <stdlib.h>
      23             : #include <sys/vfs.h>
      24             : #include <sys/stat.h>
      25             : 
      26             : #ifdef HAVE_LINUX_BTRFS_H
      27             : #include <linux/btrfs.h>
      28             : #endif
      29             : 
      30             : #include "missing.h"
      31             : #include "util.h"
      32             : #include "path-util.h"
      33             : #include "macro.h"
      34             : #include "copy.h"
      35             : #include "selinux-util.h"
      36             : #include "smack-util.h"
      37             : #include "fileio.h"
      38             : #include "btrfs-ctree.h"
      39             : #include "btrfs-util.h"
      40             : 
      41             : /* WARNING: Be careful with file system ioctls! When we get an fd, we
      42             :  * need to make sure it either refers to only a regular file or
      43             :  * directory, or that it is located on btrfs, before invoking any
      44             :  * btrfs ioctls. The ioctl numbers are reused by some device drivers
      45             :  * (such as DRM), and hence might have bad effects when invoked on
      46             :  * device nodes (that reference drivers) rather than fds to normal
      47             :  * files or directories. */
      48             : 
      49           0 : static int validate_subvolume_name(const char *name) {
      50             : 
      51           0 :         if (!filename_is_valid(name))
      52           0 :                 return -EINVAL;
      53             : 
      54           0 :         if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
      55           0 :                 return -E2BIG;
      56             : 
      57           0 :         return 0;
      58             : }
      59             : 
      60           0 : static int open_parent(const char *path, int flags) {
      61           0 :         _cleanup_free_ char *parent = NULL;
      62             :         int r, fd;
      63             : 
      64           0 :         assert(path);
      65             : 
      66           0 :         r = path_get_parent(path, &parent);
      67           0 :         if (r < 0)
      68           0 :                 return r;
      69             : 
      70           0 :         fd = open(parent, flags);
      71           0 :         if (fd < 0)
      72           0 :                 return -errno;
      73             : 
      74           0 :         return fd;
      75             : }
      76             : 
      77           0 : static int extract_subvolume_name(const char *path, const char **subvolume) {
      78             :         const char *fn;
      79             :         int r;
      80             : 
      81           0 :         assert(path);
      82           0 :         assert(subvolume);
      83             : 
      84           0 :         fn = basename(path);
      85             : 
      86           0 :         r = validate_subvolume_name(fn);
      87           0 :         if (r < 0)
      88           0 :                 return r;
      89             : 
      90           0 :         *subvolume = fn;
      91           0 :         return 0;
      92             : }
      93             : 
      94          18 : int btrfs_is_filesystem(int fd) {
      95             :         struct statfs sfs;
      96             : 
      97          18 :         assert(fd >= 0);
      98             : 
      99          18 :         if (fstatfs(fd, &sfs) < 0)
     100           0 :                 return -errno;
     101             : 
     102          18 :         return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
     103             : }
     104             : 
     105           0 : int btrfs_is_subvol(int fd) {
     106             :         struct stat st;
     107             : 
     108           0 :         assert(fd >= 0);
     109             : 
     110             :         /* On btrfs subvolumes always have the inode 256 */
     111             : 
     112           0 :         if (fstat(fd, &st) < 0)
     113           0 :                 return -errno;
     114             : 
     115           0 :         if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
     116           0 :                 return 0;
     117             : 
     118           0 :         return btrfs_is_filesystem(fd);
     119             : }
     120             : 
     121           0 : int btrfs_subvol_make(const char *path) {
     122           0 :         struct btrfs_ioctl_vol_args args = {};
     123           0 :         _cleanup_close_ int fd = -1;
     124             :         const char *subvolume;
     125             :         int r;
     126             : 
     127           0 :         assert(path);
     128             : 
     129           0 :         r = extract_subvolume_name(path, &subvolume);
     130           0 :         if (r < 0)
     131           0 :                 return r;
     132             : 
     133           0 :         fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
     134           0 :         if (fd < 0)
     135           0 :                 return fd;
     136             : 
     137           0 :         strncpy(args.name, subvolume, sizeof(args.name)-1);
     138             : 
     139           0 :         if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
     140           0 :                 return -errno;
     141             : 
     142           0 :         return 0;
     143             : }
     144             : 
     145           0 : int btrfs_subvol_make_label(const char *path) {
     146             :         int r;
     147             : 
     148           0 :         assert(path);
     149             : 
     150           0 :         r = mac_selinux_create_file_prepare(path, S_IFDIR);
     151           0 :         if (r < 0)
     152           0 :                 return r;
     153             : 
     154           0 :         r = btrfs_subvol_make(path);
     155           0 :         mac_selinux_create_file_clear();
     156             : 
     157           0 :         if (r < 0)
     158           0 :                 return r;
     159             : 
     160           0 :         return mac_smack_fix(path, false, false);
     161             : }
     162             : 
     163           0 : int btrfs_subvol_set_read_only_fd(int fd, bool b) {
     164             :         uint64_t flags, nflags;
     165             :         struct stat st;
     166             : 
     167           0 :         assert(fd >= 0);
     168             : 
     169           0 :         if (fstat(fd, &st) < 0)
     170           0 :                 return -errno;
     171             : 
     172           0 :         if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
     173           0 :                 return -EINVAL;
     174             : 
     175           0 :         if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
     176           0 :                 return -errno;
     177             : 
     178           0 :         if (b)
     179           0 :                 nflags = flags | BTRFS_SUBVOL_RDONLY;
     180             :         else
     181           0 :                 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
     182             : 
     183           0 :         if (flags == nflags)
     184           0 :                 return 0;
     185             : 
     186           0 :         if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
     187           0 :                 return -errno;
     188             : 
     189           0 :         return 0;
     190             : }
     191             : 
     192           0 : int btrfs_subvol_set_read_only(const char *path, bool b) {
     193           0 :         _cleanup_close_ int fd = -1;
     194             : 
     195           0 :         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
     196           0 :         if (fd < 0)
     197           0 :                 return -errno;
     198             : 
     199           0 :         return btrfs_subvol_set_read_only_fd(fd, b);
     200             : }
     201             : 
     202           0 : int btrfs_subvol_get_read_only_fd(int fd) {
     203             :         uint64_t flags;
     204             :         struct stat st;
     205             : 
     206           0 :         assert(fd >= 0);
     207             : 
     208           0 :         if (fstat(fd, &st) < 0)
     209           0 :                 return -errno;
     210             : 
     211           0 :         if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
     212           0 :                 return -EINVAL;
     213             : 
     214           0 :         if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
     215           0 :                 return -errno;
     216             : 
     217           0 :         return !!(flags & BTRFS_SUBVOL_RDONLY);
     218             : }
     219             : 
     220           6 : int btrfs_reflink(int infd, int outfd) {
     221             :         struct stat st;
     222             :         int r;
     223             : 
     224           6 :         assert(infd >= 0);
     225           6 :         assert(outfd >= 0);
     226             : 
     227             :         /* Make sure we invoke the ioctl on a regular file, so that no
     228             :          * device driver accidentally gets it. */
     229             : 
     230           6 :         if (fstat(outfd, &st) < 0)
     231           0 :                 return -errno;
     232             : 
     233           6 :         if (!S_ISREG(st.st_mode))
     234           0 :                 return -EINVAL;
     235             : 
     236           6 :         r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
     237           6 :         if (r < 0)
     238           6 :                 return -errno;
     239             : 
     240           0 :         return 0;
     241             : }
     242             : 
     243           0 : int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
     244           0 :         struct btrfs_ioctl_clone_range_args args = {
     245             :                 .src_fd = infd,
     246             :                 .src_offset = in_offset,
     247             :                 .src_length = sz,
     248             :                 .dest_offset = out_offset,
     249             :         };
     250             :         struct stat st;
     251             :         int r;
     252             : 
     253           0 :         assert(infd >= 0);
     254           0 :         assert(outfd >= 0);
     255           0 :         assert(sz > 0);
     256             : 
     257           0 :         if (fstat(outfd, &st) < 0)
     258           0 :                 return -errno;
     259             : 
     260           0 :         if (!S_ISREG(st.st_mode))
     261           0 :                 return -EINVAL;
     262             : 
     263           0 :         r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
     264           0 :         if (r < 0)
     265           0 :                 return -errno;
     266             : 
     267           0 :         return 0;
     268             : }
     269             : 
     270           0 : int btrfs_get_block_device_fd(int fd, dev_t *dev) {
     271           0 :         struct btrfs_ioctl_fs_info_args fsi = {};
     272             :         uint64_t id;
     273             :         int r;
     274             : 
     275           0 :         assert(fd >= 0);
     276           0 :         assert(dev);
     277             : 
     278           0 :         r = btrfs_is_filesystem(fd);
     279           0 :         if (r < 0)
     280           0 :                 return r;
     281           0 :         if (!r)
     282           0 :                 return -ENOTTY;
     283             : 
     284           0 :         if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
     285           0 :                 return -errno;
     286             : 
     287             :         /* We won't do this for btrfs RAID */
     288           0 :         if (fsi.num_devices != 1)
     289           0 :                 return 0;
     290             : 
     291           0 :         for (id = 1; id <= fsi.max_id; id++) {
     292           0 :                 struct btrfs_ioctl_dev_info_args di = {
     293             :                         .devid = id,
     294             :                 };
     295             :                 struct stat st;
     296             : 
     297           0 :                 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
     298           0 :                         if (errno == ENODEV)
     299           0 :                                 continue;
     300             : 
     301           0 :                         return -errno;
     302             :                 }
     303             : 
     304           0 :                 if (stat((char*) di.path, &st) < 0)
     305           0 :                         return -errno;
     306             : 
     307           0 :                 if (!S_ISBLK(st.st_mode))
     308           0 :                         return -ENODEV;
     309             : 
     310           0 :                 if (major(st.st_rdev) == 0)
     311           0 :                         return -ENODEV;
     312             : 
     313           0 :                 *dev = st.st_rdev;
     314           0 :                 return 1;
     315             :         }
     316             : 
     317           0 :         return -ENODEV;
     318             : }
     319             : 
     320           0 : int btrfs_get_block_device(const char *path, dev_t *dev) {
     321           0 :         _cleanup_close_ int fd = -1;
     322             : 
     323           0 :         assert(path);
     324           0 :         assert(dev);
     325             : 
     326           0 :         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
     327           0 :         if (fd < 0)
     328           0 :                 return -errno;
     329             : 
     330           0 :         return btrfs_get_block_device_fd(fd, dev);
     331             : }
     332             : 
     333           0 : int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
     334           0 :         struct btrfs_ioctl_ino_lookup_args args = {
     335             :                 .objectid = BTRFS_FIRST_FREE_OBJECTID
     336             :         };
     337             :         int r;
     338             : 
     339           0 :         assert(fd >= 0);
     340           0 :         assert(ret);
     341             : 
     342           0 :         r = btrfs_is_filesystem(fd);
     343           0 :         if (r < 0)
     344           0 :                 return r;
     345           0 :         if (!r)
     346           0 :                 return -ENOTTY;
     347             : 
     348           0 :         if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
     349           0 :                 return -errno;
     350             : 
     351           0 :         *ret = args.treeid;
     352           0 :         return 0;
     353             : }
     354             : 
     355           0 : int btrfs_subvol_get_id(int fd, const char *subvol, uint64_t *ret) {
     356           0 :         _cleanup_close_ int subvol_fd = -1;
     357             : 
     358           0 :         assert(fd >= 0);
     359           0 :         assert(ret);
     360             : 
     361           0 :         subvol_fd = openat(fd, subvol, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
     362           0 :         if (subvol_fd < 0)
     363           0 :                 return -errno;
     364             : 
     365           0 :         return btrfs_subvol_get_id_fd(subvol_fd, ret);
     366             : }
     367             : 
     368           0 : static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
     369           0 :         assert(args);
     370             : 
     371             :         /* the objectid, type, offset together make up the btrfs key,
     372             :          * which is considered a single 136byte integer when
     373             :          * comparing. This call increases the counter by one, dealing
     374             :          * with the overflow between the overflows */
     375             : 
     376           0 :         if (args->key.min_offset < (uint64_t) -1) {
     377           0 :                 args->key.min_offset++;
     378           0 :                 return true;
     379             :         }
     380             : 
     381           0 :         if (args->key.min_type < (uint8_t) -1) {
     382           0 :                 args->key.min_type++;
     383           0 :                 args->key.min_offset = 0;
     384           0 :                 return true;
     385             :         }
     386             : 
     387           0 :         if (args->key.min_objectid < (uint64_t) -1) {
     388           0 :                 args->key.min_objectid++;
     389           0 :                 args->key.min_offset = 0;
     390           0 :                 args->key.min_type = 0;
     391           0 :                 return true;
     392             :         }
     393             : 
     394           0 :         return 0;
     395             : }
     396             : 
     397           0 : static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
     398           0 :         assert(args);
     399           0 :         assert(h);
     400             : 
     401           0 :         args->key.min_objectid = h->objectid;
     402           0 :         args->key.min_type = h->type;
     403           0 :         args->key.min_offset = h->offset;
     404           0 : }
     405             : 
     406           0 : static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
     407           0 :         assert(args);
     408             : 
     409             :         /* Compare min and max */
     410             : 
     411           0 :         if (args->key.min_objectid < args->key.max_objectid)
     412           0 :                 return -1;
     413           0 :         if (args->key.min_objectid > args->key.max_objectid)
     414           0 :                 return 1;
     415             : 
     416           0 :         if (args->key.min_type < args->key.max_type)
     417           0 :                 return -1;
     418           0 :         if (args->key.min_type > args->key.max_type)
     419           0 :                 return 1;
     420             : 
     421           0 :         if (args->key.min_offset < args->key.max_offset)
     422           0 :                 return -1;
     423           0 :         if (args->key.min_offset > args->key.max_offset)
     424           0 :                 return 1;
     425             : 
     426           0 :         return 0;
     427             : }
     428             : 
     429             : #define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args)                  \
     430             :         for ((i) = 0,                                                   \
     431             :              (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
     432             :              (i) < (args).key.nr_items;                                 \
     433             :              (i)++,                                                     \
     434             :              (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
     435             : 
     436             : #define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh)                              \
     437             :         ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
     438             : 
     439           0 : int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
     440           0 :         struct btrfs_ioctl_search_args args = {
     441             :                 /* Tree of tree roots */
     442             :                 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
     443             : 
     444             :                 /* Look precisely for the subvolume items */
     445             :                 .key.min_type = BTRFS_ROOT_ITEM_KEY,
     446             :                 .key.max_type = BTRFS_ROOT_ITEM_KEY,
     447             : 
     448             :                 .key.min_offset = 0,
     449             :                 .key.max_offset = (uint64_t) -1,
     450             : 
     451             :                 /* No restrictions on the other components */
     452             :                 .key.min_transid = 0,
     453             :                 .key.max_transid = (uint64_t) -1,
     454             :         };
     455             : 
     456             :         uint64_t subvol_id;
     457           0 :         bool found = false;
     458             :         int r;
     459             : 
     460           0 :         assert(fd >= 0);
     461           0 :         assert(ret);
     462             : 
     463           0 :         r = btrfs_subvol_get_id_fd(fd, &subvol_id);
     464           0 :         if (r < 0)
     465           0 :                 return r;
     466             : 
     467           0 :         args.key.min_objectid = args.key.max_objectid = subvol_id;
     468             : 
     469           0 :         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
     470             :                 const struct btrfs_ioctl_search_header *sh;
     471             :                 unsigned i;
     472             : 
     473           0 :                 args.key.nr_items = 256;
     474           0 :                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
     475           0 :                         return -errno;
     476             : 
     477           0 :                 if (args.key.nr_items <= 0)
     478           0 :                         break;
     479             : 
     480           0 :                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
     481             : 
     482             :                         const struct btrfs_root_item *ri;
     483             : 
     484             :                         /* Make sure we start the next search at least from this entry */
     485           0 :                         btrfs_ioctl_search_args_set(&args, sh);
     486             : 
     487           0 :                         if (sh->objectid != subvol_id)
     488           0 :                                 continue;
     489           0 :                         if (sh->type != BTRFS_ROOT_ITEM_KEY)
     490           0 :                                 continue;
     491             : 
     492             :                         /* Older versions of the struct lacked the otime setting */
     493           0 :                         if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
     494           0 :                                 continue;
     495             : 
     496           0 :                         ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
     497             : 
     498           0 :                         ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
     499           0 :                                 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
     500             : 
     501           0 :                         ret->subvol_id = subvol_id;
     502           0 :                         ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
     503             : 
     504             :                         assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
     505           0 :                         memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
     506           0 :                         memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
     507             : 
     508           0 :                         found = true;
     509           0 :                         goto finish;
     510             :                 }
     511             : 
     512             :                 /* Increase search key by one, to read the next item, if we can. */
     513           0 :                 if (!btrfs_ioctl_search_args_inc(&args))
     514           0 :                         break;
     515             :         }
     516             : 
     517             : finish:
     518           0 :         if (!found)
     519           0 :                 return -ENODATA;
     520             : 
     521           0 :         return 0;
     522             : }
     523             : 
     524           0 : int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
     525             : 
     526           0 :         struct btrfs_ioctl_search_args args = {
     527             :                 /* Tree of quota items */
     528             :                 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
     529             : 
     530             :                 /* The object ID is always 0 */
     531             :                 .key.min_objectid = 0,
     532             :                 .key.max_objectid = 0,
     533             : 
     534             :                 /* Look precisely for the quota items */
     535             :                 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
     536             :                 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
     537             : 
     538             :                 /* No restrictions on the other components */
     539             :                 .key.min_transid = 0,
     540             :                 .key.max_transid = (uint64_t) -1,
     541             :         };
     542             : 
     543             :         uint64_t subvol_id;
     544           0 :         bool found_info = false, found_limit = false;
     545             :         int r;
     546             : 
     547           0 :         assert(fd >= 0);
     548           0 :         assert(ret);
     549             : 
     550           0 :         r = btrfs_subvol_get_id_fd(fd, &subvol_id);
     551           0 :         if (r < 0)
     552           0 :                 return r;
     553             : 
     554           0 :         args.key.min_offset = args.key.max_offset = subvol_id;
     555             : 
     556           0 :         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
     557             :                 const struct btrfs_ioctl_search_header *sh;
     558             :                 unsigned i;
     559             : 
     560           0 :                 args.key.nr_items = 256;
     561           0 :                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
     562           0 :                         return -errno;
     563             : 
     564           0 :                 if (args.key.nr_items <= 0)
     565           0 :                         break;
     566             : 
     567           0 :                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
     568             : 
     569             :                         /* Make sure we start the next search at least from this entry */
     570           0 :                         btrfs_ioctl_search_args_set(&args, sh);
     571             : 
     572           0 :                         if (sh->objectid != 0)
     573           0 :                                 continue;
     574           0 :                         if (sh->offset != subvol_id)
     575           0 :                                 continue;
     576             : 
     577           0 :                         if (sh->type == BTRFS_QGROUP_INFO_KEY) {
     578           0 :                                 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
     579             : 
     580           0 :                                 ret->referenced = le64toh(qii->rfer);
     581           0 :                                 ret->exclusive = le64toh(qii->excl);
     582             : 
     583           0 :                                 found_info = true;
     584             : 
     585           0 :                         } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
     586           0 :                                 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
     587             : 
     588           0 :                                 ret->referenced_max = le64toh(qli->max_rfer);
     589           0 :                                 ret->exclusive_max = le64toh(qli->max_excl);
     590             : 
     591           0 :                                 if (ret->referenced_max == 0)
     592           0 :                                         ret->referenced_max = (uint64_t) -1;
     593           0 :                                 if (ret->exclusive_max == 0)
     594           0 :                                         ret->exclusive_max = (uint64_t) -1;
     595             : 
     596           0 :                                 found_limit = true;
     597             :                         }
     598             : 
     599           0 :                         if (found_info && found_limit)
     600           0 :                                 goto finish;
     601             :                 }
     602             : 
     603             :                 /* Increase search key by one, to read the next item, if we can. */
     604           0 :                 if (!btrfs_ioctl_search_args_inc(&args))
     605           0 :                         break;
     606             :         }
     607             : 
     608             : finish:
     609           0 :         if (!found_limit && !found_info)
     610           0 :                 return -ENODATA;
     611             : 
     612           0 :         if (!found_info) {
     613           0 :                 ret->referenced = (uint64_t) -1;
     614           0 :                 ret->exclusive = (uint64_t) -1;
     615             :         }
     616             : 
     617           0 :         if (!found_limit) {
     618           0 :                 ret->referenced_max = (uint64_t) -1;
     619           0 :                 ret->exclusive_max = (uint64_t) -1;
     620             :         }
     621             : 
     622           0 :         return 0;
     623             : }
     624             : 
     625           2 : int btrfs_defrag_fd(int fd) {
     626             :         struct stat st;
     627             : 
     628           2 :         assert(fd >= 0);
     629             : 
     630           2 :         if (fstat(fd, &st) < 0)
     631           0 :                 return -errno;
     632             : 
     633           2 :         if (!S_ISREG(st.st_mode))
     634           0 :                 return -EINVAL;
     635             : 
     636           2 :         if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
     637           2 :                 return -errno;
     638             : 
     639           0 :         return 0;
     640             : }
     641             : 
     642           0 : int btrfs_defrag(const char *p) {
     643           0 :         _cleanup_close_ int fd = -1;
     644             : 
     645           0 :         fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
     646           0 :         if (fd < 0)
     647           0 :                 return -errno;
     648             : 
     649           0 :         return btrfs_defrag_fd(fd);
     650             : }
     651             : 
     652           0 : int btrfs_quota_enable_fd(int fd, bool b) {
     653           0 :         struct btrfs_ioctl_quota_ctl_args args = {
     654             :                 .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
     655             :         };
     656             :         int r;
     657             : 
     658           0 :         assert(fd >= 0);
     659             : 
     660           0 :         r = btrfs_is_filesystem(fd);
     661           0 :         if (r < 0)
     662           0 :                 return r;
     663           0 :         if (!r)
     664           0 :                 return -ENOTTY;
     665             : 
     666           0 :         if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
     667           0 :                 return -errno;
     668             : 
     669           0 :         return 0;
     670             : }
     671             : 
     672           0 : int btrfs_quota_enable(const char *path, bool b) {
     673           0 :         _cleanup_close_ int fd = -1;
     674             : 
     675           0 :         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
     676           0 :         if (fd < 0)
     677           0 :                 return -errno;
     678             : 
     679           0 :         return btrfs_quota_enable_fd(fd, b);
     680             : }
     681             : 
     682           0 : int btrfs_quota_limit_fd(int fd, uint64_t referenced_max) {
     683           0 :         struct btrfs_ioctl_qgroup_limit_args args = {
     684             :                 .lim.max_rfer =
     685             :                         referenced_max == (uint64_t) -1 ? 0 :
     686             :                         referenced_max == 0 ? 1 : referenced_max,
     687             :                 .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
     688             :         };
     689             :         int r;
     690             : 
     691           0 :         assert(fd >= 0);
     692             : 
     693           0 :         r = btrfs_is_filesystem(fd);
     694           0 :         if (r < 0)
     695           0 :                 return r;
     696           0 :         if (!r)
     697           0 :                 return -ENOTTY;
     698             : 
     699           0 :         if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0)
     700           0 :                 return -errno;
     701             : 
     702           0 :         return 0;
     703             : }
     704             : 
     705           0 : int btrfs_quota_limit(const char *path, uint64_t referenced_max) {
     706           0 :         _cleanup_close_ int fd = -1;
     707             : 
     708           0 :         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
     709           0 :         if (fd < 0)
     710           0 :                 return -errno;
     711             : 
     712           0 :         return btrfs_quota_limit_fd(fd, referenced_max);
     713             : }
     714             : 
     715           0 : int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
     716           0 :         struct btrfs_ioctl_vol_args args = {};
     717           0 :         _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
     718           0 :         _cleanup_close_ int loop_fd = -1, backing_fd = -1;
     719             :         struct stat st;
     720           0 :         dev_t dev = 0;
     721             :         int r;
     722             : 
     723             :         /* btrfs cannot handle file systems < 16M, hence use this as minimum */
     724           0 :         if (new_size < 16*1024*1024)
     725           0 :                 new_size = 16*1024*1024;
     726             : 
     727           0 :         r = btrfs_get_block_device_fd(fd, &dev);
     728           0 :         if (r < 0)
     729           0 :                 return r;
     730           0 :         if (r == 0)
     731           0 :                 return -ENODEV;
     732             : 
     733           0 :         if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
     734           0 :                 return -ENOMEM;
     735           0 :         r = read_one_line_file(p, &backing);
     736           0 :         if (r == -ENOENT)
     737           0 :                 return -ENODEV;
     738           0 :         if (r < 0)
     739           0 :                 return r;
     740           0 :         if (isempty(backing) || !path_is_absolute(backing))
     741           0 :                 return -ENODEV;
     742             : 
     743           0 :         backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
     744           0 :         if (backing_fd < 0)
     745           0 :                 return -errno;
     746             : 
     747           0 :         if (fstat(backing_fd, &st) < 0)
     748           0 :                 return -errno;
     749           0 :         if (!S_ISREG(st.st_mode))
     750           0 :                 return -ENODEV;
     751             : 
     752           0 :         if (new_size == (uint64_t) st.st_size)
     753           0 :                 return 0;
     754             : 
     755           0 :         if (grow_only && new_size < (uint64_t) st.st_size)
     756           0 :                 return -EINVAL;
     757             : 
     758           0 :         if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
     759           0 :                 return -ENOMEM;
     760           0 :         loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
     761           0 :         if (loop_fd < 0)
     762           0 :                 return -errno;
     763             : 
     764           0 :         if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
     765           0 :                 return -EINVAL;
     766             : 
     767           0 :         if (new_size < (uint64_t) st.st_size) {
     768             :                 /* Decrease size: first decrease btrfs size, then shorten loopback */
     769           0 :                 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
     770           0 :                         return -errno;
     771             :         }
     772             : 
     773           0 :         if (ftruncate(backing_fd, new_size) < 0)
     774           0 :                 return -errno;
     775             : 
     776           0 :         if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
     777           0 :                 return -errno;
     778             : 
     779           0 :         if (new_size > (uint64_t) st.st_size) {
     780             :                 /* Increase size: first enlarge loopback, then increase btrfs size */
     781           0 :                 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
     782           0 :                         return -errno;
     783             :         }
     784             : 
     785             :         /* Make sure the free disk space is correctly updated for both file systems */
     786           0 :         (void) fsync(fd);
     787           0 :         (void) fsync(backing_fd);
     788             : 
     789           0 :         return 1;
     790             : }
     791             : 
     792           0 : int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
     793           0 :         _cleanup_close_ int fd = -1;
     794             : 
     795           0 :         fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
     796           0 :         if (fd < 0)
     797           0 :                 return -errno;
     798             : 
     799           0 :         return btrfs_resize_loopback_fd(fd, new_size, grow_only);
     800             : }
     801             : 
     802           0 : static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, bool recursive) {
     803           0 :         struct btrfs_ioctl_search_args args = {
     804             :                 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
     805             : 
     806             :                 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
     807             :                 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
     808             : 
     809             :                 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
     810             :                 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
     811             : 
     812             :                 .key.min_transid = 0,
     813             :                 .key.max_transid = (uint64_t) -1,
     814             :         };
     815             : 
     816           0 :         struct btrfs_ioctl_vol_args vol_args = {};
     817           0 :         _cleanup_close_ int subvol_fd = -1;
     818             :         struct stat st;
     819           0 :         bool made_writable = false;
     820             :         int r;
     821             : 
     822           0 :         assert(fd >= 0);
     823           0 :         assert(subvolume);
     824             : 
     825           0 :         if (fstat(fd, &st) < 0)
     826           0 :                 return -errno;
     827             : 
     828           0 :         if (!S_ISDIR(st.st_mode))
     829           0 :                 return -EINVAL;
     830             : 
     831             :         /* First, try to remove the subvolume. If it happens to be
     832             :          * already empty, this will just work. */
     833           0 :         strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
     834           0 :         if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0)
     835           0 :                 return 0;
     836           0 :         if (!recursive || errno != ENOTEMPTY)
     837           0 :                 return -errno;
     838             : 
     839             :         /* OK, the subvolume is not empty, let's look for child
     840             :          * subvolumes, and remove them, first */
     841           0 :         subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
     842           0 :         if (subvol_fd < 0)
     843           0 :                 return -errno;
     844             : 
     845           0 :         if (subvol_id == 0) {
     846           0 :                 r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
     847           0 :                 if (r < 0)
     848           0 :                         return r;
     849             :         }
     850             : 
     851           0 :         args.key.min_offset = args.key.max_offset = subvol_id;
     852             : 
     853           0 :         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
     854             :                 const struct btrfs_ioctl_search_header *sh;
     855             :                 unsigned i;
     856             : 
     857           0 :                 args.key.nr_items = 256;
     858           0 :                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
     859           0 :                         return -errno;
     860             : 
     861           0 :                 if (args.key.nr_items <= 0)
     862           0 :                         break;
     863             : 
     864           0 :                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
     865           0 :                         _cleanup_free_ char *p = NULL;
     866             :                         const struct btrfs_root_ref *ref;
     867             :                         struct btrfs_ioctl_ino_lookup_args ino_args;
     868             : 
     869           0 :                         btrfs_ioctl_search_args_set(&args, sh);
     870             : 
     871           0 :                         if (sh->type != BTRFS_ROOT_BACKREF_KEY)
     872           0 :                                 continue;
     873           0 :                         if (sh->offset != subvol_id)
     874           0 :                                 continue;
     875             : 
     876           0 :                         ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
     877             : 
     878           0 :                         p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
     879           0 :                         if (!p)
     880           0 :                                 return -ENOMEM;
     881             : 
     882           0 :                         zero(ino_args);
     883           0 :                         ino_args.treeid = subvol_id;
     884           0 :                         ino_args.objectid = htole64(ref->dirid);
     885             : 
     886           0 :                         if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
     887           0 :                                 return -errno;
     888             : 
     889           0 :                         if (!made_writable) {
     890           0 :                                 r = btrfs_subvol_set_read_only_fd(subvol_fd, false);
     891           0 :                                 if (r < 0)
     892           0 :                                         return r;
     893             : 
     894           0 :                                 made_writable = true;
     895             :                         }
     896             : 
     897           0 :                         if (isempty(ino_args.name))
     898             :                                 /* Subvolume is in the top-level
     899             :                                  * directory of the subvolume. */
     900           0 :                                 r = subvol_remove_children(subvol_fd, p, sh->objectid, recursive);
     901             :                         else {
     902           0 :                                 _cleanup_close_ int child_fd = -1;
     903             : 
     904             :                                 /* Subvolume is somewhere further down,
     905             :                                  * hence we need to open the
     906             :                                  * containing directory first */
     907             : 
     908           0 :                                 child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
     909           0 :                                 if (child_fd < 0)
     910           0 :                                         return -errno;
     911             : 
     912           0 :                                 r = subvol_remove_children(child_fd, p, sh->objectid, recursive);
     913             :                         }
     914           0 :                         if (r < 0)
     915           0 :                                 return r;
     916             :                 }
     917             : 
     918             :                 /* Increase search key by one, to read the next item, if we can. */
     919           0 :                 if (!btrfs_ioctl_search_args_inc(&args))
     920           0 :                         break;
     921             :         }
     922             : 
     923             :         /* OK, the child subvolumes should all be gone now, let's try
     924             :          * again to remove the subvolume */
     925           0 :         if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0)
     926           0 :                 return -errno;
     927             : 
     928           0 :         return 0;
     929             : }
     930             : 
     931           0 : int btrfs_subvol_remove(const char *path, bool recursive) {
     932           0 :         _cleanup_close_ int fd = -1;
     933             :         const char *subvolume;
     934             :         int r;
     935             : 
     936           0 :         assert(path);
     937             : 
     938           0 :         r = extract_subvolume_name(path, &subvolume);
     939           0 :         if (r < 0)
     940           0 :                 return r;
     941             : 
     942           0 :         fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
     943           0 :         if (fd < 0)
     944           0 :                 return fd;
     945             : 
     946           0 :         return subvol_remove_children(fd, subvolume, 0, recursive);
     947             : }
     948             : 
     949           0 : int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive) {
     950           0 :         return subvol_remove_children(fd, subvolume, 0, recursive);
     951             : }
     952             : 
     953           0 : static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t old_subvol_id, BtrfsSnapshotFlags flags) {
     954             : 
     955           0 :         struct btrfs_ioctl_search_args args = {
     956             :                 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
     957             : 
     958             :                 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
     959             :                 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
     960             : 
     961             :                 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
     962             :                 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
     963             : 
     964             :                 .key.min_transid = 0,
     965             :                 .key.max_transid = (uint64_t) -1,
     966             :         };
     967             : 
     968           0 :         struct btrfs_ioctl_vol_args_v2 vol_args = {
     969           0 :                 .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0,
     970             :                 .fd = old_fd,
     971             :         };
     972           0 :         _cleanup_close_ int subvolume_fd = -1;
     973             :         uint64_t new_subvol_id;
     974             :         int r;
     975             : 
     976           0 :         assert(old_fd >= 0);
     977           0 :         assert(new_fd >= 0);
     978           0 :         assert(subvolume);
     979             : 
     980           0 :         strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
     981           0 :         vol_args.fd = old_fd;
     982             : 
     983           0 :         if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0)
     984           0 :                 return -errno;
     985             : 
     986           0 :         if (!(flags & BTRFS_SNAPSHOT_RECURSIVE))
     987           0 :                 return 0;
     988             : 
     989           0 :         if (old_subvol_id == 0) {
     990           0 :                 r = btrfs_subvol_get_id_fd(old_fd, &old_subvol_id);
     991           0 :                 if (r < 0)
     992           0 :                         return r;
     993             :         }
     994             : 
     995           0 :         r = btrfs_subvol_get_id(new_fd, vol_args.name, &new_subvol_id);
     996           0 :         if (r < 0)
     997           0 :                 return r;
     998             : 
     999           0 :         args.key.min_offset = args.key.max_offset = old_subvol_id;
    1000             : 
    1001           0 :         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
    1002             :                 const struct btrfs_ioctl_search_header *sh;
    1003             :                 unsigned i;
    1004             : 
    1005           0 :                 args.key.nr_items = 256;
    1006           0 :                 if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
    1007           0 :                         return -errno;
    1008             : 
    1009           0 :                 if (args.key.nr_items <= 0)
    1010           0 :                         break;
    1011             : 
    1012           0 :                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
    1013           0 :                         _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL;
    1014             :                         struct btrfs_ioctl_ino_lookup_args ino_args;
    1015             :                         const struct btrfs_root_ref *ref;
    1016           0 :                         _cleanup_close_ int old_child_fd = -1, new_child_fd = -1;
    1017             : 
    1018           0 :                         btrfs_ioctl_search_args_set(&args, sh);
    1019             : 
    1020           0 :                         if (sh->type != BTRFS_ROOT_BACKREF_KEY)
    1021           0 :                                 continue;
    1022             : 
    1023             :                         /* Avoid finding the source subvolume a second
    1024             :                          * time */
    1025           0 :                         if (sh->offset != old_subvol_id)
    1026           0 :                                 continue;
    1027             : 
    1028             :                         /* Avoid running into loops if the new
    1029             :                          * subvolume is below the old one. */
    1030           0 :                         if (sh->objectid == new_subvol_id)
    1031           0 :                                 continue;
    1032             : 
    1033           0 :                         ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
    1034           0 :                         p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
    1035           0 :                         if (!p)
    1036           0 :                                 return -ENOMEM;
    1037             : 
    1038           0 :                         zero(ino_args);
    1039           0 :                         ino_args.treeid = old_subvol_id;
    1040           0 :                         ino_args.objectid = htole64(ref->dirid);
    1041             : 
    1042           0 :                         if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
    1043           0 :                                 return -errno;
    1044             : 
    1045             :                         /* The kernel returns an empty name if the
    1046             :                          * subvolume is in the top-level directory,
    1047             :                          * and otherwise appends a slash, so that we
    1048             :                          * can just concatenate easily here, without
    1049             :                          * adding a slash. */
    1050           0 :                         c = strappend(ino_args.name, p);
    1051           0 :                         if (!c)
    1052           0 :                                 return -ENOMEM;
    1053             : 
    1054           0 :                         old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
    1055           0 :                         if (old_child_fd < 0)
    1056           0 :                                 return -errno;
    1057             : 
    1058           0 :                         np = strjoin(subvolume, "/", ino_args.name, NULL);
    1059           0 :                         if (!np)
    1060           0 :                                 return -ENOMEM;
    1061             : 
    1062           0 :                         new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
    1063           0 :                         if (new_child_fd < 0)
    1064           0 :                                 return -errno;
    1065             : 
    1066           0 :                         if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
    1067             :                                 /* If the snapshot is read-only we
    1068             :                                  * need to mark it writable
    1069             :                                  * temporarily, to put the subsnapshot
    1070             :                                  * into place. */
    1071             : 
    1072           0 :                                 if (subvolume_fd < 0) {
    1073           0 :                                         subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
    1074           0 :                                         if (subvolume_fd < 0)
    1075           0 :                                                 return -errno;
    1076             :                                 }
    1077             : 
    1078           0 :                                 r = btrfs_subvol_set_read_only_fd(subvolume_fd, false);
    1079           0 :                                 if (r < 0)
    1080           0 :                                         return r;
    1081             :                         }
    1082             : 
    1083             :                         /* When btrfs clones the subvolumes, child
    1084             :                          * subvolumes appear as empty directories. Remove
    1085             :                          * them, so that we can create a new snapshot
    1086             :                          * in their place */
    1087           0 :                         if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0) {
    1088           0 :                                 int k = -errno;
    1089             : 
    1090           0 :                                 if (flags & BTRFS_SNAPSHOT_READ_ONLY)
    1091           0 :                                         (void) btrfs_subvol_set_read_only_fd(subvolume_fd, true);
    1092             : 
    1093           0 :                                 return k;
    1094             :                         }
    1095             : 
    1096           0 :                         r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
    1097             : 
    1098             :                         /* Restore the readonly flag */
    1099           0 :                         if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
    1100             :                                 int k;
    1101             : 
    1102           0 :                                 k = btrfs_subvol_set_read_only_fd(subvolume_fd, true);
    1103           0 :                                 if (r >= 0 && k < 0)
    1104           0 :                                         return k;
    1105             :                         }
    1106             : 
    1107           0 :                         if (r < 0)
    1108           0 :                                 return r;
    1109             :                 }
    1110             : 
    1111             :                 /* Increase search key by one, to read the next item, if we can. */
    1112           0 :                 if (!btrfs_ioctl_search_args_inc(&args))
    1113           0 :                         break;
    1114             :         }
    1115             : 
    1116           0 :         return 0;
    1117             : }
    1118             : 
    1119           0 : int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
    1120           0 :         _cleanup_close_ int new_fd = -1;
    1121             :         const char *subvolume;
    1122             :         int r;
    1123             : 
    1124           0 :         assert(old_fd >= 0);
    1125           0 :         assert(new_path);
    1126             : 
    1127           0 :         r = btrfs_is_subvol(old_fd);
    1128           0 :         if (r < 0)
    1129           0 :                 return r;
    1130           0 :         if (r == 0) {
    1131           0 :                 if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
    1132           0 :                         return -EISDIR;
    1133             : 
    1134           0 :                 r = btrfs_subvol_make(new_path);
    1135           0 :                 if (r < 0)
    1136           0 :                         return r;
    1137             : 
    1138           0 :                 r = copy_directory_fd(old_fd, new_path, true);
    1139           0 :                 if (r < 0) {
    1140           0 :                         btrfs_subvol_remove(new_path, false);
    1141           0 :                         return r;
    1142             :                 }
    1143             : 
    1144           0 :                 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
    1145           0 :                         r = btrfs_subvol_set_read_only(new_path, true);
    1146           0 :                         if (r < 0) {
    1147           0 :                                 btrfs_subvol_remove(new_path, false);
    1148           0 :                                 return r;
    1149             :                         }
    1150             :                 }
    1151             : 
    1152           0 :                 return 0;
    1153             :         }
    1154             : 
    1155           0 :         r = extract_subvolume_name(new_path, &subvolume);
    1156           0 :         if (r < 0)
    1157           0 :                 return r;
    1158             : 
    1159           0 :         new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
    1160           0 :         if (new_fd < 0)
    1161           0 :                 return new_fd;
    1162             : 
    1163           0 :         return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
    1164             : }
    1165             : 
    1166           0 : int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
    1167           0 :         _cleanup_close_ int old_fd = -1;
    1168             : 
    1169           0 :         assert(old_path);
    1170           0 :         assert(new_path);
    1171             : 
    1172           0 :         old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
    1173           0 :         if (old_fd < 0)
    1174           0 :                 return -errno;
    1175             : 
    1176           0 :         return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);
    1177             : }

Generated by: LCOV version 1.11