btrfs-progs: gitignore: Ignore *.restored test image
[platform/upstream/btrfs-progs.git] / cmds-subvolume.c
index b14f86e..ba57eaa 100644 (file)
@@ -14,6 +14,7 @@
  * Boston, MA 021110-1307, USA.
  */
 
  * Boston, MA 021110-1307, USA.
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <errno.h>
 #include <sys/stat.h>
 #include <sys/ioctl.h>
 #include <errno.h>
 #include <sys/stat.h>
+#include <sys/vfs.h>
 #include <libgen.h>
 #include <limits.h>
 #include <getopt.h>
 #include <uuid/uuid.h>
 #include <libgen.h>
 #include <limits.h>
 #include <getopt.h>
 #include <uuid/uuid.h>
+#include <linux/magic.h>
+
+#include <btrfsutil.h>
 
 #include "kerncompat.h"
 #include "ioctl.h"
 
 #include "kerncompat.h"
 #include "ioctl.h"
 #include "utils.h"
 #include "btrfs-list.h"
 #include "utils.h"
 #include "utils.h"
 #include "btrfs-list.h"
 #include "utils.h"
+#include "help.h"
+
+static int wait_for_subvolume_cleaning(int fd, size_t count, uint64_t *ids,
+                                      int sleep_interval)
+{
+       size_t i;
+       enum btrfs_util_error err;
+
+       while (1) {
+               int clean = 1;
+
+               for (i = 0; i < count; i++) {
+                       if (!ids[i])
+                               continue;
+                       err = btrfs_util_subvolume_info_fd(fd, ids[i], NULL);
+                       if (err == BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND) {
+                               printf("Subvolume id %" PRIu64 " is gone\n",
+                                      ids[i]);
+                               ids[i] = 0;
+                       } else if (err) {
+                               error_btrfs_util(err);
+                               return -errno;
+                       } else {
+                               clean = 0;
+                       }
+               }
+               if (clean)
+                       break;
+               sleep(sleep_interval);
+       }
+
+       return 0;
+}
 
 static const char * const subvolume_cmd_group_usage[] = {
        "btrfs subvolume <command> <args>",
 
 static const char * const subvolume_cmd_group_usage[] = {
        "btrfs subvolume <command> <args>",
@@ -64,9 +102,8 @@ static int cmd_subvol_create(int argc, char **argv)
        struct btrfs_qgroup_inherit *inherit = NULL;
        DIR     *dirstream = NULL;
 
        struct btrfs_qgroup_inherit *inherit = NULL;
        DIR     *dirstream = NULL;
 
-       optind = 1;
        while (1) {
        while (1) {
-               int c = getopt(argc, argv, "c:i:v");
+               int c = getopt(argc, argv, "c:i:");
                if (c < 0)
                        break;
 
                if (c < 0)
                        break;
 
@@ -97,8 +134,12 @@ static int cmd_subvol_create(int argc, char **argv)
 
        retval = 1;     /* failure */
        res = test_isdir(dst);
 
        retval = 1;     /* failure */
        res = test_isdir(dst);
+       if (res < 0 && res != -ENOENT) {
+               error("cannot access %s: %s", dst, strerror(-res));
+               goto out;
+       }
        if (res >= 0) {
        if (res >= 0) {
-               fprintf(stderr, "ERROR: '%s' exists\n", dst);
+               error("target path already exists: %s", dst);
                goto out;
        }
 
                goto out;
        }
 
@@ -108,23 +149,19 @@ static int cmd_subvol_create(int argc, char **argv)
        dstdir = dirname(dupdir);
 
        if (!test_issubvolname(newname)) {
        dstdir = dirname(dupdir);
 
        if (!test_issubvolname(newname)) {
-               fprintf(stderr, "ERROR: incorrect subvolume name '%s'\n",
-                       newname);
+               error("invalid subvolume name: %s", newname);
                goto out;
        }
 
        len = strlen(newname);
        if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
                goto out;
        }
 
        len = strlen(newname);
        if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
-               fprintf(stderr, "ERROR: subvolume name too long '%s'\n",
-                       newname);
+               error("subvolume name too long: %s", newname);
                goto out;
        }
 
                goto out;
        }
 
-       fddst = open_file_or_dir(dstdir, &dirstream);
-       if (fddst < 0) {
-               fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
+       fddst = btrfs_open_dir(dstdir, &dirstream, 1);
+       if (fddst < 0)
                goto out;
                goto out;
-       }
 
        printf("Create subvolume '%s/%s'\n", dstdir, newname);
        if (inherit) {
 
        printf("Create subvolume '%s/%s'\n", dstdir, newname);
        if (inherit) {
@@ -147,8 +184,7 @@ static int cmd_subvol_create(int argc, char **argv)
        }
 
        if (res < 0) {
        }
 
        if (res < 0) {
-               fprintf(stderr, "ERROR: cannot create subvolume - %s\n",
-                       strerror(errno));
+               error("cannot create subvolume: %m");
                goto out;
        }
 
                goto out;
        }
 
@@ -162,33 +198,20 @@ out:
        return retval;
 }
 
        return retval;
 }
 
