2 * Copyright (C) 2018 Facebook
4 * This file is part of libbtrfsutil.
6 * libbtrfsutil is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * libbtrfsutil is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
25 #include <sys/ioctl.h>
27 #include <sys/types.h>
29 #include <linux/magic.h>
31 #include "btrfsutil_internal.h"
34 * This intentionally duplicates btrfs_util_is_subvolume_fd() instead of opening
35 * a file descriptor and calling it, because fstat() and fstatfs() don't accept
36 * file descriptors opened with O_PATH on old kernels (before v3.6 and before
37 * v3.12, respectively), but stat() and statfs() can be called on a path that
38 * the user doesn't have read or write permissions to.
40 PUBLIC enum btrfs_util_error btrfs_util_is_subvolume(const char *path)
46 ret = statfs(path, &sfs);
48 return BTRFS_UTIL_ERROR_STATFS_FAILED;
50 if (sfs.f_type != BTRFS_SUPER_MAGIC) {
52 return BTRFS_UTIL_ERROR_NOT_BTRFS;
55 ret = stat(path, &st);
57 return BTRFS_UTIL_ERROR_STAT_FAILED;
59 if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) {
61 return BTRFS_UTIL_ERROR_NOT_SUBVOLUME;
67 PUBLIC enum btrfs_util_error btrfs_util_is_subvolume_fd(int fd)
73 ret = fstatfs(fd, &sfs);
75 return BTRFS_UTIL_ERROR_STATFS_FAILED;
77 if (sfs.f_type != BTRFS_SUPER_MAGIC) {
79 return BTRFS_UTIL_ERROR_NOT_BTRFS;
84 return BTRFS_UTIL_ERROR_STAT_FAILED;
86 if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) {
88 return BTRFS_UTIL_ERROR_NOT_SUBVOLUME;
94 PUBLIC enum btrfs_util_error btrfs_util_subvolume_id(const char *path,
97 enum btrfs_util_error err;
100 fd = open(path, O_RDONLY);
102 return BTRFS_UTIL_ERROR_OPEN_FAILED;
104 err = btrfs_util_subvolume_id_fd(fd, id_ret);
105 SAVE_ERRNO_AND_CLOSE(fd);
109 PUBLIC enum btrfs_util_error btrfs_util_subvolume_id_fd(int fd,
112 struct btrfs_ioctl_ino_lookup_args args = {
114 .objectid = BTRFS_FIRST_FREE_OBJECTID,
118 ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
121 return BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED;
124 *id_ret = args.treeid;
126 return BTRFS_UTIL_OK;
129 PUBLIC enum btrfs_util_error btrfs_util_subvolume_path(const char *path,
133 enum btrfs_util_error err;
136 fd = open(path, O_RDONLY);
138 return BTRFS_UTIL_ERROR_OPEN_FAILED;
140 err = btrfs_util_subvolume_path_fd(fd, id, path_ret);
141 SAVE_ERRNO_AND_CLOSE(fd);
145 PUBLIC enum btrfs_util_error btrfs_util_subvolume_path_fd(int fd, uint64_t id,
149 size_t capacity = 4096;
152 enum btrfs_util_error err;
154 err = btrfs_util_is_subvolume_fd(fd);
158 err = btrfs_util_subvolume_id_fd(fd, &id);
163 path = malloc(capacity);
165 return BTRFS_UTIL_ERROR_NO_MEMORY;
166 p = path + capacity - 1;
169 while (id != BTRFS_FS_TREE_OBJECTID) {
170 struct btrfs_ioctl_search_args search = {
172 .tree_id = BTRFS_ROOT_TREE_OBJECTID,
175 .min_type = BTRFS_ROOT_BACKREF_KEY,
176 .max_type = BTRFS_ROOT_BACKREF_KEY,
178 .max_offset = UINT64_MAX,
180 .max_transid = UINT64_MAX,
184 struct btrfs_ioctl_ino_lookup_args lookup;
185 const struct btrfs_ioctl_search_header *header;
186 const struct btrfs_root_ref *ref;
193 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search);
196 return BTRFS_UTIL_ERROR_SEARCH_FAILED;
199 if (search.key.nr_items == 0) {
202 return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
205 header = (struct btrfs_ioctl_search_header *)search.buf;
206 ref = (struct btrfs_root_ref *)(header + 1);
207 name = (char *)(ref + 1);
208 name_len = le16_to_cpu(ref->name_len);
213 lookup.objectid = le64_to_cpu(ref->dirid);
214 ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &lookup);
217 return BTRFS_UTIL_ERROR_SEARCH_FAILED;
219 lookup_len = strlen(lookup.name);
221 total_len = name_len + lookup_len + (id != BTRFS_FS_TREE_OBJECTID);
222 if (p - total_len < path) {
223 char *new_path, *new_p;
224 size_t new_capacity = capacity * 2;
226 new_path = malloc(new_capacity);
229 return BTRFS_UTIL_ERROR_NO_MEMORY;
231 new_p = new_path + new_capacity - (path + capacity - p);
232 memcpy(new_p, p, path + capacity - p);
236 capacity = new_capacity;
239 memcpy(p, name, name_len);
241 memcpy(p, lookup.name, lookup_len);
242 if (id != BTRFS_FS_TREE_OBJECTID)
247 memmove(path, p, path + capacity - p);
251 return BTRFS_UTIL_OK;
254 static void copy_timespec(struct timespec *timespec,
255 const struct btrfs_timespec *btrfs_timespec)
257 timespec->tv_sec = le64_to_cpu(btrfs_timespec->sec);
258 timespec->tv_nsec = le32_to_cpu(btrfs_timespec->nsec);
261 static void copy_root_item(struct btrfs_util_subvolume_info *subvol,
262 const struct btrfs_root_item *root)
264 subvol->flags = le64_to_cpu(root->flags);
265 memcpy(subvol->uuid, root->uuid, sizeof(subvol->uuid));
266 memcpy(subvol->parent_uuid, root->parent_uuid,
267 sizeof(subvol->parent_uuid));
268 memcpy(subvol->received_uuid, root->received_uuid,
269 sizeof(subvol->received_uuid));
270 subvol->generation = le64_to_cpu(root->generation);
271 subvol->ctransid = le64_to_cpu(root->ctransid);
272 subvol->otransid = le64_to_cpu(root->otransid);
273 subvol->stransid = le64_to_cpu(root->stransid);
274 subvol->rtransid = le64_to_cpu(root->rtransid);
275 copy_timespec(&subvol->ctime, &root->ctime);
276 copy_timespec(&subvol->otime, &root->otime);
277 copy_timespec(&subvol->stime, &root->stime);
278 copy_timespec(&subvol->rtime, &root->rtime);
281 PUBLIC enum btrfs_util_error btrfs_util_subvolume_info(const char *path,
283 struct btrfs_util_subvolume_info *subvol)
285 enum btrfs_util_error err;
288 fd = open(path, O_RDONLY);
290 return BTRFS_UTIL_ERROR_OPEN_FAILED;
292 err = btrfs_util_subvolume_info_fd(fd, id, subvol);
293 SAVE_ERRNO_AND_CLOSE(fd);
297 PUBLIC enum btrfs_util_error btrfs_util_subvolume_info_fd(int fd, uint64_t id,
298 struct btrfs_util_subvolume_info *subvol)
300 struct btrfs_ioctl_search_args search = {
302 .tree_id = BTRFS_ROOT_TREE_OBJECTID,
303 .min_type = BTRFS_ROOT_ITEM_KEY,
304 .max_type = BTRFS_ROOT_BACKREF_KEY,
306 .max_offset = UINT64_MAX,
308 .max_transid = UINT64_MAX,
312 enum btrfs_util_error err;
313 size_t items_pos = 0, buf_off = 0;
314 bool need_root_item = true, need_root_backref = true;
318 err = btrfs_util_is_subvolume_fd(fd);
322 err = btrfs_util_subvolume_id_fd(fd, &id);
327 if ((id < BTRFS_FIRST_FREE_OBJECTID && id != BTRFS_FS_TREE_OBJECTID) ||
328 id > BTRFS_LAST_FREE_OBJECTID) {
330 return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
333 search.key.min_objectid = search.key.max_objectid = id;
337 subvol->parent_id = 0;
339 if (id == BTRFS_FS_TREE_OBJECTID)
340 need_root_backref = false;
343 * We only need the backref for filling in the subvolume info.
345 need_root_backref = false;
348 /* Don't bother searching for the backref if we don't need it. */
349 if (!need_root_backref)
350 search.key.max_type = BTRFS_ROOT_ITEM_KEY;
352 while (need_root_item || need_root_backref) {
353 const struct btrfs_ioctl_search_header *header;
355 if (items_pos >= search.key.nr_items) {
356 search.key.nr_items = 4096;
357 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search);
359 return BTRFS_UTIL_ERROR_SEARCH_FAILED;
363 if (search.key.nr_items == 0) {
364 if (need_root_item) {
366 return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
373 header = (struct btrfs_ioctl_search_header *)(search.buf + buf_off);
374 if (header->type == BTRFS_ROOT_ITEM_KEY) {
376 const struct btrfs_root_item *root;
378 root = (const struct btrfs_root_item *)(header + 1);
379 copy_root_item(subvol, root);
381 need_root_item = false;
382 search.key.min_type = BTRFS_ROOT_BACKREF_KEY;
383 } else if (header->type == BTRFS_ROOT_BACKREF_KEY) {
385 const struct btrfs_root_ref *ref;
387 ref = (const struct btrfs_root_ref *)(header + 1);
388 subvol->parent_id = header->offset;
389 subvol->dir_id = le64_to_cpu(ref->dirid);
391 need_root_backref = false;
392 search.key.min_type = UINT32_MAX;
396 buf_off += sizeof(*header) + header->len;
399 return BTRFS_UTIL_OK;
402 PUBLIC enum btrfs_util_error btrfs_util_get_subvolume_read_only_fd(int fd,
408 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
410 return BTRFS_UTIL_ERROR_SUBVOL_GETFLAGS_FAILED;
412 *read_only_ret = flags & BTRFS_SUBVOL_RDONLY;
413 return BTRFS_UTIL_OK;
416 PUBLIC enum btrfs_util_error btrfs_util_get_subvolume_read_only(const char *path,
419 enum btrfs_util_error err;
422 fd = open(path, O_RDONLY);
424 return BTRFS_UTIL_ERROR_OPEN_FAILED;
426 err = btrfs_util_get_subvolume_read_only_fd(fd, ret);
427 SAVE_ERRNO_AND_CLOSE(fd);
431 PUBLIC enum btrfs_util_error btrfs_util_set_subvolume_read_only(const char *path,
434 enum btrfs_util_error err;
437 fd = open(path, O_RDONLY);
439 return BTRFS_UTIL_ERROR_OPEN_FAILED;
441 err = btrfs_util_set_subvolume_read_only_fd(fd, read_only);
442 SAVE_ERRNO_AND_CLOSE(fd);
446 PUBLIC enum btrfs_util_error btrfs_util_set_subvolume_read_only_fd(int fd,
452 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
454 return BTRFS_UTIL_ERROR_SUBVOL_GETFLAGS_FAILED;
457 flags |= BTRFS_SUBVOL_RDONLY;
459 flags &= ~BTRFS_SUBVOL_RDONLY;
461 ret = ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &flags);
463 return BTRFS_UTIL_ERROR_SUBVOL_SETFLAGS_FAILED;
465 return BTRFS_UTIL_OK;
468 PUBLIC enum btrfs_util_error btrfs_util_get_default_subvolume(const char *path,
471 enum btrfs_util_error err;
474 fd = open(path, O_RDONLY);
476 return BTRFS_UTIL_ERROR_OPEN_FAILED;
478 err = btrfs_util_get_default_subvolume_fd(fd, id_ret);
479 SAVE_ERRNO_AND_CLOSE(fd);
483 PUBLIC enum btrfs_util_error btrfs_util_get_default_subvolume_fd(int fd,
486 struct btrfs_ioctl_search_args search = {
488 .tree_id = BTRFS_ROOT_TREE_OBJECTID,
489 .min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID,
490 .max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID,
491 .min_type = BTRFS_DIR_ITEM_KEY,
492 .max_type = BTRFS_DIR_ITEM_KEY,
494 .max_offset = UINT64_MAX,
496 .max_transid = UINT64_MAX,
500 size_t items_pos = 0, buf_off = 0;
504 const struct btrfs_ioctl_search_header *header;
506 if (items_pos >= search.key.nr_items) {
507 search.key.nr_items = 4096;
508 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search);
510 return BTRFS_UTIL_ERROR_SEARCH_FAILED;
514 if (search.key.nr_items == 0) {
516 return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
520 header = (struct btrfs_ioctl_search_header *)(search.buf + buf_off);
521 if (header->type == BTRFS_DIR_ITEM_KEY) {
522 const struct btrfs_dir_item *dir;
526 dir = (struct btrfs_dir_item *)(header + 1);
527 name = (const char *)(dir + 1);
528 name_len = le16_to_cpu(dir->name_len);
529 if (strncmp(name, "default", name_len) == 0) {
530 *id_ret = le64_to_cpu(dir->location.objectid);
536 buf_off += sizeof(*header) + header->len;
537 search.key.min_offset = header->offset + 1;
540 return BTRFS_UTIL_OK;
543 PUBLIC enum btrfs_util_error btrfs_util_set_default_subvolume(const char *path,
546 enum btrfs_util_error err;
549 fd = open(path, O_RDONLY);
551 return BTRFS_UTIL_ERROR_OPEN_FAILED;
553 err = btrfs_util_set_default_subvolume_fd(fd, id);
554 SAVE_ERRNO_AND_CLOSE(fd);
558 PUBLIC enum btrfs_util_error btrfs_util_set_default_subvolume_fd(int fd,
561 enum btrfs_util_error err;
565 err = btrfs_util_is_subvolume_fd(fd);
569 err = btrfs_util_subvolume_id_fd(fd, &id);
574 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &id);
576 return BTRFS_UTIL_ERROR_DEFAULT_SUBVOL_FAILED;
578 return BTRFS_UTIL_OK;
581 static enum btrfs_util_error openat_parent_and_name(int dirfd, const char *path,
582 char *name, size_t name_len,
585 char *tmp_path, *slash, *dirname, *basename;
588 /* Ignore trailing slashes. */
590 while (len > 1 && path[len - 1] == '/')
593 tmp_path = malloc(len + 1);
595 return BTRFS_UTIL_ERROR_NO_MEMORY;
596 memcpy(tmp_path, path, len);
597 tmp_path[len] = '\0';
599 slash = memrchr(tmp_path, '/', len);
600 if (slash == tmp_path) {
602 basename = tmp_path + 1;
606 basename = slash + 1;
612 len = strlen(basename);
613 if (len >= name_len) {
615 errno = ENAMETOOLONG;
616 return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
618 memcpy(name, basename, len);
621 *fd = openat(dirfd, dirname, O_RDONLY | O_DIRECTORY);
624 return BTRFS_UTIL_ERROR_OPEN_FAILED;
628 return BTRFS_UTIL_OK;
631 PUBLIC enum btrfs_util_error btrfs_util_create_subvolume(const char *path,
633 uint64_t *async_transid,
634 struct btrfs_util_qgroup_inherit *qgroup_inherit)
636 char name[BTRFS_SUBVOL_NAME_MAX + 1];
637 enum btrfs_util_error err;
640 err = openat_parent_and_name(AT_FDCWD, path, name, sizeof(name),
645 err = btrfs_util_create_subvolume_fd(parent_fd, name, flags,
646 async_transid, qgroup_inherit);
647 SAVE_ERRNO_AND_CLOSE(parent_fd);
651 PUBLIC enum btrfs_util_error btrfs_util_create_subvolume_fd(int parent_fd,
654 uint64_t *async_transid,
655 struct btrfs_util_qgroup_inherit *qgroup_inherit)
657 struct btrfs_ioctl_vol_args_v2 args = {};
663 return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
667 args.flags |= BTRFS_SUBVOL_CREATE_ASYNC;
668 if (qgroup_inherit) {
669 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
670 args.qgroup_inherit = (struct btrfs_qgroup_inherit *)qgroup_inherit;
671 args.size = (sizeof(*args.qgroup_inherit) +
672 args.qgroup_inherit->num_qgroups *
673 sizeof(args.qgroup_inherit->qgroups[0]));
677 if (len >= sizeof(args.name)) {
678 errno = ENAMETOOLONG;
679 return BTRFS_UTIL_ERROR_INVALID_ARGUMENT;
681 memcpy(args.name, name, len);
682 args.name[len] = '\0';
684 ret = ioctl(parent_fd, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
686 return BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED;
689 *async_transid = args.transid;
691 return BTRFS_UTIL_OK;