chiark / gitweb /
util: rename ignore_file() to hidden_file()
[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         /* On btrfs subvolumes always have the inode 256 */
88
89         if (fstat(fd, &st) < 0)
90                 return -errno;
91
92         if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
93                 return 0;
94
95         if (fstatfs(fd, &sfs) < 0)
96                 return -errno;
97
98         return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
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_subvol_is_read_only_fd(int fd) {
236         uint64_t flags;
237
238         if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
239                 return -errno;
240
241         return !!(flags & BTRFS_SUBVOL_RDONLY);
242 }
243
244 int btrfs_reflink(int infd, int outfd) {
245         int r;
246
247         assert(infd >= 0);
248         assert(outfd >= 0);
249
250         r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
251         if (r < 0)
252                 return -errno;
253
254         return 0;
255 }
256
257 int btrfs_get_block_device(const char *path, dev_t *dev) {
258         struct btrfs_ioctl_fs_info_args fsi = {};
259         _cleanup_close_ int fd = -1;
260         uint64_t id;
261
262         assert(path);
263         assert(dev);
264
265         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
266         if (fd < 0)
267                 return -errno;
268
269         if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
270                 return -errno;
271
272         /* We won't do this for btrfs RAID */
273         if (fsi.num_devices != 1)
274                 return 0;
275
276         for (id = 1; id <= fsi.max_id; id++) {
277                 struct btrfs_ioctl_dev_info_args di = {
278                         .devid = id,
279                 };
280                 struct stat st;
281
282                 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
283                         if (errno == ENODEV)
284                                 continue;
285
286                         return -errno;
287                 }
288
289                 if (stat((char*) di.path, &st) < 0)
290                         return -errno;
291
292                 if (!S_ISBLK(st.st_mode))
293                         return -ENODEV;
294
295                 if (major(st.st_rdev) == 0)
296                         return -ENODEV;
297
298                 *dev = st.st_rdev;
299                 return 1;
300         }
301
302         return -ENODEV;
303 }