-/*
- * test if path is a subvolume:
- * this function return
- * 0-> path exists but it is not a subvolume
- * 1-> path exists and it is  a subvolume
- * -1 -> path is unaccessible
- */
-int test_issubvolume(char *path)
+static int wait_for_commit(int fd)
 {
 {
-       struct stat     st;
-       int             res;
+       enum btrfs_util_error err;
+       uint64_t transid;
 
 
-       res = stat(path, &st);
-       if(res < 0 )
+       err = btrfs_util_start_sync_fd(fd, &transid);
+       if (err)
                return -1;
 
                return -1;
 
-       return (st.st_ino == 256) && S_ISDIR(st.st_mode);
-}
-
-static int wait_for_commit(int fd)
-{
-       int ret;
+       err = btrfs_util_wait_sync_fd(fd, transid);
+       if (err)
+               return -1;
 
 
-       ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
-       if (ret < 0)
-               return ret;
-       return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
+       return 0;
 }
 
 static const char * const cmd_subvol_delete_usage[] = {
 }
 
 static const char * const cmd_subvol_delete_usage[] = {
@@ -203,42 +226,47 @@ static const char * const cmd_subvol_delete_usage[] = {
        "",
        "-c|--commit-after      wait for transaction commit at the end of the operation",
        "-C|--commit-each       wait for transaction commit after deleting each subvolume",
        "",
        "-c|--commit-after      wait for transaction commit at the end of the operation",
        "-C|--commit-each       wait for transaction commit after deleting each subvolume",
+       "-v|--verbose           verbose output of operations",
        NULL
 };
 
 static int cmd_subvol_delete(int argc, char **argv)
 {
        NULL
 };
 
 static int cmd_subvol_delete(int argc, char **argv)
 {
-       int     res, len, e, ret = 0;
+       int res, ret = 0;
        int cnt;
        int fd = -1;
        int cnt;
        int fd = -1;
-       struct btrfs_ioctl_vol_args     args;
        char    *dname, *vname, *cpath;
        char    *dupdname = NULL;
        char    *dupvname = NULL;
        char    *path;
        DIR     *dirstream = NULL;
        int verbose = 0;
        char    *dname, *vname, *cpath;
        char    *dupdname = NULL;
        char    *dupvname = NULL;
        char    *path;
        DIR     *dirstream = NULL;
        int verbose = 0;
-       int sync_mode = 0;
-       struct option long_options[] = {
-               {"commit-after", no_argument, NULL, 'c'},  /* sync mode 1 */
-               {"commit-each", no_argument, NULL, 'C'},  /* sync mode 2 */
-               {NULL, 0, NULL, 0}
-       };
-
-       optind = 1;
+       int commit_mode = 0;
+       u8 fsid[BTRFS_FSID_SIZE];
+       char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
+       struct seen_fsid *seen_fsid_hash[SEEN_FSID_HASH_SIZE] = { NULL, };
+       enum { COMMIT_AFTER = 1, COMMIT_EACH = 2 };
+       enum btrfs_util_error err;
+
        while (1) {
                int c;
        while (1) {
                int c;
-
-               c = getopt_long(argc, argv, "cC", long_options, NULL);
+               static const struct option long_options[] = {
+                       {"commit-after", no_argument, NULL, 'c'},
+                       {"commit-each", no_argument, NULL, 'C'},
+                       {"verbose", no_argument, NULL, 'v'},
+                       {NULL, 0, NULL, 0}
+               };
+
+               c = getopt_long(argc, argv, "cCv", long_options, NULL);
                if (c < 0)
                        break;
 
                switch(c) {
                case 'c':
                if (c < 0)
                        break;
 
                switch(c) {
                case 'c':
-                       sync_mode = 1;
+                       commit_mode = COMMIT_AFTER;
                        break;
                case 'C':
                        break;
                case 'C':
-                       sync_mode = 2;
+                       commit_mode = COMMIT_EACH;
                        break;
                case 'v':
                        verbose++;
                        break;
                case 'v':
                        verbose++;
@@ -253,8 +281,8 @@ static int cmd_subvol_delete(int argc, char **argv)
 
        if (verbose > 0) {
                printf("Transaction commit: %s\n",
 
        if (verbose > 0) {
                printf("Transaction commit: %s\n",
-                       !sync_mode ? "none (default)" :
-                       sync_mode == 1 ? "at the end" : "after each");
+                       !commit_mode ? "none (default)" :
+                       commit_mode == COMMIT_AFTER ? "at the end" : "after each");
        }
 
        cnt = optind;
        }
 
        cnt = optind;
@@ -262,14 +290,9 @@ static int cmd_subvol_delete(int argc, char **argv)
 again:
        path = argv[cnt];
 
 again:
        path = argv[cnt];
 
-       res = test_issubvolume(path);
-       if (res < 0) {
-               fprintf(stderr, "ERROR: error accessing '%s'\n", path);
-               ret = 1;
-               goto out;
-       }
-       if (!res) {
-               fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
+       err = btrfs_util_is_subvolume(path);
+       if (err) {
+               error_btrfs_util(err);
                ret = 1;
                goto out;
        }
                ret = 1;
                goto out;
        }
@@ -277,8 +300,7 @@ again:
        cpath = realpath(path, NULL);
        if (!cpath) {
                ret = errno;
        cpath = realpath(path, NULL);
        if (!cpath) {
                ret = errno;
-               fprintf(stderr, "ERROR: finding real path for '%s': %s\n",
-                       path, strerror(errno));
+               error("cannot find real path for '%s': %m", path);
                goto out;
        }
        dupdname = strdup(cpath);
                goto out;
        }
        dupdname = strdup(cpath);
@@ -287,76 +309,97 @@ again:
        vname = basename(dupvname);
        free(cpath);
 
        vname = basename(dupvname);
        free(cpath);
 
-       if (!test_issubvolname(vname)) {
-               fprintf(stderr, "ERROR: incorrect subvolume name '%s'\n",
-                       vname);
-               ret = 1;
-               goto out;
-       }
-
-       len = strlen(vname);
-       if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
-               fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
-                       vname);
-               ret = 1;
-               goto out;
-       }
-
-       fd = open_file_or_dir(dname, &dirstream);
+       fd = btrfs_open_dir(dname, &dirstream, 1);
        if (fd < 0) {
        if (fd < 0) {
-               fprintf(stderr, "ERROR: can't access '%s'\n", dname);
                ret = 1;
                goto out;
        }
 
        printf("Delete subvolume (%s): '%s/%s'\n",
                ret = 1;
                goto out;
        }
 
        printf("Delete subvolume (%s): '%s/%s'\n",
-               sync_mode == 2 || (sync_mode == 1 && cnt + 1 == argc)
+               commit_mode == COMMIT_EACH || (commit_mode == COMMIT_AFTER && cnt + 1 == argc)
                ? "commit" : "no-commit", dname, vname);
                ? "commit" : "no-commit", dname, vname);
-       strncpy_null(args.name, vname);
-       res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
-       e = errno;
 
 
-       if(res < 0 ){
-               fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
-                       dname, vname, strerror(e));
+       err = btrfs_util_delete_subvolume_fd(fd, vname, 0);
+       if (err) {
+               error_btrfs_util(err);
                ret = 1;
                goto out;
        }
 
                ret = 1;
                goto out;
        }
 
-       if (sync_mode == 1) {
+       if (commit_mode == COMMIT_EACH) {
                res = wait_for_commit(fd);
                if (res < 0) {
                res = wait_for_commit(fd);
                if (res < 0) {
-                       fprintf(stderr,
-                               "ERROR: unable to wait for commit after '%s': %s\n",
-                               path, strerror(errno));
+                       error("unable to wait for commit after '%s': %m", path);
+                       ret = 1;
+               }
+       } else if (commit_mode == COMMIT_AFTER) {
+               res = get_fsid(dname, fsid, 0);
+               if (res < 0) {
+                       error("unable to get fsid for '%s': %s",
+                               path, strerror(-res));
+                       error(
+                       "delete suceeded but commit may not be done in the end");
                        ret = 1;
                        ret = 1;
+                       goto out;
+               }
+
+               if (add_seen_fsid(fsid, seen_fsid_hash, fd, dirstream) == 0) {
+                       if (verbose > 0) {
+                               uuid_unparse(fsid, uuidbuf);
+                               printf("  new fs is found for '%s', fsid: %s\n",
+                                               path, uuidbuf);
+                       }
+                       /*
+                        * This is the first time a subvolume on this
+                        * filesystem is deleted, keep fd in order to issue
+                        * SYNC ioctl in the end
+                        */
+                       goto keep_fd;
                }
        }
 
 out:
                }
        }
 
 out:
+       close_file_or_dir(fd, dirstream);
+keep_fd:
+       fd = -1;
+       dirstream = NULL;
        free(dupdname);
        free(dupvname);
        dupdname = NULL;
        dupvname = NULL;
        cnt++;
        free(dupdname);
        free(dupvname);
        dupdname = NULL;
        dupvname = NULL;
        cnt++;
-       if (cnt < argc) {
-               close_file_or_dir(fd, dirstream);
-               /* avoid double free */
-               fd = -1;
-               dirstream = NULL;
+       if (cnt < argc)
                goto again;
                goto again;
-       }
 
 
-       if (sync_mode == 2 && fd != -1) {
-               res = wait_for_commit(fd);
-               if (res < 0) {
-                       fprintf(stderr,
-                               "ERROR: unable to do final sync: %s\n",
-                               strerror(errno));
-                       ret = 1;
+       if (commit_mode == COMMIT_AFTER) {
+               int slot;
+
+               /*
+                * Traverse seen_fsid_hash and issue SYNC ioctl on each
+                * filesystem
+                */
+               for (slot = 0; slot < SEEN_FSID_HASH_SIZE; slot++) {
+                       struct seen_fsid *seen = seen_fsid_hash[slot];
+
+                       while (seen) {
+                               res = wait_for_commit(seen->fd);
+                               if (res < 0) {
+                                       uuid_unparse(seen->fsid, uuidbuf);
+                                       error(
+                       "unable to do final sync after deletion: %m, fsid: %s",
+                                               uuidbuf);
+                                       ret = 1;
+                               } else if (verbose > 0) {
+                                       uuid_unparse(seen->fsid, uuidbuf);
+                                       printf("final sync is done for fsid: %s\n",
+                                               uuidbuf);
+                               }
+                               seen = seen->next;
+                       }
                }
                }
+               /* fd will also be closed in free_seen_fsid */
+               free_seen_fsid(seen_fsid_hash);
        }
        }
-       close_file_or_dir(fd, dirstream);
 
        return ret;
 }
 
        return ret;
 }
@@ -367,24 +410,32 @@ out:
  * - lowercase for enabling specific items in the output
  */
 static const char * const cmd_subvol_list_usage[] = {
  * - lowercase for enabling specific items in the output
  */
 static const char * const cmd_subvol_list_usage[] = {
-       "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
-       "[--sort=gen,ogen,rootid,path] <path>",
-       "List subvolumes (and snapshots)",
+       "btrfs subvolume list [options] <path>",
+       "List subvolumes and snapshots in the filesystem.",
        "",
        "",
-       "-p           print parent ID",
+       "Path filtering:",
+       "-o           print only subvolumes below specified path",
        "-a           print all the subvolumes in the filesystem and",
        "             distinguish absolute and relative path with respect",
        "             to the given <path>",
        "-a           print all the subvolumes in the filesystem and",
        "             distinguish absolute and relative path with respect",
        "             to the given <path>",
+       "",
+       "Field selection:",
+       "-p           print parent ID",
        "-c           print the ogeneration of the subvolume",
        "-g           print the generation of the subvolume",
        "-c           print the ogeneration of the subvolume",
        "-g           print the generation of the subvolume",
-       "-o           print only subvolumes below specified path",
        "-u           print the uuid of subvolumes (and snapshots)",
        "-q           print the parent uuid of the snapshots",
        "-R           print the uuid of the received snapshots",
        "-u           print the uuid of subvolumes (and snapshots)",
        "-q           print the parent uuid of the snapshots",
        "-R           print the uuid of the received snapshots",
-       "-t           print the result as a table",
-       "-s           list snapshots only in the filesystem",
+       "",
+       "Type filtering:",
+       "-s           list only snapshots",
        "-r           list readonly subvolumes (including snapshots)",
        "-d           list deleted subvolumes that are not yet cleaned",
        "-r           list readonly subvolumes (including snapshots)",
        "-d           list deleted subvolumes that are not yet cleaned",
+       "",
+       "Other:",
+       "-t           print the result as a table",
+       "",
+       "Sorting:",
        "-G [+|-]value",
        "             filter the subvolumes by generation",
        "             (+value: >= value; -value: <= value; value: = value)",
        "-G [+|-]value",
        "             filter the subvolumes by generation",
        "             (+value: >= value; -value: <= value; value: = value)",
@@ -406,22 +457,22 @@ static int cmd_subvol_list(int argc, char **argv)
        int fd = -1;
        u64 top_id;
        int ret = -1, uerr = 0;
        int fd = -1;
        u64 top_id;
        int ret = -1, uerr = 0;
-       int c;
        char *subvol;
        char *subvol;
-       int is_tab_result = 0;
        int is_list_all = 0;
        int is_only_in_path = 0;
        int is_list_all = 0;
        int is_only_in_path = 0;
-       struct option long_options[] = {
-               {"sort", 1, NULL, 'S'},
-               {NULL, 0, NULL, 0}
-       };
        DIR *dirstream = NULL;
        DIR *dirstream = NULL;
+       enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT;
 
        filter_set = btrfs_list_alloc_filter_set();
        comparer_set = btrfs_list_alloc_comparer_set();
 
 
        filter_set = btrfs_list_alloc_filter_set();
        comparer_set = btrfs_list_alloc_comparer_set();
 
-       optind = 1;
        while(1) {
        while(1) {
+               int c;
+               static const struct option long_options[] = {
+                       {"sort", required_argument, NULL, 'S'},
+                       {NULL, 0, NULL, 0}
+               };
+
                c = getopt_long(argc, argv,
                                    "acdgopqsurRG:C:t", long_options, NULL);
                if (c < 0)
                c = getopt_long(argc, argv,
                                    "acdgopqsurRG:C:t", long_options, NULL);
                if (c < 0)
@@ -449,7 +500,7 @@ static int cmd_subvol_list(int argc, char **argv)
                        is_only_in_path = 1;
                        break;
                case 't':
                        is_only_in_path = 1;
                        break;
                case 't':
-                       is_tab_result = 1;
+                       layout = BTRFS_LIST_LAYOUT_TABLE;
                        break;
                case 's':
                        btrfs_list_setup_filter(&filter_set,
                        break;
                case 's':
                        btrfs_list_setup_filter(&filter_set,
@@ -506,28 +557,26 @@ static int cmd_subvol_list(int argc, char **argv)
                }
        }
 
                }
        }
 
-       if (flags)
-               btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
-                                       flags);
-
        if (check_argc_exact(argc - optind, 1)) {
                uerr = 1;
                goto out;
        }
 
        subvol = argv[optind];
        if (check_argc_exact(argc - optind, 1)) {
                uerr = 1;
                goto out;
        }
 
        subvol = argv[optind];
-       fd = open_file_or_dir(subvol, &dirstream);
+       fd = btrfs_open_dir(subvol, &dirstream, 1);
        if (fd < 0) {
                ret = -1;
        if (fd < 0) {
                ret = -1;
-               fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
+               error("can't access '%s'", subvol);
                goto out;
        }
 
                goto out;
        }
 
+       if (flags)
+               btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
+                                       flags);
+
        ret = btrfs_list_get_path_rootid(fd, &top_id);
        ret = btrfs_list_get_path_rootid(fd, &top_id);
-       if (ret) {
-               fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
+       if (ret)
                goto out;
                goto out;
-       }
 
        if (is_list_all)
                btrfs_list_setup_filter(&filter_set,
 
        if (is_list_all)
                btrfs_list_setup_filter(&filter_set,
@@ -544,28 +593,21 @@ static int cmd_subvol_list(int argc, char **argv)
        btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
        btrfs_list_setup_print_column(BTRFS_LIST_PATH);
 
        btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
        btrfs_list_setup_print_column(BTRFS_LIST_PATH);
 
-       if (is_tab_result)
-               ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
-                               BTRFS_LIST_LAYOUT_TABLE,
-                               !is_list_all && !is_only_in_path, NULL);
-       else
-               ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
-                               BTRFS_LIST_LAYOUT_DEFAULT,
-                               !is_list_all && !is_only_in_path, NULL);
+       ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
+                       layout, !is_list_all && !is_only_in_path, NULL);
 
 out:
        close_file_or_dir(fd, dirstream);
        if (filter_set)
 
 out:
        close_file_or_dir(fd, dirstream);
        if (filter_set)
-               btrfs_list_free_filter_set(filter_set);
+               free(filter_set);
        if (comparer_set)
        if (comparer_set)
-               btrfs_list_free_comparer_set(comparer_set);
+               free(comparer_set);
        if (uerr)
                usage(cmd_subvol_list_usage);
        return !!ret;
 }
 
        if (uerr)
                usage(cmd_subvol_list_usage);
        return !!ret;
 }
 
