1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
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.
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.
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/>.
26 #ifdef HAVE_LINUX_BTRFS_H
27 #include <linux/btrfs.h>
32 #include "path-util.h"
36 #include "selinux-util.h"
37 #include "smack-util.h"
38 #include "btrfs-ctree.h"
39 #include "btrfs-util.h"
41 static int validate_subvolume_name(const char *name) {
43 if (!filename_is_valid(name))
46 if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
52 static int open_parent(const char *path, int flags) {
53 _cleanup_free_ char *parent = NULL;
58 r = path_get_parent(path, &parent);
62 fd = open(parent, flags);
69 static int extract_subvolume_name(const char *path, const char **subvolume) {
78 r = validate_subvolume_name(fn);
86 int btrfs_is_snapshot(int fd) {
90 /* On btrfs subvolumes always have the inode 256 */
92 if (fstat(fd, &st) < 0)
95 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
98 if (fstatfs(fd, &sfs) < 0)
101 return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
104 int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_only, bool fallback_copy) {
105 struct btrfs_ioctl_vol_args_v2 args = {
106 .flags = read_only ? BTRFS_SUBVOL_RDONLY : 0,
108 _cleanup_close_ int old_fd = -1, new_fd = -1;
109 const char *subvolume;
114 old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
118 r = btrfs_is_snapshot(old_fd);
124 r = btrfs_subvol_make(new_path);
128 r = copy_directory_fd(old_fd, new_path, true);
130 btrfs_subvol_remove(new_path);
135 r = btrfs_subvol_set_read_only(new_path, true);
137 btrfs_subvol_remove(new_path);
148 r = extract_subvolume_name(new_path, &subvolume);
152 new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
156 strncpy(args.name, subvolume, sizeof(args.name)-1);
159 if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &args) < 0)
165 int btrfs_subvol_make(const char *path) {
166 struct btrfs_ioctl_vol_args args = {};
167 _cleanup_close_ int fd = -1;
168 const char *subvolume;
173 r = extract_subvolume_name(path, &subvolume);
177 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
181 strncpy(args.name, subvolume, sizeof(args.name)-1);
183 if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
189 int btrfs_subvol_make_label(const char *path) {
194 r = mac_selinux_create_file_prepare(path, S_IFDIR);
198 r = btrfs_subvol_make(path);
199 mac_selinux_create_file_clear();
204 return mac_smack_fix(path, false, false);
207 int btrfs_subvol_remove(const char *path) {
208 struct btrfs_ioctl_vol_args args = {};
209 _cleanup_close_ int fd = -1;
210 const char *subvolume;
215 r = extract_subvolume_name(path, &subvolume);
219 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
223 strncpy(args.name, subvolume, sizeof(args.name)-1);
225 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args) < 0)
231 int btrfs_subvol_set_read_only(const char *path, bool b) {
232 _cleanup_close_ int fd = -1;
233 uint64_t flags, nflags;
235 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
239 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
243 nflags = flags | BTRFS_SUBVOL_RDONLY;
245 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
250 if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
256 int btrfs_subvol_get_read_only_fd(int fd) {
259 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
262 return !!(flags & BTRFS_SUBVOL_RDONLY);
265 int btrfs_reflink(int infd, int outfd) {
271 r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
278 int btrfs_get_block_device(const char *path, dev_t *dev) {
279 struct btrfs_ioctl_fs_info_args fsi = {};
280 _cleanup_close_ int fd = -1;
286 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
290 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
293 /* We won't do this for btrfs RAID */
294 if (fsi.num_devices != 1)
297 for (id = 1; id <= fsi.max_id; id++) {
298 struct btrfs_ioctl_dev_info_args di = {
303 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
310 if (stat((char*) di.path, &st) < 0)
313 if (!S_ISBLK(st.st_mode))
316 if (major(st.st_rdev) == 0)
326 int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
327 struct btrfs_ioctl_ino_lookup_args args = {
328 .objectid = BTRFS_FIRST_FREE_OBJECTID
334 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
341 int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
342 struct btrfs_ioctl_search_args args = {
343 /* Tree of tree roots */
344 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
346 /* Look precisely for the subvolume items */
347 .key.min_type = BTRFS_ROOT_ITEM_KEY,
348 .key.max_type = BTRFS_ROOT_ITEM_KEY,
350 /* No restrictions on the other components */
352 .key.max_offset = (uint64_t) -1,
353 .key.min_transid = 0,
354 .key.max_transid = (uint64_t) -1,
364 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
368 args.key.min_objectid = args.key.max_objectid = subvol_id;
371 const struct btrfs_ioctl_search_header *sh;
374 args.key.nr_items = 256;
375 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
378 if (args.key.nr_items <= 0)
382 sh = (const struct btrfs_ioctl_search_header*) args.buf;
383 i < args.key.nr_items;
385 args.key.min_type = sh->type,
386 args.key.min_offset = sh->offset,
387 args.key.min_objectid = sh->objectid,
388 sh = (const struct btrfs_ioctl_search_header*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header) + sh->len)) {
390 const struct btrfs_root_item *ri;
392 if (sh->objectid != subvol_id)
394 if (sh->type != BTRFS_ROOT_ITEM_KEY)
396 if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
399 ri = (const struct btrfs_root_item *)(args.buf + sizeof(struct btrfs_ioctl_search_header));
401 ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
402 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
404 ret->subvol_id = subvol_id;
405 ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
407 assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
408 memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
409 memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
415 args.key.min_offset++;
416 if (!args.key.min_offset) /* overflow */
427 int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
429 struct btrfs_ioctl_search_args args = {
430 /* Tree of quota items */
431 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
433 /* Look precisely for the quota items */
434 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
435 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
437 .key.min_objectid = 0,
438 .key.max_objectid = 0,
440 /* No restrictions on the other components */
441 .key.min_transid = 0,
442 .key.max_transid = (uint64_t) -1,
446 bool found_info = false, found_limit = false;
452 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
456 args.key.min_offset = args.key.max_offset = subvol_id;
459 const struct btrfs_ioctl_search_header *sh;
462 args.key.nr_items = 256;
463 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
466 if (args.key.nr_items <= 0)
470 sh = (const struct btrfs_ioctl_search_header*) args.buf;
471 i < args.key.nr_items;
473 args.key.min_type = sh->type,
474 args.key.min_offset = sh->offset,
475 args.key.min_objectid = sh->objectid,
476 sh = (const struct btrfs_ioctl_search_header*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header) + sh->len)) {
480 if (sh->objectid != 0)
482 if (sh->offset != subvol_id)
485 body = (uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header);
487 if (sh->type == BTRFS_QGROUP_INFO_KEY) {
488 const struct btrfs_qgroup_info_item *qii = body;
490 ret->referred = le64toh(qii->rfer);
491 ret->exclusive = le64toh(qii->excl);
495 } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
496 const struct btrfs_qgroup_limit_item *qli = body;
498 ret->referred_max = le64toh(qli->max_rfer);
499 ret->exclusive_max = le64toh(qli->max_excl);
501 if (ret->referred_max == 0)
502 ret->referred_max = (uint64_t) -1;
503 if (ret->exclusive_max == 0)
504 ret->exclusive_max = (uint64_t) -1;
509 if (found_info && found_limit)
513 args.key.min_offset++;
514 if (!args.key.min_offset)
519 if (!found_limit && !found_info)
523 ret->referred = (uint64_t) -1;
524 ret->exclusive = (uint64_t) -1;
528 ret->referred_max = (uint64_t) -1;
529 ret->exclusive_max = (uint64_t) -1;