]>
Commit | Line | Data |
---|---|---|
d7c7c334 LP |
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" | |
d7c7c334 | 34 | #include "copy.h" |
d7b8eec7 LP |
35 | #include "selinux-util.h" |
36 | #include "smack-util.h" | |
10f9c755 | 37 | #include "btrfs-ctree.h" |
d7c7c334 LP |
38 | #include "btrfs-util.h" |
39 | ||
40 | static int validate_subvolume_name(const char *name) { | |
41 | ||
42 | if (!filename_is_valid(name)) | |
43 | return -EINVAL; | |
44 | ||
45 | if (strlen(name) > BTRFS_SUBVOL_NAME_MAX) | |
46 | return -E2BIG; | |
47 | ||
48 | return 0; | |
49 | } | |
50 | ||
51 | static int open_parent(const char *path, int flags) { | |
52 | _cleanup_free_ char *parent = NULL; | |
53 | int r, fd; | |
54 | ||
55 | assert(path); | |
56 | ||
57 | r = path_get_parent(path, &parent); | |
58 | if (r < 0) | |
59 | return r; | |
60 | ||
61 | fd = open(parent, flags); | |
62 | if (fd < 0) | |
63 | return -errno; | |
64 | ||
65 | return fd; | |
66 | } | |
67 | ||
68 | static int extract_subvolume_name(const char *path, const char **subvolume) { | |
69 | const char *fn; | |
70 | int r; | |
71 | ||
72 | assert(path); | |
73 | assert(subvolume); | |
74 | ||
75 | fn = basename(path); | |
76 | ||
77 | r = validate_subvolume_name(fn); | |
78 | if (r < 0) | |
79 | return r; | |
80 | ||
81 | *subvolume = fn; | |
82 | return 0; | |
83 | } | |
84 | ||
85 | int btrfs_is_snapshot(int fd) { | |
86 | struct stat st; | |
87 | struct statfs sfs; | |
88 | ||
cd61c3bf LP |
89 | /* On btrfs subvolumes always have the inode 256 */ |
90 | ||
91 | if (fstat(fd, &st) < 0) | |
d7c7c334 LP |
92 | return -errno; |
93 | ||
cd61c3bf | 94 | if (!S_ISDIR(st.st_mode) || st.st_ino != 256) |
d7c7c334 LP |
95 | return 0; |
96 | ||
cd61c3bf | 97 | if (fstatfs(fd, &sfs) < 0) |
d7c7c334 LP |
98 | return -errno; |
99 | ||
cd61c3bf | 100 | return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC); |
d7c7c334 LP |
101 | } |
102 | ||
103 | int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_only, bool fallback_copy) { | |
104 | struct btrfs_ioctl_vol_args_v2 args = { | |
105 | .flags = read_only ? BTRFS_SUBVOL_RDONLY : 0, | |
106 | }; | |
107 | _cleanup_close_ int old_fd = -1, new_fd = -1; | |
108 | const char *subvolume; | |
109 | int r; | |
110 | ||
111 | assert(old_path); | |
112 | ||
113 | old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); | |
114 | if (old_fd < 0) | |
115 | return -errno; | |
116 | ||
117 | r = btrfs_is_snapshot(old_fd); | |
118 | if (r < 0) | |
119 | return r; | |
120 | if (r == 0) { | |
121 | ||
122 | if (fallback_copy) { | |
123 | r = btrfs_subvol_make(new_path); | |
124 | if (r < 0) | |
125 | return r; | |
126 | ||
f2cbe59e | 127 | r = copy_directory_fd(old_fd, new_path, true); |
d7c7c334 LP |
128 | if (r < 0) { |
129 | btrfs_subvol_remove(new_path); | |
130 | return r; | |
131 | } | |
132 | ||
133 | if (read_only) { | |
10f9c755 | 134 | r = btrfs_subvol_set_read_only(new_path, true); |
d7c7c334 LP |
135 | if (r < 0) { |
136 | btrfs_subvol_remove(new_path); | |
137 | return r; | |
138 | } | |
139 | } | |
140 | ||
141 | return 0; | |
142 | } | |
143 | ||
144 | return -EISDIR; | |
145 | } | |
146 | ||
147 | r = extract_subvolume_name(new_path, &subvolume); | |
148 | if (r < 0) | |
149 | return r; | |
150 | ||
151 | new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); | |
152 | if (new_fd < 0) | |
153 | return new_fd; | |
154 | ||
155 | strncpy(args.name, subvolume, sizeof(args.name)-1); | |
156 | args.fd = old_fd; | |
157 | ||
158 | if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &args) < 0) | |
159 | return -errno; | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
164 | int btrfs_subvol_make(const char *path) { | |
165 | struct btrfs_ioctl_vol_args args = {}; | |
166 | _cleanup_close_ int fd = -1; | |
167 | const char *subvolume; | |
168 | int r; | |
169 | ||
170 | assert(path); | |
171 | ||
172 | r = extract_subvolume_name(path, &subvolume); | |
173 | if (r < 0) | |
174 | return r; | |
175 | ||
176 | fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); | |
177 | if (fd < 0) | |
178 | return fd; | |
179 | ||
180 | strncpy(args.name, subvolume, sizeof(args.name)-1); | |
181 | ||
182 | if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0) | |
183 | return -errno; | |
184 | ||
185 | return 0; | |
186 | } | |
187 | ||
d7b8eec7 LP |
188 | int btrfs_subvol_make_label(const char *path) { |
189 | int r; | |
190 | ||
191 | assert(path); | |
192 | ||
193 | r = mac_selinux_create_file_prepare(path, S_IFDIR); | |
194 | if (r < 0) | |
195 | return r; | |
196 | ||
197 | r = btrfs_subvol_make(path); | |
198 | mac_selinux_create_file_clear(); | |
199 | ||
200 | if (r < 0) | |
201 | return r; | |
202 | ||
203 | return mac_smack_fix(path, false, false); | |
204 | } | |
205 | ||
d7c7c334 LP |
206 | int btrfs_subvol_remove(const char *path) { |
207 | struct btrfs_ioctl_vol_args args = {}; | |
208 | _cleanup_close_ int fd = -1; | |
209 | const char *subvolume; | |
210 | int r; | |
211 | ||
212 | assert(path); | |
213 | ||
214 | r = extract_subvolume_name(path, &subvolume); | |
215 | if (r < 0) | |
216 | return r; | |
217 | ||
218 | fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); | |
219 | if (fd < 0) | |
220 | return fd; | |
221 | ||
222 | strncpy(args.name, subvolume, sizeof(args.name)-1); | |
223 | ||
224 | if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args) < 0) | |
225 | return -errno; | |
226 | ||
227 | return 0; | |
228 | } | |
229 | ||
0d6e763b | 230 | int btrfs_subvol_set_read_only_fd(int fd, bool b) { |
d7c7c334 | 231 | uint64_t flags, nflags; |
0d6e763b | 232 | struct stat st; |
d7c7c334 | 233 | |
0d6e763b LP |
234 | assert(fd >= 0); |
235 | ||
236 | if (fstat(fd, &st) < 0) | |
d7c7c334 LP |
237 | return -errno; |
238 | ||
0d6e763b LP |
239 | if (!S_ISDIR(st.st_mode) || st.st_ino != 256) |
240 | return -EINVAL; | |
241 | ||
d7c7c334 LP |
242 | if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0) |
243 | return -errno; | |
244 | ||
245 | if (b) | |
246 | nflags = flags | BTRFS_SUBVOL_RDONLY; | |
247 | else | |
248 | nflags = flags & ~BTRFS_SUBVOL_RDONLY; | |
249 | ||
250 | if (flags == nflags) | |
251 | return 0; | |
252 | ||
253 | if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0) | |
254 | return -errno; | |
255 | ||
256 | return 0; | |
257 | } | |
258 | ||
0d6e763b LP |
259 | int btrfs_subvol_set_read_only(const char *path, bool b) { |
260 | _cleanup_close_ int fd = -1; | |
261 | ||
262 | fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); | |
263 | if (fd < 0) | |
264 | return -errno; | |
265 | ||
266 | return btrfs_subvol_set_read_only_fd(fd, b); | |
267 | } | |
268 | ||
10f9c755 | 269 | int btrfs_subvol_get_read_only_fd(int fd) { |
cd61c3bf LP |
270 | uint64_t flags; |
271 | ||
272 | if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0) | |
273 | return -errno; | |
274 | ||
275 | return !!(flags & BTRFS_SUBVOL_RDONLY); | |
276 | } | |
277 | ||
d7c7c334 LP |
278 | int btrfs_reflink(int infd, int outfd) { |
279 | int r; | |
280 | ||
281 | assert(infd >= 0); | |
282 | assert(outfd >= 0); | |
283 | ||
284 | r = ioctl(outfd, BTRFS_IOC_CLONE, infd); | |
285 | if (r < 0) | |
286 | return -errno; | |
287 | ||
288 | return 0; | |
289 | } | |
290 | ||
1c7dd825 LP |
291 | int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) { |
292 | struct btrfs_ioctl_clone_range_args args = { | |
293 | .src_fd = infd, | |
294 | .src_offset = in_offset, | |
295 | .src_length = sz, | |
296 | .dest_offset = out_offset, | |
297 | }; | |
298 | int r; | |
299 | ||
300 | assert(infd >= 0); | |
301 | assert(outfd >= 0); | |
302 | assert(sz > 0); | |
303 | ||
304 | r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args); | |
305 | if (r < 0) | |
306 | return -errno; | |
307 | ||
308 | return 0; | |
309 | } | |
310 | ||
d7c7c334 LP |
311 | int btrfs_get_block_device(const char *path, dev_t *dev) { |
312 | struct btrfs_ioctl_fs_info_args fsi = {}; | |
313 | _cleanup_close_ int fd = -1; | |
314 | uint64_t id; | |
315 | ||
316 | assert(path); | |
317 | assert(dev); | |
318 | ||
319 | fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); | |
320 | if (fd < 0) | |
321 | return -errno; | |
322 | ||
323 | if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0) | |
324 | return -errno; | |
325 | ||
326 | /* We won't do this for btrfs RAID */ | |
327 | if (fsi.num_devices != 1) | |
328 | return 0; | |
329 | ||
330 | for (id = 1; id <= fsi.max_id; id++) { | |
331 | struct btrfs_ioctl_dev_info_args di = { | |
332 | .devid = id, | |
333 | }; | |
334 | struct stat st; | |
335 | ||
336 | if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) { | |
337 | if (errno == ENODEV) | |
338 | continue; | |
339 | ||
340 | return -errno; | |
341 | } | |
342 | ||
343 | if (stat((char*) di.path, &st) < 0) | |
344 | return -errno; | |
345 | ||
346 | if (!S_ISBLK(st.st_mode)) | |
347 | return -ENODEV; | |
348 | ||
349 | if (major(st.st_rdev) == 0) | |
350 | return -ENODEV; | |
351 | ||
352 | *dev = st.st_rdev; | |
353 | return 1; | |
354 | } | |
355 | ||
356 | return -ENODEV; | |
357 | } | |
10f9c755 LP |
358 | |
359 | int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) { | |
360 | struct btrfs_ioctl_ino_lookup_args args = { | |
361 | .objectid = BTRFS_FIRST_FREE_OBJECTID | |
362 | }; | |
363 | ||
364 | assert(fd >= 0); | |
365 | assert(ret); | |
366 | ||
367 | if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0) | |
368 | return -errno; | |
369 | ||
370 | *ret = args.treeid; | |
371 | return 0; | |
372 | } | |
373 | ||
5743a585 LP |
374 | static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) { |
375 | assert(args); | |
376 | ||
377 | /* the objectid, type, offset together make up the btrfs key, | |
378 | * which is considered a single 136byte integer when | |
379 | * comparing. This call increases the counter by one, dealing | |
380 | * with the overflow between the overflows */ | |
381 | ||
382 | if (args->key.min_offset < (uint64_t) -1) { | |
383 | args->key.min_offset++; | |
384 | return true; | |
385 | } | |
386 | ||
387 | if (args->key.min_type < (uint8_t) -1) { | |
388 | args->key.min_type++; | |
389 | args->key.min_offset = 0; | |
390 | return true; | |
391 | } | |
392 | ||
393 | if (args->key.min_objectid < (uint64_t) -1) { | |
394 | args->key.min_objectid++; | |
395 | args->key.min_offset = 0; | |
396 | args->key.min_type = 0; | |
397 | return true; | |
398 | } | |
399 | ||
400 | return 0; | |
401 | } | |
402 | ||
403 | static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) { | |
404 | assert(args); | |
405 | assert(h); | |
406 | ||
407 | args->key.min_objectid = h->objectid; | |
408 | args->key.min_type = h->type; | |
409 | args->key.min_offset = h->offset; | |
410 | } | |
411 | ||
412 | static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) { | |
413 | assert(args); | |
414 | ||
415 | /* Compare min and max */ | |
416 | ||
417 | if (args->key.min_objectid < args->key.max_objectid) | |
418 | return -1; | |
419 | if (args->key.min_objectid > args->key.max_objectid) | |
420 | return 1; | |
421 | ||
422 | if (args->key.min_type < args->key.max_type) | |
423 | return -1; | |
424 | if (args->key.min_type > args->key.max_type) | |
425 | return 1; | |
426 | ||
427 | if (args->key.min_offset < args->key.max_offset) | |
428 | return -1; | |
429 | if (args->key.min_offset > args->key.max_offset) | |
430 | return 1; | |
431 | ||
432 | return 0; | |
433 | } | |
434 | ||
435 | #define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \ | |
436 | for ((i) = 0, \ | |
437 | (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \ | |
438 | (i) < (args).key.nr_items; \ | |
439 | (i)++, \ | |
440 | (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len)) | |
441 | ||
442 | #define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh) \ | |
443 | ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header))) | |
444 | ||
10f9c755 LP |
445 | int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) { |
446 | struct btrfs_ioctl_search_args args = { | |
447 | /* Tree of tree roots */ | |
b6b18498 | 448 | .key.tree_id = BTRFS_ROOT_TREE_OBJECTID, |
10f9c755 LP |
449 | |
450 | /* Look precisely for the subvolume items */ | |
451 | .key.min_type = BTRFS_ROOT_ITEM_KEY, | |
452 | .key.max_type = BTRFS_ROOT_ITEM_KEY, | |
453 | ||
10f9c755 LP |
454 | .key.min_offset = 0, |
455 | .key.max_offset = (uint64_t) -1, | |
5743a585 LP |
456 | |
457 | /* No restrictions on the other components */ | |
10f9c755 LP |
458 | .key.min_transid = 0, |
459 | .key.max_transid = (uint64_t) -1, | |
10f9c755 LP |
460 | }; |
461 | ||
10f9c755 | 462 | uint64_t subvol_id; |
b6b18498 | 463 | bool found = false; |
10f9c755 LP |
464 | int r; |
465 | ||
466 | assert(fd >= 0); | |
467 | assert(ret); | |
468 | ||
469 | r = btrfs_subvol_get_id_fd(fd, &subvol_id); | |
470 | if (r < 0) | |
471 | return r; | |
472 | ||
473 | args.key.min_objectid = args.key.max_objectid = subvol_id; | |
10f9c755 | 474 | |
5743a585 | 475 | while (btrfs_ioctl_search_args_compare(&args) <= 0) { |
b6b18498 LP |
476 | const struct btrfs_ioctl_search_header *sh; |
477 | unsigned i; | |
478 | ||
479 | args.key.nr_items = 256; | |
480 | if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) | |
481 | return -errno; | |
482 | ||
483 | if (args.key.nr_items <= 0) | |
484 | break; | |
10f9c755 | 485 | |
5743a585 | 486 | FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { |
10f9c755 | 487 | |
b6b18498 LP |
488 | const struct btrfs_root_item *ri; |
489 | ||
5743a585 LP |
490 | /* Make sure we start the next search at least from this entry */ |
491 | btrfs_ioctl_search_args_set(&args, sh); | |
492 | ||
b6b18498 LP |
493 | if (sh->objectid != subvol_id) |
494 | continue; | |
495 | if (sh->type != BTRFS_ROOT_ITEM_KEY) | |
496 | continue; | |
5743a585 LP |
497 | |
498 | /* Older versions of the struct lacked the otime setting */ | |
b6b18498 LP |
499 | if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec)) |
500 | continue; | |
10f9c755 | 501 | |
5743a585 | 502 | ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); |
10f9c755 | 503 | |
b6b18498 LP |
504 | ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC + |
505 | (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC; | |
10f9c755 | 506 | |
b6b18498 LP |
507 | ret->subvol_id = subvol_id; |
508 | ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY); | |
10f9c755 | 509 | |
b6b18498 LP |
510 | assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid)); |
511 | memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid)); | |
512 | memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid)); | |
513 | ||
514 | found = true; | |
515 | goto finish; | |
516 | } | |
517 | ||
5743a585 LP |
518 | /* Increase search key by one, to read the next item, if we can. */ |
519 | if (!btrfs_ioctl_search_args_inc(&args)) | |
b6b18498 LP |
520 | break; |
521 | } | |
522 | ||
523 | finish: | |
524 | if (!found) | |
525 | return -ENODATA; | |
526 | ||
527 | return 0; | |
528 | } | |
529 | ||
530 | int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) { | |
531 | ||
532 | struct btrfs_ioctl_search_args args = { | |
533 | /* Tree of quota items */ | |
534 | .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID, | |
535 | ||
5743a585 LP |
536 | /* The object ID is always 0 */ |
537 | .key.min_objectid = 0, | |
538 | .key.max_objectid = 0, | |
539 | ||
b6b18498 LP |
540 | /* Look precisely for the quota items */ |
541 | .key.min_type = BTRFS_QGROUP_STATUS_KEY, | |
542 | .key.max_type = BTRFS_QGROUP_LIMIT_KEY, | |
543 | ||
b6b18498 LP |
544 | /* No restrictions on the other components */ |
545 | .key.min_transid = 0, | |
546 | .key.max_transid = (uint64_t) -1, | |
547 | }; | |
548 | ||
549 | uint64_t subvol_id; | |
550 | bool found_info = false, found_limit = false; | |
551 | int r; | |
552 | ||
553 | assert(fd >= 0); | |
554 | assert(ret); | |
555 | ||
556 | r = btrfs_subvol_get_id_fd(fd, &subvol_id); | |
557 | if (r < 0) | |
558 | return r; | |
559 | ||
560 | args.key.min_offset = args.key.max_offset = subvol_id; | |
561 | ||
5743a585 | 562 | while (btrfs_ioctl_search_args_compare(&args) <= 0) { |
b6b18498 LP |
563 | const struct btrfs_ioctl_search_header *sh; |
564 | unsigned i; | |
565 | ||
566 | args.key.nr_items = 256; | |
567 | if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) | |
568 | return -errno; | |
569 | ||
570 | if (args.key.nr_items <= 0) | |
571 | break; | |
572 | ||
5743a585 | 573 | FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { |
b6b18498 | 574 | |
5743a585 LP |
575 | /* Make sure we start the next search at least from this entry */ |
576 | btrfs_ioctl_search_args_set(&args, sh); | |
b6b18498 LP |
577 | |
578 | if (sh->objectid != 0) | |
579 | continue; | |
580 | if (sh->offset != subvol_id) | |
581 | continue; | |
582 | ||
b6b18498 | 583 | if (sh->type == BTRFS_QGROUP_INFO_KEY) { |
5743a585 | 584 | const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); |
b6b18498 LP |
585 | |
586 | ret->referred = le64toh(qii->rfer); | |
587 | ret->exclusive = le64toh(qii->excl); | |
588 | ||
589 | found_info = true; | |
590 | ||
591 | } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) { | |
5743a585 | 592 | const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); |
b6b18498 LP |
593 | |
594 | ret->referred_max = le64toh(qli->max_rfer); | |
595 | ret->exclusive_max = le64toh(qli->max_excl); | |
596 | ||
597 | if (ret->referred_max == 0) | |
598 | ret->referred_max = (uint64_t) -1; | |
599 | if (ret->exclusive_max == 0) | |
600 | ret->exclusive_max = (uint64_t) -1; | |
601 | ||
602 | found_limit = true; | |
603 | } | |
604 | ||
605 | if (found_info && found_limit) | |
606 | goto finish; | |
607 | } | |
608 | ||
5743a585 LP |
609 | /* Increase search key by one, to read the next item, if we can. */ |
610 | if (!btrfs_ioctl_search_args_inc(&args)) | |
b6b18498 LP |
611 | break; |
612 | } | |
613 | ||
614 | finish: | |
615 | if (!found_limit && !found_info) | |
616 | return -ENODATA; | |
617 | ||
618 | if (!found_info) { | |
619 | ret->referred = (uint64_t) -1; | |
620 | ret->exclusive = (uint64_t) -1; | |
621 | } | |
622 | ||
623 | if (!found_limit) { | |
624 | ret->referred_max = (uint64_t) -1; | |
625 | ret->exclusive_max = (uint64_t) -1; | |
626 | } | |
10f9c755 LP |
627 | |
628 | return 0; | |
629 | } | |
f27a3864 LP |
630 | |
631 | int btrfs_defrag_fd(int fd) { | |
632 | assert(fd >= 0); | |
633 | ||
634 | if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0) | |
635 | return -errno; | |
636 | ||
637 | return 0; | |
638 | } | |
639 | ||
640 | int btrfs_defrag(const char *p) { | |
641 | _cleanup_close_ int fd = -1; | |
642 | ||
643 | fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); | |
644 | if (fd < 0) | |
645 | return -errno; | |
646 | ||
647 | return btrfs_defrag_fd(fd); | |
648 | } |