-static const char * const cmd_snapshot_usage[] = {
-       "btrfs subvolume snapshot [-r] <source> <dest>|[<dest>/]<name>",
+static const char * const cmd_subvol_snapshot_usage[] = {
        "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
        "Create a snapshot of the subvolume",
        "Create a writable/readonly snapshot of the subvolume <source> with",
        "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
        "Create a snapshot of the subvolume",
        "Create a writable/readonly snapshot of the subvolume <source> with",
@@ -578,7 +620,7 @@ static const char * const cmd_snapshot_usage[] = {
        NULL
 };
 
        NULL
 };
 
-static int cmd_snapshot(int argc, char **argv)
+static int cmd_subvol_snapshot(int argc, char **argv)
 {
        char    *subvol, *dst;
        int     res, retval;
 {
        char    *subvol, *dst;
        int     res, retval;
@@ -588,11 +630,11 @@ static int cmd_snapshot(int argc, char **argv)
        char    *dupdir = NULL;
        char    *newname;
        char    *dstdir;
        char    *dupdir = NULL;
        char    *newname;
        char    *dstdir;
+       enum btrfs_util_error err;
        struct btrfs_ioctl_vol_args_v2  args;
        struct btrfs_qgroup_inherit *inherit = NULL;
        DIR *dirstream1 = NULL, *dirstream2 = NULL;
 
        struct btrfs_ioctl_vol_args_v2  args;
        struct btrfs_qgroup_inherit *inherit = NULL;
        DIR *dirstream1 = NULL, *dirstream2 = NULL;
 
-       optind = 1;
        memset(&args, 0, sizeof(args));
        while (1) {
                int c = getopt(argc, argv, "c:i:r");
        memset(&args, 0, sizeof(args));
        while (1) {
                int c = getopt(argc, argv, "c:i:r");
@@ -625,30 +667,30 @@ static int cmd_snapshot(int argc, char **argv)
                        }
                        break;
                default:
                        }
                        break;
                default:
-                       usage(cmd_snapshot_usage);
+                       usage(cmd_subvol_snapshot_usage);
                }
        }
 
        if (check_argc_exact(argc - optind, 2))
                }
        }
 
        if (check_argc_exact(argc - optind, 2))
