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 "btrfs-util.h"
38 static int validate_subvolume_name(const char *name) {
40 if (!filename_is_valid(name))
43 if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
49 static int open_parent(const char *path, int flags) {
50 _cleanup_free_ char *parent = NULL;
55 r = path_get_parent(path, &parent);
59 fd = open(parent, flags);
66 static int extract_subvolume_name(const char *path, const char **subvolume) {
75 r = validate_subvolume_name(fn);
83 int btrfs_is_snapshot(int fd) {
87 if (fstatfs(fd, &sfs) < 0)
90 if (!F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
93 if (fstat(fd, &st) < 0)
96 /* On btrfs subvolumes always have the inode 256 */
98 return S_ISDIR(st.st_mode) && st.st_ino == 256;
101 int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_only, bool fallback_copy) {
102 struct btrfs_ioctl_vol_args_v2 args = {
103 .flags = read_only ? BTRFS_SUBVOL_RDONLY : 0,
105 _cleanup_close_ int old_fd = -1, new_fd = -1;
106 const char *subvolume;
111 old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
115 r = btrfs_is_snapshot(old_fd);
121 r = btrfs_subvol_make(new_path);
125 r = copy_tree_fd(old_fd, new_path, true);
127 btrfs_subvol_remove(new_path);
132 r = btrfs_subvol_read_only(new_path, true);
134 btrfs_subvol_remove(new_path);
145 r = extract_subvolume_name(new_path, &subvolume);
149 new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
153 strncpy(args.name, subvolume, sizeof(args.name)-1);
156 if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &args) < 0)
162 int btrfs_subvol_make(const char *path) {
163 struct btrfs_ioctl_vol_args args = {};
164 _cleanup_close_ int fd = -1;
165 const char *subvolume;
170 r = extract_subvolume_name(path, &subvolume);
174 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
178 strncpy(args.name, subvolume, sizeof(args.name)-1);
180 if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
186 int btrfs_subvol_remove(const char *path) {
187 struct btrfs_ioctl_vol_args args = {};
188 _cleanup_close_ int fd = -1;
189 const char *subvolume;
194 r = extract_subvolume_name(path, &subvolume);
198 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
202 strncpy(args.name, subvolume, sizeof(args.name)-1);
204 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args) < 0)
210 int btrfs_subvol_read_only(const char *path, bool b) {
211 _cleanup_close_ int fd = -1;
212 uint64_t flags, nflags;
214 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
218 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
222 nflags = flags | BTRFS_SUBVOL_RDONLY;
224 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
229 if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
235 int btrfs_reflink(int infd, int outfd) {
241 r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
248 int btrfs_get_block_device(const char *path, dev_t *dev) {
249 struct btrfs_ioctl_fs_info_args fsi = {};
250 _cleanup_close_ int fd = -1;
256 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
260 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
263 /* We won't do this for btrfs RAID */
264 if (fsi.num_devices != 1)
267 for (id = 1; id <= fsi.max_id; id++) {
268 struct btrfs_ioctl_dev_info_args di = {
273 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
280 if (stat((char*) di.path, &st) < 0)
283 if (!S_ISBLK(st.st_mode))
286 if (major(st.st_rdev) == 0)