static int cmd_subvol_create(int argc, char **argv) { int retval, res, len; int fddst = -1; char *dupname = NULL; char *dupdir = NULL; char *newname; char *dstdir; char *dst; struct btrfs_qgroup_inherit *inherit = NULL; DIR *dirstream = NULL; optind = 1; while (1) { int c = getopt(argc, argv, "c:i:v"); if (c < 0) break; switch (c) { case 'c': res = qgroup_inherit_add_copy(&inherit, optarg, 0); if (res) { retval = res; goto out; } break; case 'i': res = qgroup_inherit_add_group(&inherit, optarg); if (res) { retval = res; goto out; } break; default: usage(cmd_subvol_create_usage); } } if (check_argc_exact(argc - optind, 1)) usage(cmd_subvol_create_usage); dst = argv[optind]; retval = 1; /* failure */ res = test_isdir(dst); if (res >= 0) { fprintf(stderr, "ERROR: '%s' exists\n", dst); goto out; } dupname = strdup(dst); newname = basename(dupname); dupdir = strdup(dst); dstdir = dirname(dupdir); if (!test_issubvolname(newname)) { fprintf(stderr, "ERROR: incorrect subvolume name '%s'\n", newname); goto out; } len = strlen(newname); if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { fprintf(stderr, "ERROR: subvolume name too long '%s'\n", newname); goto out; } fddst = open_file_or_dir(dstdir, &dirstream); if (fddst < 0) { fprintf(stderr, "ERROR: can't access '%s'\n", dstdir); goto out; } printf("Create subvolume '%s/%s'\n", dstdir, newname); if (inherit) { struct btrfs_ioctl_vol_args_v2 args; memset(&args, 0, sizeof(args)); strncpy_null(args.name, newname); args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT; args.size = qgroup_inherit_size(inherit); args.qgroup_inherit = inherit; res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args); } else { struct btrfs_ioctl_vol_args args; memset(&args, 0, sizeof(args)); strncpy_null(args.name, newname); res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args); } if (res < 0) { fprintf(stderr, "ERROR: cannot create subvolume - %s\n", strerror(errno)); goto out; } retval = 0; /* success */ out: close_file_or_dir(fddst, dirstream); free(inherit); free(dupname); free(dupdir); return retval; }
static int cmd_snapshot(int argc, char **argv) { char *subvol, *dst; int res, retval; int fd = -1, fddst = -1; int len, readonly = 0; char *dupname = NULL; char *dupdir = NULL; char *newname; char *dstdir; 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"); if (c < 0) break; switch (c) { case 'c': res = qgroup_inherit_add_copy(&inherit, optarg, 0); if (res) { retval = res; goto out; } break; case 'i': res = qgroup_inherit_add_group(&inherit, optarg); if (res) { retval = res; goto out; } break; case 'r': readonly = 1; break; case 'x': res = qgroup_inherit_add_copy(&inherit, optarg, 1); if (res) { retval = res; goto out; } break; default: usage(cmd_snapshot_usage); } } if (check_argc_exact(argc - optind, 2)) usage(cmd_snapshot_usage); 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); goto out; } res = test_isdir(dst); if (res == 0) { fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst); goto out; } if (res > 0) { dupname = strdup(subvol); newname = basename(dupname); dstdir = dst; } else { dupname = strdup(dst); newname = basename(dupname); dupdir = strdup(dst); dstdir = dirname(dupdir); } if (!test_issubvolname(newname)) { fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n", newname); goto out; } len = strlen(newname); if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { fprintf(stderr, "ERROR: snapshot name too long '%s'\n", newname); goto out; } fddst = open_file_or_dir(dstdir, &dirstream1); if (fddst < 0) { fprintf(stderr, "ERROR: can't access '%s'\n", dstdir); goto out; } fd = open_file_or_dir(subvol, &dirstream2); if (fd < 0) { fprintf(stderr, "ERROR: can't access '%s'\n", dstdir); goto out; } if (readonly) { args.flags |= BTRFS_SUBVOL_RDONLY; printf("Create a readonly snapshot of '%s' in '%s/%s'\n", subvol, dstdir, newname); } else { printf("Create a snapshot of '%s' in '%s/%s'\n", subvol, dstdir, newname); } args.fd = fd; if (inherit) { args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT; args.size = qgroup_inherit_size(inherit); args.qgroup_inherit = inherit; } strncpy_null(args.name, newname); res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args); if (res < 0) { fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n", subvol, strerror(errno)); goto out; } retval = 0; /* success */ out: close_file_or_dir(fddst, dirstream1); close_file_or_dir(fd, dirstream2); free(inherit); free(dupname); free(dupdir); return retval; }
static int cmd_subvol_delete(int argc, char **argv) { int res, len, e, ret = 0; 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 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; while (1) { int c; c = getopt_long(argc, argv, "cC", long_options, NULL); if (c < 0) break; switch(c) { case 'c': sync_mode = 1; break; case 'C': sync_mode = 2; break; default: usage(cmd_subvol_delete_usage); } } if (check_argc_min(argc - optind, 1)) usage(cmd_subvol_delete_usage); printf("Transaction commit: %s\n", !sync_mode ? "none (default)" : sync_mode == 1 ? "at the end" : "after each"); cnt = optind; 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); ret = 1; goto out; } cpath = realpath(path, NULL); if (!cpath) { ret = errno; fprintf(stderr, "ERROR: finding real path for '%s': %s\n", path, strerror(errno)); goto out; } dupdname = strdup(cpath); dname = dirname(dupdname); dupvname = strdup(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); if (fd < 0) { fprintf(stderr, "ERROR: can't access '%s'\n", dname); ret = 1; goto out; } printf("Delete subvolume '%s/%s'\n", 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)); ret = 1; goto out; } if (sync_mode == 1) { res = wait_for_commit(fd); if (res < 0) { fprintf(stderr, "ERROR: unable to wait for commit after '%s': %s\n", path, strerror(errno)); ret = 1; } } out: 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; 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; } } close_file_or_dir(fd, dirstream); return ret; }