-               usage(cmd_snapshot_usage);
+               usage(cmd_subvol_snapshot_usage);
 
        subvol = argv[optind];
        dst = argv[optind + 1];
 
        retval = 1;     /* failure */
 
        subvol = argv[optind];
        dst = argv[optind + 1];
 
        retval = 1;     /* failure */
-       res = test_issubvolume(subvol);
-       if (res < 0) {
-               fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
-               goto out;
-       }
-       if (!res) {
-               fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
+       err = btrfs_util_is_subvolume(subvol);
+       if (err) {
+               error_btrfs_util(err);
                goto out;
        }
 
        res = test_isdir(dst);
                goto out;
        }
 
        res = test_isdir(dst);
+       if (res < 0 && res != -ENOENT) {
+               error("cannot access %s: %s", dst, strerror(-res));
+               goto out;
+       }
        if (res == 0) {
        if (res == 0) {
-               fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
+               error("'%s' exists and it is not a directory", dst);
                goto out;
        }
 
                goto out;
        }
 
@@ -664,29 +706,23 @@ static int cmd_snapshot(int argc, char **argv)
        }
 
        if (!test_issubvolname(newname)) {
        }
 
        if (!test_issubvolname(newname)) {
-               fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n",
-                       newname);
+               error("invalid snapshot name '%s'", newname);
                goto out;
        }
 
        len = strlen(newname);
        if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
                goto out;
        }
 
        len = strlen(newname);
        if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
-               fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
-                       newname);
+               error("snapshot name too long '%s'", newname);
                goto out;
        }
 
                goto out;
        }
 
