btrfs-progs: add getopt stubs where needed
[platform/upstream/btrfs-progs.git] / cmds-subvolume.c
index 4258ea1..02e1dec 100644 (file)
 #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 <linux/magic.h>
 
 #include "kerncompat.h"
 #include "ioctl.h"
@@ -66,34 +68,35 @@ static int is_subvolume_cleaned(int fd, u64 subvolid)
 static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
                int sleep_interval)
 {
-       int ret = 0;
-       int remaining;
+       int ret;
        int i;
 
-       remaining = count;
        while (1) {
+               int clean = 1;
+
                for (i = 0; i < 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));
-                               goto out;
+                               error(
+                           "cannot read status of dead subvolume %llu: %s",
+                                       (unsigned long long)ids[i], strerror(-ret));
+                               return ret;
                        }
                        if (ret) {
                                printf("Subvolume id %llu is gone\n", ids[i]);
                                ids[i] = 0;
-                               remaining--;
+                       } else {
+                               clean = 0;
                        }
                }
-               if (!remaining)
+               if (clean)
                        break;
                sleep(sleep_interval);
        }
-out:
-       return ret;
+
+       return 0;
 }
 
 static const char * const subvolume_cmd_group_usage[] = {
@@ -157,8 +160,12 @@ static int cmd_subvol_create(int argc, char **argv)
 
        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) {
-               fprintf(stderr, "ERROR: '%s' exists\n", dst);
+               error("target path already exists: %s", dst);
                goto out;
        }
 
@@ -168,23 +175,19 @@ static int cmd_subvol_create(int argc, char **argv)
        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) {
-               fprintf(stderr, "ERROR: subvolume name too long '%s'\n",
-                       newname);
+               error("subvolume name too long: %s", newname);
                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;
-       }
 
        printf("Create subvolume '%s/%s'\n", dstdir, newname);
        if (inherit) {
@@ -207,8 +210,7 @@ static int cmd_subvol_create(int argc, char **argv)
        }
 
        if (res < 0) {
-               fprintf(stderr, "ERROR: cannot create subvolume - %s\n",
-                       strerror(errno));
+               error("cannot create subvolume: %s", strerror(errno));
                goto out;
        }
 
@@ -223,22 +225,30 @@ out:
 }
 
 /*
- * 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
+ * Test if path is a subvolume
+ * Returns:
+ *   0 - path exists but it is not a subvolume
+ *   1 - path exists and it is  a subvolume
+ * < 0 - error
  */
-int test_issubvolume(char *path)
+int test_issubvolume(const char *path)
 {
        struct stat     st;
+       struct statfs stfs;
        int             res;
 
        res = stat(path, &st);
-       if(res < 0 )
-               return -1;
+       if (res < 0)
+               return -errno;
+
+       if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode))
+               return 0;
+
+       res = statfs(path, &stfs);
+       if (res < 0)
+               return -errno;
 
-       return (st.st_ino == 256) && S_ISDIR(st.st_mode);
+       return (int)stfs.f_type == BTRFS_SUPER_MAGIC;
 }
 
 static int wait_for_commit(int fd)
@@ -268,7 +278,7 @@ static const char * const cmd_subvol_delete_usage[] = {
 
 static int cmd_subvol_delete(int argc, char **argv)
 {
-       int     res, e, ret = 0;
+       int res, ret = 0;
        int cnt;
        int fd = -1;
        struct btrfs_ioctl_vol_args     args;
@@ -324,12 +334,12 @@ again:
 
        res = test_issubvolume(path);
        if (res < 0) {
-               fprintf(stderr, "ERROR: error accessing '%s'\n", path);
+               error("cannot access subvolume %s: %s", path, strerror(-res));
                ret = 1;
                goto out;
        }
        if (!res) {
-               fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
+               error("not a subvolume: %s", path);
                ret = 1;
                goto out;
        }
@@ -337,7 +347,7 @@ again:
        cpath = realpath(path, NULL);
        if (!cpath) {
                ret = errno;
-               fprintf(stderr, "ERROR: finding real path for '%s': %s\n",
+               error("cannot find real path for '%s': %s",
                        path, strerror(errno));
                goto out;
        }
@@ -347,9 +357,8 @@ again:
        vname = basename(dupvname);
        free(cpath);
 
-       fd = open_file_or_dir(dname, &dirstream);
+       fd = btrfs_open_dir(dname, &dirstream, 1);
        if (fd < 0) {
-               fprintf(stderr, "ERROR: can't access '%s'\n", dname);
                ret = 1;
                goto out;
        }
@@ -360,11 +369,9 @@ again:
        memset(&args, 0, sizeof(args));
        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));
+               error("cannot delete '%s/%s': %s", dname, vname,
+                       strerror(errno));
                ret = 1;
                goto out;
        }
