#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"
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[] = {
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;
}
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) {
}
if (res < 0) {
- fprintf(stderr, "ERROR: cannot create subvolume - %s\n",
- strerror(errno));
+ error("cannot create subvolume: %s", strerror(errno));
goto 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)
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;
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
}
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;
}
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;
}
}
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;
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;
}
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;
}
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;
}
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;
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;
}
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;
}
* 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));
sk->max_transid = (u64)-1;
sk->nr_items = 4096;
+ *ids = NULL;
while (1) {
struct btrfs_ioctl_search_header *sh;
unsigned long off;
off += sizeof(*sh);
if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
- *ids[idx] = sh->offset;
- idx++;
if (idx >= count) {
u64 *newids;
return -ENOMEM;
*ids = newids;
}
+ (*ids)[idx] = sh->offset;
+ idx++;
}
off += sh->len;
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;
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;
}
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;
} 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;
}
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;
}