-       fddst = open_file_or_dir(dstdir, &dirstream1);
-       if (fddst < 0) {
-               fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
+       fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
+       if (fddst < 0)
                goto out;
                goto out;
-       }
 
 
-       fd = open_file_or_dir(subvol, &dirstream2);
-       if (fd < 0) {
-               fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
+       fd = btrfs_open_dir(subvol, &dirstream2, 1);
+       if (fd < 0)
                goto out;
                goto out;
-       }
 
        if (readonly) {
                args.flags |= BTRFS_SUBVOL_RDONLY;
 
        if (readonly) {
                args.flags |= BTRFS_SUBVOL_RDONLY;
@@ -708,8 +744,7 @@ static int cmd_snapshot(int argc, char **argv)
        res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
 
        if (res < 0) {
        res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
 
        if (res < 0) {
-               fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
-                       subvol, strerror(errno));
+               error("cannot snapshot '%s': %m", subvol);
                goto out;
        }
 
                goto out;
        }
 
@@ -734,32 +769,25 @@ static const char * const cmd_subvol_get_default_usage[] = {
 static int cmd_subvol_get_default(int argc, char **argv)
 {
        int fd = -1;
 static int cmd_subvol_get_default(int argc, char **argv)
 {
        int fd = -1;
-       int ret;
-       char *subvol;
-       struct btrfs_list_filter_set *filter_set;
-       u64 default_id;
+       int ret = 1;
+       uint64_t default_id;
        DIR *dirstream = NULL;
        DIR *dirstream = NULL;
+       enum btrfs_util_error err;
+       struct btrfs_util_subvolume_info subvol;
+       char *path;
+
+       clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
 
 
-       if (check_argc_exact(argc, 2))
+       if (check_argc_exact(argc - optind, 1))
                usage(cmd_subvol_get_default_usage);
 
                usage(cmd_subvol_get_default_usage);
 
-       subvol = argv[1];
-       fd = open_file_or_dir(subvol, &dirstream);
-       if (fd < 0) {
-               fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
+       fd = btrfs_open_dir(argv[1], &dirstream, 1);
+       if (fd < 0)
                return 1;
                return 1;
-       }
 
 
-       ret = btrfs_list_get_default_subvolume(fd, &default_id);
-       if (ret) {
-               fprintf(stderr, "ERROR: can't perform the search - %s\n",
-                       strerror(errno));
-               goto out;
-       }
-
-       ret = 1;
-       if (default_id == 0) {
-               fprintf(stderr, "ERROR: 'default' dir item not found\n");
+       err = btrfs_util_get_default_subvolume_fd(fd, &default_id);
+       if (err) {
+               error_btrfs_util(err);
                goto out;
        }
 
                goto out;
        }
 
@@ -770,105 +798,104 @@ static int cmd_subvol_get_default(int argc, char **argv)
                goto out;
        }
 
                goto out;
        }
 
-       filter_set = btrfs_list_alloc_filter_set();
-       btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
-                               default_id);
+       err = btrfs_util_subvolume_info_fd(fd, default_id, &subvol);
+       if (err) {
+               error_btrfs_util(err);
+               goto out;
+       }
 
 
-       /* by default we shall print the following columns*/
-       btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
-       btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
-       btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
-       btrfs_list_setup_print_column(BTRFS_LIST_PATH);
+       err = btrfs_util_subvolume_path_fd(fd, default_id, &path);
+       if (err) {
+               error_btrfs_util(err);
+               goto out;
+       }
 
 
-       ret = btrfs_list_subvols_print(fd, filter_set, NULL,
-               BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
+       printf("ID %" PRIu64 " gen %" PRIu64 " top level %" PRIu64 " path %s\n",
+              subvol.id, subvol.generation, subvol.parent_id, path);
 
 
-       if (filter_set)
-               btrfs_list_free_filter_set(filter_set);
+       free(path);
+
+       ret = 0;
 out:
        close_file_or_dir(fd, dirstream);
 out:
        close_file_or_dir(fd, dirstream);
-       return !!ret;
+       return ret;
 }
 
 static const char * const cmd_subvol_set_default_usage[] = {
 }
 
 static const char * const cmd_subvol_set_default_usage[] = {
+       "btrfs subvolume set-default <subvolume>\n"
        "btrfs subvolume set-default <subvolid> <path>",
        "btrfs subvolume set-default <subvolid> <path>",
-       "Set the default subvolume of a filesystem",
+       "Set the default subvolume of the filesystem mounted as default.",
+       "The subvolume can be specified by its path,",
+       "or the pair of subvolume id and path to the filesystem.",
        NULL
 };
 
 static int cmd_subvol_set_default(int argc, char **argv)
 {
        NULL
 };
 
 static int cmd_subvol_set_default(int argc, char **argv)
 {
-       int     ret=0, fd, e;
-       u64     objectid;
-       char    *path;
-       char    *subvolid;
-       DIR     *dirstream = NULL;
+       u64 objectid;
+       char *path;
+       enum btrfs_util_error err;
 
 
-       if (check_argc_exact(argc, 3))
-               usage(cmd_subvol_set_default_usage);
+       clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
 
 
-       subvolid = argv[1];
-       path = argv[2];
-
-       objectid = arg_strtou64(subvolid);
+       if (check_argc_min(argc - optind, 1) ||
+                       check_argc_max(argc - optind, 2))
+               usage(cmd_subvol_set_default_usage);
 
 
-       fd = open_file_or_dir(path, &dirstream);
-       if (fd < 0) {
-               fprintf(stderr, "ERROR: can't access '%s'\n", path);
-               return 1;
+       if (argc - optind == 1) {
+               /* path to the subvolume is specified */
+               objectid = 0;
+               path = argv[optind];
+       } else {
+               /* subvol id and path to the filesystem are specified */
+               objectid = arg_strtou64(argv[optind]);
+               path = argv[optind + 1];
        }
 
        }
 
-       ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
-       e = errno;
-       close_file_or_dir(fd, dirstream);
-       if (ret < 0) {
-               fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
-                       strerror(e));
+       err = btrfs_util_set_default_subvolume(path, objectid);
+       if (err) {
+               error_btrfs_util(err);
                return 1;
        }
        return 0;
 }
 
                return 1;
        }
        return 0;
 }
 
-static const char * const cmd_find_new_usage[] = {
+static const char * const cmd_subvol_find_new_usage[] = {
        "btrfs subvolume find-new <path> <lastgen>",
        "List the recently modified files in a filesystem",
        NULL
 };
 
        "btrfs subvolume find-new <path> <lastgen>",
        "List the recently modified files in a filesystem",
        NULL
 };
 
-static int cmd_find_new(int argc, char **argv)
+static int cmd_subvol_find_new(int argc, char **argv)
 {
        int fd;
        int ret;
        char *subvol;
        u64 last_gen;
        DIR *dirstream = NULL;
 {
        int fd;
        int ret;
        char *subvol;
        u64 last_gen;
        DIR *dirstream = NULL;
+       enum btrfs_util_error err;
+
+       clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
 
 
-       if (check_argc_exact(argc, 3))
-               usage(cmd_find_new_usage);
+       if (check_argc_exact(argc - optind, 2))
+               usage(cmd_subvol_find_new_usage);
 
 
-       subvol = argv[1];
-       last_gen = arg_strtou64(argv[2]);
+       subvol = argv[optind];
+       last_gen = arg_strtou64(argv[optind + 1]);
 
 
-       ret = test_issubvolume(subvol);
-       if (ret < 0) {
-               fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
-               return 1;
-       }
-       if (!ret) {
-               fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
+       err = btrfs_util_is_subvolume(subvol);
+       if (err) {
+               error_btrfs_util(err);
                return 1;
        }
 
                return 1;
        }
 
-       fd = open_file_or_dir(subvol, &dirstream);
-       if (fd < 0) {
-               fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
+       fd = btrfs_open_dir(subvol, &dirstream, 1);
+       if (fd < 0)
                return 1;
                return 1;
-       }
 
 
-       ret = ioctl(fd, BTRFS_IOC_SYNC);
-       if (ret < 0) {
-               fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
-                       subvol, strerror(errno));
+       err = btrfs_util_sync_fd(fd);
+       if (err) {
+               error_btrfs_util(err);
                close_file_or_dir(fd, dirstream);
                return 1;
        }
                close_file_or_dir(fd, dirstream);
                return 1;
        }
@@ -879,155 +906,205 @@ static int cmd_find_new(int argc, char **argv)
 }
 
 static const char * const cmd_subvol_show_usage[] = {
 }
 
 static const char * const cmd_subvol_show_usage[] = {
-       "btrfs subvolume show <subvol-path>",
-       "Show more information of the subvolume",
+       "btrfs subvolume show [options] <subvol-path>|<mnt>",
+       "Show more information about the subvolume",
+       "-r|--rootid   rootid of the subvolume",
+       "-u|--uuid     uuid of the subvolume",
+       "",
+       "If no option is specified, <subvol-path> will be shown, otherwise",
+       "the rootid or uuid are resolved relative to the <mnt> path.",
        NULL
 };
 
 static int cmd_subvol_show(int argc, char **argv)
 {
        NULL
 };
 
 static int cmd_subvol_show(int argc, char **argv)
 {
-       struct root_info get_ri;
-       struct btrfs_list_filter_set *filter_set;
        char tstr[256];
        char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
        char tstr[256];
        char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
-       char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
-       char raw_prefix[] = "\t\t\t\t";
-       u64 sv_id, mntid;
-       int fd = -1, mntfd = -1;
+       char *fullpath = NULL;
+       int fd = -1;
        int ret = 1;
        int ret = 1;
-       DIR *dirstream1 = NULL, *dirstream2 = NULL;
+       DIR *dirstream1 = NULL;
+       int by_rootid = 0;
+       int by_uuid = 0;
+       u64 rootid_arg = 0;
+       u8 uuid_arg[BTRFS_UUID_SIZE];
+       struct btrfs_util_subvolume_iterator *iter;
+       struct btrfs_util_subvolume_info subvol;
+       char *subvol_path = NULL;
+       enum btrfs_util_error err;
 
 
-       if (check_argc_exact(argc, 2))
-               usage(cmd_subvol_show_usage);
+       while (1) {
+               int c;
+               static const struct option long_options[] = {
+                       { "rootid", required_argument, NULL, 'r'},
+                       { "uuid", required_argument, NULL, 'u'},
+                       { NULL, 0, NULL, 0 }
+               };
 
 
-       fullpath = realpath(argv[1], NULL);
-       if (!fullpath) {
-               fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
-                       argv[1], strerror(errno));
-               goto out;
-       }
+               c = getopt_long(argc, argv, "r:u:", long_options, NULL);
+               if (c < 0)
+                       break;
 
 
-       ret = test_issubvolume(fullpath);
-       if (ret < 0) {
-               fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
-               goto out;
-       }
-       if (!ret) {
-               fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
-               ret = 1;
-               goto out;
+               switch (c) {
+               case 'r':
+                       rootid_arg = arg_strtou64(optarg);
+                       by_rootid = 1;
+                       break;
+               case 'u':
+                       uuid_parse(optarg, uuid_arg);
+                       by_uuid = 1;
+                       break;
+               default:
+                       usage(cmd_subvol_show_usage);
+               }
        }
 
        }
 