@@ -372,8 +379,7 @@ again:
        if (commit_mode == 1) {
                res = wait_for_commit(fd);
                if (res < 0) {
-                       fprintf(stderr,
-                               "ERROR: unable to wait for commit after '%s': %s\n",
+                       error("unable to wait for commit after '%s': %s",
                                path, strerror(errno));
                        ret = 1;
                }
@@ -396,8 +402,7 @@ out:
        if (commit_mode == 2 && fd != -1) {
                res = wait_for_commit(fd);
                if (res < 0) {
-                       fprintf(stderr,
-                               "ERROR: unable to do final sync: %s\n",
+                       error("unable to do final sync after deletion: %s",
                                strerror(errno));
                        ret = 1;
                }
@@ -563,16 +568,16 @@ static int cmd_subvol_list(int argc, char **argv)
        }
 
        subvol = argv[optind];
-       fd = open_file_or_dir(subvol, &dirstream);
+       fd = btrfs_open_dir(subvol, &dirstream, 1);
        if (fd < 0) {
                ret = -1;
-               fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
+               error("can't access '%s'", subvol);
                goto out;
        }
 
        ret = btrfs_list_get_path_rootid(fd, &top_id);
        if (ret) {
-               fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
+               error("can't get rootid for '%s'", subvol);
                goto out;
        }
 
@@ -684,17 +689,21 @@ static int cmd_subvol_snapshot(int argc, char **argv)
        retval = 1;     /* failure */
        res = test_issubvolume(subvol);
        if (res < 0) {
-               fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
+               error("cannot access subvolume %s: %s", subvol, strerror(-res));
                goto out;
        }
        if (!res) {
-               fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
+               error("not a subvolume: %s", subvol);
                goto out;
        }
 
        res = test_isdir(dst);
+       if (res < 0 && res != -ENOENT) {
+               error("cannot access %s: %s", dst, strerror(-res));
+               goto out;
+       }
        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;
        }
 
@@ -710,29 +719,23 @@ static int cmd_subvol_snapshot(int argc, char **argv)
        }
 
        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) {
-               fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
-                       newname);
+               error("snapshot name too long '%s'", newname);
                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;
-       }
 
-       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;
-       }
 
        if (readonly) {
                args.flags |= BTRFS_SUBVOL_RDONLY;
@@ -754,8 +757,7 @@ static int cmd_subvol_snapshot(int argc, char **argv)
        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': %s", subvol, strerror(errno));
                goto out;
        }
 
@@ -786,26 +788,26 @@ static int cmd_subvol_get_default(int argc, char **argv)
        u64 default_id;
        DIR *dirstream = NULL;
 
-       if (check_argc_exact(argc, 2))
+       clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
+
+       if (check_argc_exact(argc - optind, 2))
                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(subvol, &dirstream, 1);
+       if (fd < 0)
                return 1;
