chiark / gitweb /
test: do not use last cap from kernel in test-cap-list
[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-ctree.h"
37 #include "btrfs-util.h"
38
39 static int validate_subvolume_name(const char *name) {
40
41         if (!filename_is_valid(name))
42                 return -EINVAL;
43
44         if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
45                 return -E2BIG;
46
47         return 0;
48 }
49
50 static int open_parent(const char *path, int flags) {
51         _cleanup_free_ char *parent = NULL;
52         int r, fd;
53
54         assert(path);
55
56         r = path_get_parent(path, &parent);
57         if (r < 0)
58                 return r;
59
60         fd = open(parent, flags);
61         if (fd < 0)
62                 return -errno;
63
64         return fd;
65 }
66
67 static int extract_subvolume_name(const char *path, const char **subvolume) {
68         const char *fn;
69         int r;
70
71         assert(path);
72         assert(subvolume);
73
74         fn = basename(path);
75
76         r = validate_subvolume_name(fn);
77         if (r < 0)
78                 return r;
79
80         *subvolume = fn;
81         return 0;
82 }
83
84 int btrfs_is_snapshot(int fd) {
85         struct stat st;
86         struct statfs sfs;
87
88         /* On btrfs subvolumes always have the inode 256 */
89
90         if (fstat(fd, &st) < 0)
91                 return -errno;
92
93         if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
94                 return 0;
95
96         if (fstatfs(fd, &sfs) < 0)
97                 return -errno;
98
99         return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
100 }
101
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,
105         };
106         _cleanup_close_ int old_fd = -1, new_fd = -1;
107         const char *subvolume;
108         int r;
109
110         assert(old_path);
111
112         old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
113         if (old_fd < 0)
114                 return -errno;
115
116         r = btrfs_is_snapshot(old_fd);
117         if (r < 0)
118                 return r;
119         if (r == 0) {
120
121                 if (fallback_copy) {
122                         r = btrfs_subvol_make(new_path);
123                         if (r < 0)
124                                 return r;
125
126                         r = copy_directory_fd(old_fd, new_path, true);
127                         if (r < 0) {
128                                 btrfs_subvol_remove(new_path);
129                                 return r;
130                         }
131
132                         if (read_only) {
133                                 r = btrfs_subvol_set_read_only(new_path, true);
134                                 if (r < 0) {
135                                         btrfs_subvol_remove(new_path);
136                                         return r;
137                                 }
138                         }
139
140                         return 0;
141                 }
142
143                 return -EISDIR;
144         }
145
146         r = extract_subvolume_name(new_path, &subvolume);
147         if (r < 0)
148                 return r;
149
150         new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
151         if (new_fd < 0)
152                 return new_fd;
153
154         strncpy(args.name, subvolume, sizeof(args.name)-1);
155         args.fd = old_fd;
156
157         if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &args) < 0)
158                 return -errno;
159
160         return 0;
161 }
162
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;
167         int r;
168
169         assert(path);
170
171         r = extract_subvolume_name(path, &subvolume);
172         if (r < 0)
173                 return r;
174
175         fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
176         if (fd < 0)
177                 return fd;
178
179         strncpy(args.name, subvolume, sizeof(args.name)-1);
180
181         if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
182                 return -errno;
183
184         return 0;
185 }
186
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;
191         int r;
192
193         assert(path);
194
195         r = extract_subvolume_name(path, &subvolume);
196         if (r < 0)
197                 return r;
198
199         fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
200         if (fd < 0)
201                 return fd;
202
203         strncpy(args.name, subvolume, sizeof(args.name)-1);
204
205         if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args) < 0)
206                 return -errno;
207
208         return 0;
209 }
210
211 int btrfs_subvol_set_read_only(const char *path, bool b) {
212         _cleanup_close_ int fd = -1;
213         uint64_t flags, nflags;
214
215         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
216         if (fd < 0)
217                 return -errno;
218
219         if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
220                 return -errno;
221
222         if (b)
223                 nflags = flags | BTRFS_SUBVOL_RDONLY;
224         else
225                 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
226
227         if (flags == nflags)
228                 return 0;
229
230         if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
231                 return -errno;
232
233         return 0;
234 }
235
236 int btrfs_subvol_get_read_only_fd(int fd) {
237         uint64_t flags;
238
239         if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
240                 return -errno;
241
242         return !!(flags & BTRFS_SUBVOL_RDONLY);
243 }
244
245 int btrfs_reflink(int infd, int outfd) {
246         int r;
247
248         assert(infd >= 0);
249         assert(outfd >= 0);
250
251         r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
252         if (r < 0)
253                 return -errno;
254
255         return 0;
256 }
257
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;
261         uint64_t id;
262
263         assert(path);
264         assert(dev);
265
266         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
267         if (fd < 0)
268                 return -errno;
269
270         if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
271                 return -errno;
272
273         /* We won't do this for btrfs RAID */
274         if (fsi.num_devices != 1)
275                 return 0;
276
277         for (id = 1; id <= fsi.max_id; id++) {
278                 struct btrfs_ioctl_dev_info_args di = {
279                         .devid = id,
280                 };
281                 struct stat st;
282
283                 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
284                         if (errno == ENODEV)
285                                 continue;
286
287                         return -errno;
288                 }
289
290                 if (stat((char*) di.path, &st) < 0)
291                         return -errno;
292
293                 if (!S_ISBLK(st.st_mode))
294                         return -ENODEV;
295
296                 if (major(st.st_rdev) == 0)
297                         return -ENODEV;
298
299                 *dev = st.st_rdev;
300                 return 1;
301         }
302
303         return -ENODEV;
304 }
305
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
309         };
310
311         assert(fd >= 0);
312         assert(ret);
313
314         if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
315                 return -errno;
316
317         *ret = args.treeid;
318         return 0;
319 }
320
321 int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
322         struct btrfs_ioctl_search_args args = {
323                 /* Tree of tree roots */
324                 .key.tree_id = 1,
325
326                 /* Look precisely for the subvolume items */
327                 .key.min_type = BTRFS_ROOT_ITEM_KEY,
328                 .key.max_type = BTRFS_ROOT_ITEM_KEY,
329
330                 /* No restrictions on the other components */
331                 .key.min_offset = 0,
332                 .key.max_offset = (uint64_t) -1,
333                 .key.min_transid = 0,
334                 .key.max_transid = (uint64_t) -1,
335
336                 /* Some large value */
337                 .key.nr_items = 2,
338         };
339
340         struct btrfs_ioctl_search_header *sh;
341         struct btrfs_root_item *ri;
342         uint64_t subvol_id;
343         int r;
344
345         assert(fd >= 0);
346         assert(ret);
347
348         r = btrfs_subvol_get_id_fd(fd, &subvol_id);
349         if (r < 0)
350                 return r;
351
352         args.key.min_objectid = args.key.max_objectid = subvol_id;
353         if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
354                 return -errno;
355
356         if (args.key.nr_items != 1)
357                 return -EIO;
358
359         sh = (struct btrfs_ioctl_search_header*) args.buf;
360         assert(sh->type == BTRFS_ROOT_ITEM_KEY);
361         assert(sh->objectid == subvol_id);
362
363         if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
364                 return -ENOTSUP;
365
366         ri = (struct btrfs_root_item *)(args.buf + sizeof(struct btrfs_ioctl_search_header));
367
368         ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
369                      (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
370
371         ret->subvol_id = subvol_id;
372         ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
373
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));
377
378         return 0;
379 }