-       ret = find_mount_root(fullpath, &mnt);
-       if (ret < 0) {
-               fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
-                               "%s\n", fullpath, strerror(-ret));
-               goto out;
+       if (check_argc_exact(argc - optind, 1))
+               usage(cmd_subvol_show_usage);
+
+       if (by_rootid && by_uuid) {
+               error(
+               "options --rootid and --uuid cannot be used at the same time");
+               usage(cmd_subvol_show_usage);
        }
        }
-       if (ret > 0) {
-               fprintf(stderr,
-                       "ERROR: %s doesn't belong to btrfs mount point\n",
-                       fullpath);
+
+       fullpath = realpath(argv[optind], NULL);
+       if (!fullpath) {
+               error("cannot find real path for '%s': %m", argv[optind]);
                goto out;
        }
                goto out;
        }
-       ret = 1;
-       svpath = get_subvol_name(mnt, fullpath);
 
        fd = open_file_or_dir(fullpath, &dirstream1);
        if (fd < 0) {
 
        fd = open_file_or_dir(fullpath, &dirstream1);
        if (fd < 0) {
-               fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
+               error("can't access '%s'", fullpath);
                goto out;
        }
 
                goto out;
        }
 
-       ret = btrfs_list_get_path_rootid(fd, &sv_id);
-       if (ret) {
-               fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
-                       fullpath);
-               goto out;
-       }
+       if (by_uuid) {
+               err = btrfs_util_create_subvolume_iterator_fd(fd,
+                                                             BTRFS_FS_TREE_OBJECTID,
+                                                             0, &iter);
+               if (err) {
+                       error_btrfs_util(err);
+                       goto out;
+               }
 
 
-       mntfd = open_file_or_dir(mnt, &dirstream2);
-       if (mntfd < 0) {
-               fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
-               goto out;
-       }
+               for (;;) {
+                       err = btrfs_util_subvolume_iterator_next_info(iter,
+                                                                     &subvol_path,
+                                                                     &subvol);
+                       if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
+                               uuid_unparse(uuid_arg, uuidparse);
+                               error("can't find uuid '%s' on '%s'", uuidparse,
+                                     fullpath);
+                               btrfs_util_destroy_subvolume_iterator(iter);
+                               goto out;
+                       } else if (err) {
+                               error_btrfs_util(err);
+                               btrfs_util_destroy_subvolume_iterator(iter);
+                               goto out;
+                       }
 
 
-       ret = btrfs_list_get_path_rootid(mntfd, &mntid);
-       if (ret) {
-               fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
-               goto out;
-       }
+                       if (uuid_compare(subvol.uuid, uuid_arg) == 0)
+                               break;
 
 
-       if (sv_id == BTRFS_FS_TREE_OBJECTID) {
-               printf("%s is btrfs root\n", fullpath);
-               goto out;
-       }
+                       free(subvol_path);
+               }
+               btrfs_util_destroy_subvolume_iterator(iter);
+       } else {
+               /*
+                * If !by_rootid, rootid_arg = 0, which means find the
+                * subvolume ID of the fd and use that.
+                */
+               err = btrfs_util_subvolume_info_fd(fd, rootid_arg, &subvol);
+               if (err) {
+                       error_btrfs_util(err);
+                       goto out;
+               }
 
 
-       memset(&get_ri, 0, sizeof(get_ri));
-       get_ri.root_id = sv_id;
+               err = btrfs_util_subvolume_path_fd(fd, subvol.id, &subvol_path);
+               if (err) {
+                       error_btrfs_util(err);
+                       goto out;
+               }
 
 
-       ret = btrfs_get_subvol(mntfd, &get_ri);
-       if (ret) {
-               fprintf(stderr, "ERROR: can't find '%s'\n",
-                       svpath);
-               goto out;
        }
 
        /* print the info */
        }
 
        /* print the info */
-       printf("%s\n", fullpath);
-       printf("\tName: \t\t\t%s\n", get_ri.name);
+       printf("%s\n", subvol.id == BTRFS_FS_TREE_OBJECTID ? "/" : subvol_path);
+       printf("\tName: \t\t\t%s\n",
+              (subvol.id == BTRFS_FS_TREE_OBJECTID ? "<FS_TREE>" :
+               basename(subvol_path)));
 
 
-       if (uuid_is_null(get_ri.uuid))
+       if (uuid_is_null(subvol.uuid))
                strcpy(uuidparse, "-");
        else
                strcpy(uuidparse, "-");
        else
-               uuid_unparse(get_ri.uuid, uuidparse);
-       printf("\tuuid: \t\t\t%s\n", uuidparse);
+               uuid_unparse(subvol.uuid, uuidparse);
+       printf("\tUUID: \t\t\t%s\n", uuidparse);
 
 
-       if (uuid_is_null(get_ri.puuid))
+       if (uuid_is_null(subvol.parent_uuid))
                strcpy(uuidparse, "-");
        else
                strcpy(uuidparse, "-");
        else