-       }
 
        ret = btrfs_list_get_default_subvolume(fd, &default_id);
        if (ret) {
-               fprintf(stderr, "ERROR: can't perform the search - %s\n",
+               error("failed to look up default subvolume: %s",
                        strerror(errno));
                goto out;
        }
 
        ret = 1;
        if (default_id == 0) {
-               fprintf(stderr, "ERROR: 'default' dir item not found\n");
+               error("'default' dir item not found");
                goto out;
        }
 
@@ -850,25 +852,25 @@ static int cmd_subvol_set_default(int argc, char **argv)
        char    *subvolid;
        DIR     *dirstream = NULL;
 
-       if (check_argc_exact(argc, 3))
+       clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
+
+       if (check_argc_exact(argc - optind, 2))
                usage(cmd_subvol_set_default_usage);
 
-       subvolid = argv[1];
-       path = argv[2];
+       subvolid = argv[optind];
+       path = argv[optind + 1];
 
        objectid = arg_strtou64(subvolid);
 
-       fd = open_file_or_dir(path, &dirstream);
-       if (fd < 0) {
-               fprintf(stderr, "ERROR: can't access '%s'\n", path);
+       fd = btrfs_open_dir(path, &dirstream, 1);
+       if (fd < 0)
                return 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",
+               error("unable to set a new default subvolume: %s",
                        strerror(e));
                return 1;
        }
@@ -889,31 +891,31 @@ static int cmd_subvol_find_new(int argc, char **argv)
        u64 last_gen;
        DIR *dirstream = NULL;
 
-       if (check_argc_exact(argc, 3))
+       clean_args_no_options(argc, argv, cmd_subvol_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);
+               error("cannot access subvolume %s: %s", subvol, strerror(-ret));
                return 1;
        }
        if (!ret) {
-               fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
+               error("not a subvolume: %s", subvol);
                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;
-       }
 
        ret = ioctl(fd, BTRFS_IOC_SYNC);
        if (ret < 0) {
-               fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
+               error("sync ioctl failed on '%s': %s",
                        subvol, strerror(errno));
                close_file_or_dir(fd, dirstream);
                return 1;
@@ -943,63 +945,59 @@ static int cmd_subvol_show(int argc, char **argv)
        int ret = 1;
        DIR *dirstream1 = NULL, *dirstream2 = NULL;
 
-       if (check_argc_exact(argc, 2))
+       clean_args_no_options(argc, argv, cmd_subvol_show_usage);
+
+       if (check_argc_exact(argc - optind, 1))
                usage(cmd_subvol_show_usage);
 
-       fullpath = realpath(argv[1], NULL);
+       fullpath = realpath(argv[optind], NULL);
        if (!fullpath) {
-               fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
-                       argv[1], strerror(errno));
+               error("cannot find real path for '%s': %s",
+                       argv[optind], strerror(errno));
                goto out;
        }
 
        ret = test_issubvolume(fullpath);
        if (ret < 0) {
-               fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
+               error("cannot access subvolume %s: %s", fullpath,
+                       strerror(-ret));
                goto out;
        }
        if (!ret) {
-               fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
+               error("not a subvolume: %s", fullpath);
                ret = 1;
                goto out;
        }
 
        ret = find_mount_root(fullpath, &mnt);
        if (ret < 0) {
-               fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
-                               "%s\n", fullpath, strerror(-ret));
+               error("find_mount_root failed on '%s': %s",
+                       fullpath, strerror(-ret));
                goto out;
        }
        if (ret > 0) {
-               fprintf(stderr,
-                       "ERROR: %s doesn't belong to btrfs mount point\n",
-                       fullpath);
+               error("%s doesn't belong to btrfs mount point", fullpath);
                goto out;
        }
        ret = 1;
        svpath = get_subvol_name(mnt, fullpath);
 
-       fd = open_file_or_dir(fullpath, &dirstream1);
-       if (fd < 0) {
-               fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
+       fd = btrfs_open_dir(fullpath, &dirstream1, 1);
+       if (fd < 0)
                goto out;
-       }
 
        ret = btrfs_list_get_path_rootid(fd, &sv_id);
        if (ret) {
-               fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
-                       fullpath);
+               error("can't get rootid for '%s'", fullpath);
                goto out;
        }
 
-       mntfd = open_file_or_dir(mnt, &dirstream2);
-       if (mntfd < 0) {
-               fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
+       mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
+       if (mntfd < 0)
                goto out;
-       }
 
        if (sv_id == BTRFS_FS_TREE_OBJECTID) {
-               printf("%s is btrfs root\n", fullpath);
+               printf("%s is toplevel subvolume\n", fullpath);
                goto out;
        }
 
