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