-               uuid_unparse(get_ri.puuid, uuidparse);
-       printf("\tParent uuid: \t\t%s\n", uuidparse);
+               uuid_unparse(subvol.parent_uuid, uuidparse);
+       printf("\tParent UUID: \t\t%s\n", uuidparse);
 
 
-       if (get_ri.otime) {
+       if (uuid_is_null(subvol.received_uuid))
+               strcpy(uuidparse, "-");
+       else
+               uuid_unparse(subvol.received_uuid, uuidparse);
+       printf("\tReceived UUID: \t\t%s\n", uuidparse);
+
+       if (subvol.otime.tv_sec) {
                struct tm tm;
 
                struct tm tm;
 
-               localtime_r(&get_ri.otime, &tm);
-               strftime(tstr, 256, "%Y-%m-%d %X", &tm);
+               localtime_r(&subvol.otime.tv_sec, &tm);
+               strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
        } else
                strcpy(tstr, "-");
        printf("\tCreation time: \t\t%s\n", tstr);
 
        } else
                strcpy(tstr, "-");
        printf("\tCreation time: \t\t%s\n", tstr);
 
-       printf("\tObject ID: \t\t%llu\n", get_ri.root_id);
-       printf("\tGeneration (Gen): \t%llu\n", get_ri.gen);
-       printf("\tGen at creation: \t%llu\n", get_ri.ogen);
-       printf("\tParent: \t\t%llu\n", get_ri.ref_tree);
-       printf("\tTop Level: \t\t%llu\n", get_ri.top_id);
+       printf("\tSubvolume ID: \t\t%" PRIu64 "\n", subvol.id);
+       printf("\tGeneration: \t\t%" PRIu64 "\n", subvol.generation);
+       printf("\tGen at creation: \t%" PRIu64 "\n", subvol.otransid);
+       printf("\tParent ID: \t\t%" PRIu64 "\n", subvol.parent_id);
+       printf("\tTop level ID: \t\t%" PRIu64 "\n", subvol.parent_id);
 
 
-       if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
+       if (subvol.flags & BTRFS_ROOT_SUBVOL_RDONLY)
                printf("\tFlags: \t\t\treadonly\n");
        else
                printf("\tFlags: \t\t\t-\n");
 
        /* print the snapshots of the given subvol if any*/
        printf("\tSnapshot(s):\n");
                printf("\tFlags: \t\t\treadonly\n");
        else
                printf("\tFlags: \t\t\t-\n");
 
        /* print the snapshots of the given subvol if any*/
        printf("\tSnapshot(s):\n");
-       filter_set = btrfs_list_alloc_filter_set();
-       btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
-                               (u64)(unsigned long)get_ri.uuid);
-       btrfs_list_setup_print_column(BTRFS_LIST_PATH);
-       btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
-                       1, raw_prefix);
 
 
-       /* clean up */
-       free(get_ri.path);
-       free(get_ri.name);
-       free(get_ri.full_path);
-       btrfs_list_free_filter_set(filter_set);
+       err = btrfs_util_create_subvolume_iterator_fd(fd,
+                                                     BTRFS_FS_TREE_OBJECTID, 0,
+                                                     &iter);
 
 
+       for (;;) {
+               struct btrfs_util_subvolume_info subvol2;
+               char *path;
+
+               err = btrfs_util_subvolume_iterator_next_info(iter, &path, &subvol2);
+               if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
+                       break;
+               } else if (err) {
+                       error_btrfs_util(err);
+                       btrfs_util_destroy_subvolume_iterator(iter);
+                       goto out;
+               }
+
+               if (uuid_compare(subvol2.parent_uuid, subvol.uuid) == 0)
+                       printf("\t\t\t\t%s\n", path);
+
+               free(path);
+       }
+       btrfs_util_destroy_subvolume_iterator(iter);
+
+       ret = 0;
 out:
 out:
+       free(subvol_path);
        close_file_or_dir(fd, dirstream1);
        close_file_or_dir(fd, dirstream1);
-       close_file_or_dir(mntfd, dirstream2);
-       free(mnt);
        free(fullpath);
        return !!ret;
 }
        free(fullpath);
        return !!ret;
 }
@@ -1037,117 +1114,24 @@ static const char * const cmd_subvol_sync_usage[] = {
        "Wait until given subvolume(s) are completely removed from the filesystem.",
        "Wait until given subvolume(s) are completely removed from the filesystem",
        "after deletion.",
        "Wait until given subvolume(s) are completely removed from the filesystem.",
        "Wait until given subvolume(s) are completely removed from the filesystem",
        "after deletion.",
-       "If no subvolume id is given, wait until all ongoing deletion requests",
-       "are complete. This may take long if new deleted subvolumes appear during",
-       "the sleep interval.",
+       "If no subvolume id is given, wait until all current deletion requests",
+       "are completed, but do not wait for subvolumes deleted meanwhile.",
+       "The status of subvolume ids is checked periodically.",
        "",
        "-s <N>       sleep N seconds between checks (default: 1)",
        NULL
 };
 
        "",
        "-s <N>       sleep N seconds between checks (default: 1)",
        NULL
 };
 