@@ -1008,8 +1006,7 @@ static int cmd_subvol_show(int argc, char **argv)
 
        ret = btrfs_get_subvol(mntfd, &get_ri);
        if (ret) {
-               fprintf(stderr, "ERROR: can't find '%s'\n",
-                       svpath);
+               error("can't find '%s'", svpath);
                goto out;
        }
 
@@ -1163,12 +1160,13 @@ again:
  * Enumerate all dead subvolumes that exist in the filesystem.
  * Fill @ids and reallocate to bigger size if needed.
  */
-static int enumerate_dead_subvols(int fd, int count, u64 **ids)
+static int enumerate_dead_subvols(int fd, u64 **ids)
 {
        int ret;
        struct btrfs_ioctl_search_args args;
        struct btrfs_ioctl_search_key *sk = &args.key;
        int idx = 0;
+       int count = 0;
 
        memset(&args, 0, sizeof(args));
 
@@ -1183,6 +1181,7 @@ static int enumerate_dead_subvols(int fd, int count, u64 **ids)
        sk->max_transid = (u64)-1;
        sk->nr_items = 4096;
 
+       *ids = NULL;
        while (1) {
                struct btrfs_ioctl_search_header *sh;
                unsigned long off;
@@ -1201,8 +1200,6 @@ static int enumerate_dead_subvols(int fd, int count, u64 **ids)
                        off += sizeof(*sh);
 
                        if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
-                               *ids[idx] = sh->offset;
-                               idx++;
                                if (idx >= count) {
                                        u64 *newids;
 
@@ -1212,6 +1209,8 @@ static int enumerate_dead_subvols(int fd, int count, u64 **ids)
                                                return -ENOMEM;
                                        *ids = newids;
                                }
+                               (*ids)[idx] = sh->offset;
+                               idx++;
                        }
                        off += sh->len;
 
@@ -1253,8 +1252,7 @@ static int cmd_subvol_sync(int argc, char **argv)
                case 's':
                        sleep_interval = atoi(argv[optind]);
                        if (sleep_interval < 1) {
-                               fprintf(stderr,
-                                       "ERROR: invalid sleep interval %s\n",
+                               error("invalid sleep interval %s",
                                        argv[optind]);
                                ret = 1;
                                goto out;
@@ -1268,9 +1266,8 @@ static int cmd_subvol_sync(int argc, char **argv)
        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) {
-               fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
                ret = 1;
                goto out;
        }
@@ -1278,16 +1275,9 @@ static int cmd_subvol_sync(int argc, char **argv)
 
        id_count = argc - optind;
        if (!id_count) {
-               id_count = SUBVOL_ID_BATCH;
-               ids = (u64*)malloc(id_count * sizeof(u64));
-               if (!ids) {
-                       fprintf(stderr, "ERROR: not enough memory\n");
-                       ret = 1;
-                       goto out;
-               }
-               id_count = enumerate_dead_subvols(fd, id_count, &ids);
+               id_count = enumerate_dead_subvols(fd, &ids);
                if (id_count < 0) {
-                       fprintf(stderr, "ERROR: can't enumerate dead subvolumes: %s\n",
+                       error("can't enumerate dead subvolumes: %s",
                                        strerror(-id_count));
                        ret = 1;
                        goto out;
@@ -1299,7 +1289,7 @@ static int cmd_subvol_sync(int argc, char **argv)
        } else {
                ids = (u64*)malloc(id_count * sizeof(u64));
                if (!ids) {
-                       fprintf(stderr, "ERROR: not enough memory\n");
+                       error("not enough memory");
                        ret = 1;
                        goto out;
                }
@@ -1312,17 +1302,13 @@ static int cmd_subvol_sync(int argc, char **argv)
                        errno = 0;
                        id = strtoull(arg, NULL, 10);
                        if (errno < 0) {
-                               fprintf(stderr,
-                                       "ERROR: unrecognized subvolume id %s\n",
-                                       arg);
+                               error("unrecognized subvolume id %s", arg);
                                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);
+                               error("subvolume id %s out of range\n", arg);
                                ret = 1;
                                goto out;
                        }