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-ctree.h"
37 #include "btrfs-util.h"
39 static int validate_subvolume_name(const char *name) {
41 if (!filename_is_valid(name))
44 if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
50 static int open_parent(const char *path, int flags) {
51 _cleanup_free_ char *parent = NULL;
56 r = path_get_parent(path, &parent);
60 fd = open(parent, flags);
67 static int extract_subvolume_name(const char *path, const char **subvolume) {
76 r = validate_subvolume_name(fn);
84 int btrfs_is_snapshot(int fd) {
88 /* On btrfs subvolumes always have the inode 256 */
90 if (fstat(fd, &st) < 0)
93 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
96 if (fstatfs(fd, &sfs) < 0)
99 return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
102 int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_only, bool fallback_copy) {
103 struct btrfs_ioctl_vol_args_v2 args = {
104 .flags = read_only ? BTRFS_SUBVOL_RDONLY : 0,
106 _cleanup_close_ int old_fd = -1, new_fd = -1;
107 const char *subvolume;
112 old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
116 r = btrfs_is_snapshot(old_fd);
122 r = btrfs_subvol_make(new_path);
126 r = copy_directory_fd(old_fd, new_path, true);
128 btrfs_subvol_remove(new_path);
133 r = btrfs_subvol_set_read_only(new_path, true);
135 btrfs_subvol_remove(new_path);
146 r = extract_subvolume_name(new_path, &subvolume);
150 new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
154 strncpy(args.name, subvolume, sizeof(args.name)-1);
157 if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &args) < 0)
163 int btrfs_subvol_make(const char *path) {
164 struct btrfs_ioctl_vol_args args = {};
165 _cleanup_close_ int fd = -1;
166 const char *subvolume;
171 r = extract_subvolume_name(path, &subvolume);
175 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
179 strncpy(args.name, subvolume, sizeof(args.name)-1);
181 if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
187 int btrfs_subvol_remove(const char *path) {
188 struct btrfs_ioctl_vol_args args = {};
189 _cleanup_close_ int fd = -1;
190 const char *subvolume;
195 r = extract_subvolume_name(path, &subvolume);
199 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
203 strncpy(args.name, subvolume, sizeof(args.name)-1);
205 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args) < 0)
211 int btrfs_subvol_set_read_only(const char *path, bool b) {
212 _cleanup_close_ int fd = -1;
213 uint64_t flags, nflags;
215 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
219 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
223 nflags = flags | BTRFS_SUBVOL_RDONLY;
225 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
230 if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
236 int btrfs_subvol_get_read_only_fd(int fd) {
239 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
242 return !!(flags & BTRFS_SUBVOL_RDONLY);
245 int btrfs_reflink(int infd, int outfd) {
251 r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
258 int btrfs_get_block_device(const char *path, dev_t *dev) {
259 struct btrfs_ioctl_fs_info_args fsi = {};
260 _cleanup_close_ int fd = -1;
266 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
270 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
273 /* We won't do this for btrfs RAID */
274 if (fsi.num_devices != 1)
277 for (id = 1; id <= fsi.max_id; id++) {
278 struct btrfs_ioctl_dev_info_args di = {
283 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
290 if (stat((char*) di.path, &st) < 0)
293 if (!S_ISBLK(st.st_mode))
296 if (major(st.st_rdev) == 0)
306 int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
307 struct btrfs_ioctl_ino_lookup_args args = {
308 .objectid = BTRFS_FIRST_FREE_OBJECTID
314 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
321 int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
322 struct btrfs_ioctl_search_args args = {
323 /* Tree of tree roots */
326 /* Look precisely for the subvolume items */
327 .key.min_type = BTRFS_ROOT_ITEM_KEY,
328 .key.max_type = BTRFS_ROOT_ITEM_KEY,
330 /* No restrictions on the other components */
332 .key.max_offset = (uint64_t) -1,
333 .key.min_transid = 0,
334 .key.max_transid = (uint64_t) -1,
336 /* Some large value */
340 struct btrfs_ioctl_search_header *sh;
341 struct btrfs_root_item *ri;
348 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
352 args.key.min_objectid = args.key.max_objectid = subvol_id;
353 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
356 if (args.key.nr_items != 1)
359 sh = (struct btrfs_ioctl_search_header*) args.buf;
360 assert(sh->type == BTRFS_ROOT_ITEM_KEY);
361 assert(sh->objectid == subvol_id);
363 if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
366 ri = (struct btrfs_root_item *)(args.buf + sizeof(struct btrfs_ioctl_search_header));
368 ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
369 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
371 ret->subvol_id = subvol_id;
372 ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
374 assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
375 memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
376 memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));