-static int is_subvolume_cleaned(int fd, u64 subvolid)
-{
-       int ret;
-       struct btrfs_ioctl_search_args args;
-       struct btrfs_ioctl_search_key *sk = &args.key;
-
-       sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
-       sk->min_objectid = subvolid;
-       sk->max_objectid = subvolid;
-       sk->min_type = BTRFS_ROOT_ITEM_KEY;
-       sk->max_type = BTRFS_ROOT_ITEM_KEY;
-       sk->min_offset = 0;
-       sk->max_offset = (u64)-1;
-       sk->min_transid = 0;
-       sk->max_transid = (u64)-1;
-       sk->nr_items = 1;
-
-       ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
-       if (ret < 0)
-               return -errno;
-
-       if (sk->nr_items == 0)
-               return 1;
-
-       return 0;
-}
-
-/*
- * If we're looking for any dead subvolume, take a shortcut and look
- * for any ORPHAN_ITEMs in the tree root
- */
-static int fs_has_dead_subvolumes(int fd)
-{
-       int ret;
-       struct btrfs_ioctl_search_args args;
-       struct btrfs_ioctl_search_key *sk = &args.key;
-       struct btrfs_ioctl_search_header sh;
-       u64 min_subvolid = 0;
-
-again:
-       sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
-       sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
-       sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
-       sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
-       sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
-       sk->min_offset = min_subvolid;
-       sk->max_offset = (u64)-1;
-       sk->min_transid = 0;
-       sk->max_transid = (u64)-1;
-       sk->nr_items = 1;
-
-       ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
-       if (ret < 0)
-               return -errno;
-
-       if (!sk->nr_items)
-               return 0;
-
-       memcpy(&sh, args.buf, sizeof(sh));
-       min_subvolid = sh.offset;
-
-       /*
-        * Verify that the root item is really there and we haven't hit
-        * a stale orphan
-        */
-       sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
-       sk->min_objectid = min_subvolid;
-       sk->max_objectid = min_subvolid;
-       sk->min_type = BTRFS_ROOT_ITEM_KEY;
-       sk->max_type = BTRFS_ROOT_ITEM_KEY;
-       sk->min_offset = 0;
-       sk->max_offset = (u64)-1;
-       sk->min_transid = 0;
-       sk->max_transid = (u64)-1;
-       sk->nr_items = 1;
-
-       ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
-       if (ret < 0)
-               return -errno;
-
-       /*
-        * Stale orphan, try the next one
-        */
-       if (!sk->nr_items) {
-               min_subvolid++;
-               goto again;
-       }
-
-       return 1;
-}
-
 static int cmd_subvol_sync(int argc, char **argv)
 {
        int fd = -1;
 static int cmd_subvol_sync(int argc, char **argv)
 {
        int fd = -1;
-       int i;
        int ret = 1;
        DIR *dirstream = NULL;
        int ret = 1;
        DIR *dirstream = NULL;
-       u64 *ids = NULL;
-       int id_count;
-       int remaining;
+       uint64_t *ids = NULL;
+       size_t id_count, i;
        int sleep_interval = 1;
        int sleep_interval = 1;
+       enum btrfs_util_error err;
 
 
-       optind = 1;
        while (1) {
                int c = getopt(argc, argv, "s:");
 
        while (1) {
                int c = getopt(argc, argv, "s:");
 
@@ -1156,11 +1140,9 @@ static int cmd_subvol_sync(int argc, char **argv)
 
                switch (c) {
                case 's':
 
                switch (c) {
                case 's':
-                       sleep_interval = atoi(argv[optind]);
+                       sleep_interval = atoi(optarg);
                        if (sleep_interval < 1) {
                        if (sleep_interval < 1) {
-                               fprintf(stderr,
-                                       "ERROR: invalid sleep interval %s\n",
-                                       argv[optind]);
+                               error("invalid sleep interval %s", optarg);
                                ret = 1;
                                goto out;
                        }
                                ret = 1;
                                goto out;
                        }
@@ -1173,89 +1155,57 @@ static int cmd_subvol_sync(int argc, char **argv)
        if (check_argc_min(argc - optind, 1))
                usage(cmd_subvol_sync_usage);
 
        if (check_argc_min(argc - optind, 1))
                usage(cmd_subvol_sync_usage);
 
-       fd = open_file_or_dir(argv[optind], &dirstream);
+       fd = btrfs_open_dir(argv[optind], &dirstream, 1);
        if (fd < 0) {
        if (fd < 0) {
-               fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
                ret = 1;
                goto out;
        }
        optind++;
 
        id_count = argc - optind;
                ret = 1;
                goto out;
        }
        optind++;
 
        id_count = argc - optind;
-
-       /*
-        * Wait for all
-        */
        if (!id_count) {
        if (!id_count) {
-               while (1) {
-                       ret = fs_has_dead_subvolumes(fd);
-                       if (ret < 0) {
-                               fprintf(stderr, "ERROR: can't perform the search - %s\n",
-                                               strerror(-ret));
-                               ret = 1;
-                               goto out;
-                       }
-                       if (!ret)
-                               goto out;
-                       sleep(sleep_interval);
-               }
-       }
-
-       /*
-        * Wait only for the requested ones
-        */
-       ids = (u64*)malloc(sizeof(u64) * id_count);
-
-       if (!ids) {
-               fprintf(stderr, "ERROR: not enough memory\n");
-               ret = 1;
-               goto out;
-       }
-
-       for (i = 0; i < id_count; i++) {
-               u64 id;
-               const char *arg;
-
-               arg = argv[optind + i];
-               errno = 0;
-               id = strtoull(arg, NULL, 10);
-               if (errno < 0) {
-                       fprintf(stderr, "ERROR: unrecognized subvolume id %s\n",
-                               arg);
+               err = btrfs_util_deleted_subvolumes_fd(fd, &ids, &id_count);
+               if (err) {
+                       error_btrfs_util(err);
                        ret = 1;
                        goto out;
                }
                        ret = 1;
                        goto out;
                }
-               if (id < BTRFS_FIRST_FREE_OBJECTID || id > BTRFS_LAST_FREE_OBJECTID) {
-                       fprintf(stderr, "ERROR: subvolume id %s out of range\n",
-                               arg);
+               if (id_count == 0) {
+                       ret = 0;
+                       goto out;
+               }
+       } else {
+               ids = malloc(id_count * sizeof(uint64_t));
+               if (!ids) {
+                       error("not enough memory");
                        ret = 1;
                        goto out;
                }
                        ret = 1;
                        goto out;
                }
-               ids[i] = id;
-       }
 
 
-       remaining = id_count;
-       while (1) {
                for (i = 0; i < id_count; i++) {
                for (i = 0; i < id_count; i++) {
-                       if (!ids[i])
-                               continue;
-                       ret = is_subvolume_cleaned(fd, ids[i]);
-                       if (ret < 0) {
-                               fprintf(stderr, "ERROR: can't perform the search - %s\n",
-                                               strerror(-ret));
+                       u64 id;
+                       const char *arg;
+
+                       arg = argv[optind + i];
+                       errno = 0;
+                       id = strtoull(arg, NULL, 10);
+                       if (errno) {
+                               error("unrecognized subvolume id %s", arg);
+                               ret = 1;
                                goto out;
                        }
                                goto out;
                        }
-                       if (ret) {
-                               printf("Subvolume id %llu is gone\n", ids[i]);
-                               ids[i] = 0;
-                               remaining--;
+                       if (id < BTRFS_FIRST_FREE_OBJECTID ||
+                           id > BTRFS_LAST_FREE_OBJECTID) {
+                               error("subvolume id %s out of range", arg);
+                               ret = 1;
+                               goto out;
                        }
                        }
+                       ids[i] = id;
                }
                }
-               if (!remaining)
-                       break;
-               sleep(sleep_interval);
        }
 
        }
 
+       ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
+
 out:
        free(ids);
        close_file_or_dir(fd, dirstream);
 out:
        free(ids);
        close_file_or_dir(fd, dirstream);
@@ -1263,17 +1213,22 @@ out:
        return !!ret;
 }
 
        return !!ret;
 }
 
+static const char subvolume_cmd_group_info[] =
+"manage subvolumes: create, delete, list, etc";
+
 const struct cmd_group subvolume_cmd_group = {
 const struct cmd_group subvolume_cmd_group = {
-       subvolume_cmd_group_usage, NULL, {
+       subvolume_cmd_group_usage, subvolume_cmd_group_info, {
                { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
                { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
                { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
                { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
                { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
                { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
-               { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
+               { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
+                       NULL, 0 },
                { "get-default", cmd_subvol_get_default,
                        cmd_subvol_get_default_usage, NULL, 0 },
                { "set-default", cmd_subvol_set_default,
                        cmd_subvol_set_default_usage, NULL, 0 },
                { "get-default", cmd_subvol_get_default,
                        cmd_subvol_get_default_usage, NULL, 0 },
                { "set-default", cmd_subvol_set_default,
                        cmd_subvol_set_default_usage, NULL, 0 },
-               { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
+               { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
+                       NULL, 0 },
                { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
                { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
                NULL_CMD_STRUCT
                { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
                { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
                NULL_CMD_STRUCT