chiark / gitweb /
492d7fc777cefcb2de2b9b8033e7b9ef6451fb1a
[elogind.git] / src / shared / btrfs-util.c
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 "strv.h"
35 #include "copy.h"
36 #include "btrfs-util.h"
37
38 static int validate_subvolume_name(const char *name) {
39
40         if (!filename_is_valid(name))
41                 return -EINVAL;
42
43         if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
44                 return -E2BIG;
45
46         return 0;
47 }
48
49 static int open_parent(const char *path, int flags) {
50         _cleanup_free_ char *parent = NULL;
51         int r, fd;
52
53         assert(path);
54
55         r = path_get_parent(path, &parent);
56         if (r < 0)
57                 return r;
58
59         fd = open(parent, flags);
60         if (fd < 0)
61                 return -errno;
62
63         return fd;
64 }
65
66 static int extract_subvolume_name(const char *path, const char **subvolume) {
67         const char *fn;
68         int r;
69
70         assert(path);
71         assert(subvolume);
72
73         fn = basename(path);
74
75         r = validate_subvolume_name(fn);
76         if (r < 0)
77                 return r;
78
79         *subvolume = fn;
80         return 0;
81 }
82
83 int btrfs_is_snapshot(int fd) {
84         struct stat st;
85         struct statfs sfs;
86
87         if (fstatfs(fd, &sfs) < 0)
88                 return -errno;
89
90         if (!F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
91                 return 0;
92
93         if (fstat(fd, &st) < 0)
94                 return -errno;
95
96         /* On btrfs subvolumes always have the inode 256 */
97
98         return S_ISDIR(st.st_mode) && st.st_ino == 256;
99 }
100
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,
104         };
105         _cleanup_close_ int old_fd = -1, new_fd = -1;
106         const char *subvolume;
107         int r;
108
109         assert(old_path);
110
111         old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
112         if (old_fd < 0)
113                 return -errno;
114
115         r = btrfs_is_snapshot(old_fd);
116         if (r < 0)
117                 return r;
118         if (r == 0) {
119
120                 if (fallback_copy) {
121                         r = btrfs_subvol_make(new_path);
122                         if (r < 0)
123                                 return r;
124
125                         r = copy_directory_fd(old_fd, new_path, true);
126                         if (r < 0) {
127                                 btrfs_subvol_remove(new_path);
128                                 return r;
129                         }
130
131                         if (read_only) {
132                                 r = btrfs_subvol_read_only(new_path, true);
133                                 if (r < 0) {
134                                         btrfs_subvol_remove(new_path);
135                                         return r;
136                                 }
137                         }
138
139                         return 0;
140                 }
141
142                 return -EISDIR;
143         }
144
145         r = extract_subvolume_name(new_path, &subvolume);
146         if (r < 0)
147                 return r;
148
149         new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
150         if (new_fd < 0)
151                 return new_fd;
152
153         strncpy(args.name, subvolume, sizeof(args.name)-1);
154         args.fd = old_fd;
155
156         if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &args) < 0)
157                 return -errno;
158
159         return 0;
160 }
161
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;
166         int r;
167
168         assert(path);
169
170         r = extract_subvolume_name(path, &subvolume);
171         if (r < 0)
172                 return r;
173
174         fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
175         if (fd < 0)
176                 return fd;
177
178         strncpy(args.name, subvolume, sizeof(args.name)-1);
179
180         if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
181                 return -errno;
182
183         return 0;
184 }
185
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;
190         int r;
191
192         assert(path);
193
194         r = extract_subvolume_name(path, &subvolume);
195         if (r < 0)
196                 return r;
197
198         fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
199         if (fd < 0)
200                 return fd;
201
202         strncpy(args.name, subvolume, sizeof(args.name)-1);
203
204         if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args) < 0)
205                 return -errno;
206
207         return 0;
208 }
209
210 int btrfs_subvol_read_only(const char *path, bool b) {
211         _cleanup_close_ int fd = -1;
212         uint64_t flags, nflags;
213
214         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
215         if (fd < 0)
216                 return -errno;
217
218         if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
219                 return -errno;
220
221         if (b)
222                 nflags = flags | BTRFS_SUBVOL_RDONLY;
223         else
224                 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
225
226         if (flags == nflags)
227                 return 0;
228
229         if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
230                 return -errno;
231
232         return 0;
233 }
234
235 int btrfs_reflink(int infd, int outfd) {
236         int r;
237
238         assert(infd >= 0);
239         assert(outfd >= 0);
240
241         r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
242         if (r < 0)
243                 return -errno;
244
245         return 0;
246 }
247
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;
251         uint64_t id;
252
253         assert(path);
254         assert(dev);
255
256         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
257         if (fd < 0)
258                 return -errno;
259
260         if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
261                 return -errno;
262
263         /* We won't do this for btrfs RAID */
264         if (fsi.num_devices != 1)
265                 return 0;
266
267         for (id = 1; id <= fsi.max_id; id++) {
268                 struct btrfs_ioctl_dev_info_args di = {
269                         .devid = id,
270                 };
271                 struct stat st;
272
273                 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
274                         if (errno == ENODEV)
275                                 continue;
276
277                         return -errno;
278                 }
279
280                 if (stat((char*) di.path, &st) < 0)
281                         return -errno;
282
283                 if (!S_ISBLK(st.st_mode))
284                         return -ENODEV;
285
286                 if (major(st.st_rdev) == 0)
287                         return -ENODEV;
288
289                 *dev = st.st_rdev;
290                 return 1;
291         }
292
293         return -ENODEV;
294 }