static int quota_ctl(int cmd, int argc, char **argv) { int ret = 0; int fd; int e; char *path = argv[1]; struct btrfs_ioctl_quota_ctl_args args; DIR *dirstream = NULL; if (check_argc_exact(argc, 2)) return -1; memset(&args, 0, sizeof(args)); args.cmd = cmd; fd = open_file_or_dir(path, &dirstream); if (fd < 0) { fprintf(stderr, "ERROR: can't access '%s'\n", path); return 1; } ret = ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args); e = errno; close_file_or_dir(fd, dirstream); if (ret < 0) { fprintf(stderr, "ERROR: quota command failed: %s\n", strerror(e)); return 1; } return 0; }
static int cmd_device_usage(int argc, char **argv) { unsigned unit_mode; int ret = 0; int i; unit_mode = get_unit_mode_from_arg(&argc, argv, 1); clean_args_no_options(argc, argv, cmd_device_usage_usage); if (check_argc_min(argc - optind, 1)) usage(cmd_device_usage_usage); for (i = optind; i < argc; i++) { int fd; DIR *dirstream = NULL; if (i > 1) printf("\n"); fd = btrfs_open_dir(argv[i], &dirstream, 1); if (fd < 0) { ret = 1; break; } ret = _cmd_device_usage(fd, argv[i], unit_mode); close_file_or_dir(fd, dirstream); if (ret) break; } return !!ret; }
static int cmd_balance_pause(int argc, char **argv) { const char *path; int fd; int ret; DIR *dirstream = NULL; clean_args_no_options(argc, argv, cmd_balance_pause_usage); if (check_argc_exact(argc - optind, 1)) return 1; path = argv[optind]; fd = btrfs_open_dir(path, &dirstream, 1); if (fd < 0) return 1; ret = ioctl(fd, BTRFS_IOC_BALANCE_CTL, BTRFS_BALANCE_CTL_PAUSE); if (ret < 0) { error("balance pause on '%s' failed: %s", path, (errno == ENOTCONN) ? "Not running" : strerror(errno)); if (errno == ENOTCONN) ret = 2; else ret = 1; } close_file_or_dir(fd, dirstream); return ret; }
static int cmd_inspect_subvolid_resolve(int argc, char **argv) { int ret; int fd = -1; u64 subvol_id; char path[PATH_MAX]; DIR *dirstream = NULL; clean_args_no_options(argc, argv, cmd_inspect_subvolid_resolve_usage); if (check_argc_exact(argc - optind, 2)) usage(cmd_inspect_subvolid_resolve_usage); fd = btrfs_open_dir(argv[optind + 1], &dirstream, 1); if (fd < 0) { ret = -ENOENT; goto out; } subvol_id = arg_strtou64(argv[optind]); ret = btrfs_subvolid_resolve(fd, path, sizeof(path), subvol_id); if (ret) { error("resolving subvolid %llu error %d", (unsigned long long)subvol_id, ret); goto out; } path[PATH_MAX - 1] = '\0'; printf("%s\n", path); out: close_file_or_dir(fd, dirstream); return ret ? 1 : 0; }
static int cmd_balance_cancel(int argc, char **argv) { const char *path; int fd; int ret; int e; DIR *dirstream = NULL; clean_args_no_options(argc, argv, cmd_balance_cancel_usage); if (check_argc_exact(argc - optind, 1)) usage(cmd_balance_cancel_usage); path = argv[optind]; fd = btrfs_open_dir(path, &dirstream, 1); if (fd < 0) return 1; ret = ioctl(fd, BTRFS_IOC_BALANCE_CTL, BTRFS_BALANCE_CTL_CANCEL); e = errno; close_file_or_dir(fd, dirstream); if (ret < 0) { error("balance cancel on '%s' failed: %s", path, (e == ENOTCONN) ? "Not in progress" : strerror(e)); if (e == ENOTCONN) return 2; else return 1; } return 0; }
static int qgroup_create(int create, int argc, char **argv) { int ret = 0; int fd; int e; char *path = argv[2]; struct btrfs_ioctl_qgroup_create_args args; DIR *dirstream = NULL; if (check_argc_exact(argc, 3)) return -1; memset(&args, 0, sizeof(args)); args.create = create; args.qgroupid = parse_qgroupid(argv[1]); fd = open_file_or_dir(path, &dirstream); if (fd < 0) { fprintf(stderr, "ERROR: can't access '%s'\n", path); return 1; } ret = ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args); e = errno; close_file_or_dir(fd, dirstream); if (ret < 0) { fprintf(stderr, "ERROR: unable to %s quota group: %s\n", create ? "create":"destroy", strerror(e)); return 1; } return 0; }
static int _cmd_qgroup_create(int create, int argc, char **argv) { int ret = 0; int fd; int e; char *path; struct btrfs_ioctl_qgroup_create_args args; DIR *dirstream = NULL; if (check_argc_exact(argc - optind, 2)) return -1; memset(&args, 0, sizeof(args)); args.create = create; args.qgroupid = parse_qgroupid(argv[optind]); path = argv[optind + 1]; fd = btrfs_open_dir(path, &dirstream, 1); if (fd < 0) return 1; ret = ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args); e = errno; close_file_or_dir(fd, dirstream); if (ret < 0) { error("unable to %s quota group: %s", create ? "create":"destroy", strerror(e)); return 1; } return 0; }
static int cmd_inspect_rootid(int argc, char **argv) { int ret; int fd = -1; u64 rootid; DIR *dirstream = NULL; clean_args_no_options(argc, argv, cmd_inspect_rootid_usage); if (check_argc_exact(argc - optind, 1)) usage(cmd_inspect_rootid_usage); fd = btrfs_open_dir(argv[optind], &dirstream, 1); if (fd < 0) { ret = -ENOENT; goto out; } ret = lookup_ino_rootid(fd, &rootid); if (ret) { error("rootid failed with ret=%d", ret); goto out; } printf("%llu\n", (unsigned long long)rootid); out: close_file_or_dir(fd, dirstream); return !!ret; }
static int cmd_subvol_set_default(int argc, char **argv) { int ret=0, fd, e; u64 objectid; char *path; char *subvolid; DIR *dirstream = NULL; if (check_argc_exact(argc, 3)) usage(cmd_subvol_set_default_usage); subvolid = argv[1]; path = argv[2]; objectid = arg_strtou64(subvolid); fd = open_file_or_dir(path, &dirstream); if (fd < 0) { fprintf(stderr, "ERROR: can't access '%s'\n", path); 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", strerror(e)); return 1; } return 0; }
static int cmd_inspect_inode_resolve(int argc, char **argv) { int fd; int verbose = 0; int ret; DIR *dirstream = NULL; optind = 1; while (1) { int c = getopt(argc, argv, "v"); if (c < 0) break; switch (c) { case 'v': verbose = 1; break; default: usage(cmd_inspect_inode_resolve_usage); } } if (check_argc_exact(argc - optind, 2)) usage(cmd_inspect_inode_resolve_usage); fd = btrfs_open_dir(argv[optind + 1], &dirstream, 1); if (fd < 0) return 1; ret = __ino_to_path_fd(arg_strtou64(argv[optind]), fd, verbose, argv[optind+1]); close_file_or_dir(fd, dirstream); return !!ret; }
static int cmd_subvol_set_default(int argc, char **argv) { int ret=0, fd, e; u64 objectid; char *path; char *subvolid; DIR *dirstream = NULL; 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[optind]; path = argv[optind + 1]; objectid = arg_strtou64(subvolid); 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) { error("unable to set a new default subvolume: %s", strerror(e)); return 1; } return 0; }
static int cmd_balance_cancel(int argc, char **argv) { const char *path; int fd; int ret; int e; DIR *dirstream = NULL; if (check_argc_exact(argc, 2)) usage(cmd_balance_cancel_usage); path = argv[1]; fd = open_file_or_dir(path, &dirstream); if (fd < 0) { fprintf(stderr, "ERROR: can't access '%s'\n", path); return 1; } ret = ioctl(fd, BTRFS_IOC_BALANCE_CTL, BTRFS_BALANCE_CTL_CANCEL); e = errno; close_file_or_dir(fd, dirstream); if (ret < 0) { fprintf(stderr, "ERROR: balance cancel on '%s' failed - %s\n", path, (e == ENOTCONN) ? "Not in progress" : strerror(e)); if (e == ENOTCONN) return 2; else return 1; } return 0; }
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; DIR *dirstream = NULL; clean_args_no_options(argc, argv, cmd_subvol_get_default_usage); if (check_argc_exact(argc - optind, 1)) usage(cmd_subvol_get_default_usage); subvol = argv[1]; fd = btrfs_open_dir(subvol, &dirstream, 1); if (fd < 0) return 1; ret = btrfs_list_get_default_subvolume(fd, &default_id); if (ret) { error("failed to look up default subvolume: %s", strerror(errno)); goto out; } ret = 1; if (default_id == 0) { error("'default' dir item not found"); goto out; } /* no need to resolve roots if FS_TREE is default */ if (default_id == BTRFS_FS_TREE_OBJECTID) { printf("ID 5 (FS_TREE)\n"); ret = 0; goto out; } filter_set = btrfs_list_alloc_filter_set(); btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID, default_id); /* 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); ret = btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL); if (filter_set) free(filter_set); out: close_file_or_dir(fd, dirstream); return !!ret; }
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; DIR *dirstream = NULL; if (check_argc_exact(argc, 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); 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"); goto out; } /* no need to resolve roots if FS_TREE is default */ if (default_id == BTRFS_FS_TREE_OBJECTID) { printf("ID 5 (FS_TREE)\n"); ret = 0; goto out; } filter_set = btrfs_list_alloc_filter_set(); btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID, default_id); /* 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); ret = btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL); if (filter_set) btrfs_list_free_filter_set(filter_set); out: close_file_or_dir(fd, dirstream); return !!ret; }
static int cmd_balance_resume(int argc, char **argv) { struct btrfs_ioctl_balance_args args; const char *path; DIR *dirstream = NULL; int fd; int ret; int e; if (check_argc_exact(argc, 2)) usage(cmd_balance_resume_usage); path = argv[1]; fd = open_file_or_dir(path, &dirstream); if (fd < 0) { fprintf(stderr, "ERROR: can't access '%s'\n", path); return 1; } memset(&args, 0, sizeof(args)); args.flags |= BTRFS_BALANCE_RESUME; ret = ioctl(fd, BTRFS_IOC_BALANCE_V2, &args); e = errno; close_file_or_dir(fd, dirstream); if (ret < 0) { if (e == ECANCELED) { if (args.state & BTRFS_BALANCE_STATE_PAUSE_REQ) fprintf(stderr, "balance paused by user\n"); if (args.state & BTRFS_BALANCE_STATE_CANCEL_REQ) fprintf(stderr, "balance canceled by user\n"); } else if (e == ENOTCONN || e == EINPROGRESS) { fprintf(stderr, "ERROR: balance resume on '%s' " "failed - %s\n", path, (e == ENOTCONN) ? "Not in progress" : "Already running"); if (e == ENOTCONN) return 2; else return 1; } else { fprintf(stderr, "ERROR: error during balancing '%s' - %s\n" "There may be more info in syslog - try dmesg | tail\n", path, strerror(e)); return 1; } } else { printf("Done, had to relocate %llu out of %llu chunks\n", (unsigned long long)args.stat.completed, (unsigned long long)args.stat.considered); } return 0; }
int main(int argc, char **argv) { char *path; int fd; int ret; u64 flags = 0; char *dir = "html"; DIR *dirstream = NULL; while (1) { int c = getopt(argc, argv, "cmso:h"); if (c < 0) break; switch (c) { case 'c': use_color = 1; break; case 'd': flags |= BTRFS_BLOCK_GROUP_DATA; break; case 'm': flags |= BTRFS_BLOCK_GROUP_METADATA; break; case 's': flags |= BTRFS_BLOCK_GROUP_SYSTEM; break; case 'o': dir = optarg; break; case 'h': default: fragments_usage(); } } set_argv0(argv); if (check_argc_min(argc - optind, 1)) fragments_usage(); path = argv[optind++]; fd = btrfs_open_dir(path, &dirstream, 1); if (fd < 0) exit(1); if (flags == 0) flags = BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA; ret = list_fragments(fd, flags, dir); close_file_or_dir(fd, dirstream); if (ret) exit(1); exit(0); }
static int do_balance(const char *path, struct btrfs_ioctl_balance_args *args, int nofilters) { int fd; int ret; int e; DIR *dirstream = NULL; fd = open_file_or_dir(path, &dirstream); if (fd < 0) { fprintf(stderr, "ERROR: can't access '%s'\n", path); return 1; } ret = ioctl(fd, BTRFS_IOC_BALANCE_V2, args); e = errno; if (ret < 0) { /* * older kernels don't have the new balance ioctl, try the * old one. But, the old one doesn't know any filters, so * don't fall back if they tried to use the fancy new things */ if (e == ENOTTY && nofilters) { ret = do_balance_v1(fd); if (ret == 0) goto out; e = errno; } if (e == ECANCELED) { if (args->state & BTRFS_BALANCE_STATE_PAUSE_REQ) fprintf(stderr, "balance paused by user\n"); if (args->state & BTRFS_BALANCE_STATE_CANCEL_REQ) fprintf(stderr, "balance canceled by user\n"); ret = 0; } else { fprintf(stderr, "ERROR: error during balancing '%s' " "- %s\n", path, strerror(e)); if (e != EINPROGRESS) fprintf(stderr, "There may be more info in " "syslog - try dmesg | tail\n"); ret = 1; } } else { printf("Done, had to relocate %llu out of %llu chunks\n", (unsigned long long)args->stat.completed, (unsigned long long)args->stat.considered); ret = 0; } out: close_file_or_dir(fd, dirstream); return ret; }
static int cmd_find_new(int argc, char **argv) { int fd; int ret; char *subvol; u64 last_gen; DIR *dirstream = NULL; if (check_argc_exact(argc, 3)) usage(cmd_find_new_usage); subvol = argv[1]; last_gen = arg_strtou64(argv[2]); 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); return 1; } fd = open_file_or_dir(subvol, &dirstream); if (fd < 0) { fprintf(stderr, "ERROR: can't access '%s'\n", subvol); return 1; } ret = ioctl(fd, BTRFS_IOC_SYNC); if (ret < 0) { fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n", subvol, strerror(errno)); close_file_or_dir(fd, dirstream); return 1; } ret = btrfs_list_find_updated_files(fd, 0, last_gen); close_file_or_dir(fd, dirstream); return !!ret; }
static int cmd_subvol_find_new(int argc, char **argv) { int fd; int ret; char *subvol; u64 last_gen; DIR *dirstream = NULL; 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[optind]; last_gen = arg_strtou64(argv[optind + 1]); ret = test_issubvolume(subvol); if (ret < 0) { error("cannot access subvolume %s: %s", subvol, strerror(-ret)); return 1; } if (!ret) { error("not a subvolume: %s", subvol); return 1; } fd = btrfs_open_dir(subvol, &dirstream, 1); if (fd < 0) return 1; ret = ioctl(fd, BTRFS_IOC_SYNC); if (ret < 0) { error("sync ioctl failed on '%s': %s", subvol, strerror(errno)); close_file_or_dir(fd, dirstream); return 1; } ret = btrfs_list_find_updated_files(fd, 0, last_gen); close_file_or_dir(fd, dirstream); return !!ret; }
static int cmd_balance_resume(int argc, char **argv) { struct btrfs_ioctl_balance_args args; const char *path; DIR *dirstream = NULL; int fd; int ret; clean_args_no_options(argc, argv, cmd_balance_resume_usage); if (check_argc_exact(argc - optind, 1)) return 1; path = argv[optind]; fd = btrfs_open_dir(path, &dirstream, 1); if (fd < 0) return 1; memset(&args, 0, sizeof(args)); args.flags |= BTRFS_BALANCE_RESUME; ret = ioctl(fd, BTRFS_IOC_BALANCE_V2, &args); if (ret < 0) { if (errno == ECANCELED) { if (args.state & BTRFS_BALANCE_STATE_PAUSE_REQ) fprintf(stderr, "balance paused by user\n"); if (args.state & BTRFS_BALANCE_STATE_CANCEL_REQ) fprintf(stderr, "balance canceled by user\n"); } else if (errno == ENOTCONN || errno == EINPROGRESS) { error("balance resume on '%s' failed: %s", path, (errno == ENOTCONN) ? "Not in progress" : "Already running"); if (errno == ENOTCONN) ret = 2; else ret = 1; } else { error("error during balancing '%s': %m\n" "There may be more info in syslog - try dmesg | tail", path); ret = 1; } } else { printf("Done, had to relocate %llu out of %llu chunks\n", (unsigned long long)args.stat.completed, (unsigned long long)args.stat.considered); } close_file_or_dir(fd, dirstream); return ret; }
static int cmd_replace_cancel(int argc, char **argv) { struct btrfs_ioctl_dev_replace_args args = {0}; int ret; int c; int fd; int e; char *path; DIR *dirstream = NULL; while ((c = getopt(argc, argv, "")) != -1) { switch (c) { case '?': default: usage(cmd_replace_cancel_usage); } } if (check_argc_exact(argc - optind, 1)) usage(cmd_replace_cancel_usage); path = argv[optind]; fd = open_file_or_dir(path, &dirstream); if (fd < 0) { fprintf(stderr, "ERROR: can't access \"%s\": %s\n", path, strerror(errno)); return 1; } args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL; args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT; ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args); e = errno; close_file_or_dir(fd, dirstream); if (ret) { fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s", path, strerror(e)); if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT) fprintf(stderr, ", %s\n", replace_dev_result2string(args.result)); else fprintf(stderr, "\n"); return 1; } if (args.result == BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED) { printf("INFO: ioctl(DEV_REPLACE_CANCEL)\"%s\": %s\n", path, replace_dev_result2string(args.result)); return 2; } return 0; }
static int cmd_rm_dev(int argc, char **argv) { char *mntpnt; int i, fdmnt, ret=0, e; DIR *dirstream = NULL; if (check_argc_min(argc, 3)) usage(cmd_rm_dev_usage); mntpnt = argv[argc - 1]; fdmnt = open_file_or_dir(mntpnt, &dirstream); if (fdmnt < 0) { fprintf(stderr, "ERROR: can't access '%s'\n", mntpnt); return 1; } for(i=1 ; i < argc - 1; i++ ){ struct btrfs_ioctl_vol_args arg; int res; if (!is_block_device(argv[i])) { fprintf(stderr, "ERROR: %s is not a block device\n", argv[i]); ret++; continue; } memset(&arg, 0, sizeof(arg)); strncpy_null(arg.name, argv[i]); res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg); e = errno; if (res) { const char *msg; if (res > 0) msg = btrfs_err_str(res); else msg = strerror(e); fprintf(stderr, "ERROR: error removing the device '%s' - %s\n", argv[i], msg); ret++; } } close_file_or_dir(fdmnt, dirstream); return !!ret; }
static int cmd_scrub_cancel(int argc, char **argv) { char *path; int ret; int fdmnt = -1; DIR *dirstream = NULL; if (check_argc_exact(argc, 2)) usage(cmd_scrub_cancel_usage); path = argv[1]; fdmnt = open_path_or_dev_mnt(path, &dirstream); if (fdmnt < 0) { if (errno == EINVAL) fprintf(stderr, "ERROR: '%s' is not a mounted btrfs device\n", path); else fprintf(stderr, "ERROR: can't access '%s': %s\n", path, strerror(errno)); ret = 1; goto out; } ret = ioctl(fdmnt, BTRFS_IOC_SCRUB_CANCEL, NULL); if (ret < 0) { fprintf(stderr, "ERROR: scrub cancel failed on %s: %s\n", path, errno == ENOTCONN ? "not running" : strerror(errno)); if (errno == ENOTCONN) ret = 2; else ret = 1; goto out; } ret = 0; printf("scrub cancelled\n"); out: close_file_or_dir(fdmnt, dirstream); return ret; }
static int qgroup_assign(int assign, int argc, char **argv) { int ret = 0; int fd; int e; char *path = argv[3]; struct btrfs_ioctl_qgroup_assign_args args; DIR *dirstream = NULL; if (check_argc_exact(argc, 4)) return -1; memset(&args, 0, sizeof(args)); args.assign = assign; args.src = parse_qgroupid(argv[1]); args.dst = parse_qgroupid(argv[2]); /* * FIXME src should accept subvol path */ if (btrfs_qgroup_level(args.src) >= btrfs_qgroup_level(args.dst)) { fprintf(stderr, "ERROR: bad relation requested '%s'\n", path); return 1; } fd = open_file_or_dir(path, &dirstream); if (fd < 0) { fprintf(stderr, "ERROR: can't access '%s'\n", path); return 1; } ret = ioctl(fd, BTRFS_IOC_QGROUP_ASSIGN, &args); e = errno; close_file_or_dir(fd, dirstream); if (ret < 0) { fprintf(stderr, "ERROR: unable to assign quota group: %s\n", strerror(e)); return 1; } return 0; }
static int cmd_scrub_cancel(int argc, char **argv) { char *path; int ret; int fdmnt = -1; DIR *dirstream = NULL; clean_args_no_options(argc, argv, cmd_scrub_cancel_usage); if (check_argc_exact(argc - optind, 1)) usage(cmd_scrub_cancel_usage); path = argv[optind]; fdmnt = open_path_or_dev_mnt(path, &dirstream, 1); if (fdmnt < 0) { ret = 1; goto out; } ret = ioctl(fdmnt, BTRFS_IOC_SCRUB_CANCEL, NULL); if (ret < 0) { error("scrub cancel failed on %s: %s", path, errno == ENOTCONN ? "not running" : strerror(errno)); if (errno == ENOTCONN) ret = 2; else ret = 1; goto out; } ret = 0; printf("scrub cancelled\n"); out: close_file_or_dir(fdmnt, dirstream); return ret; }
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 *fullpath = NULL, *svpath = NULL, *mnt = NULL; char raw_prefix[] = "\t\t\t\t"; u64 sv_id, mntid; int fd = -1, mntfd = -1; int ret = 1; DIR *dirstream1 = NULL, *dirstream2 = NULL; if (check_argc_exact(argc, 2)) usage(cmd_subvol_show_usage); fullpath = realpath(argv[1], NULL); if (!fullpath) { fprintf(stderr, "ERROR: finding real path for '%s', %s\n", argv[1], strerror(errno)); goto out; } 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; } 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 (ret > 0) { fprintf(stderr, "ERROR: %s doesn't belong to btrfs mount point\n", 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); 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; } mntfd = open_file_or_dir(mnt, &dirstream2); if (mntfd < 0) { fprintf(stderr, "ERROR: can't access '%s'\n", mnt); 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 (sv_id == BTRFS_FS_TREE_OBJECTID) { printf("%s is btrfs root\n", fullpath); goto out; } memset(&get_ri, 0, sizeof(get_ri)); get_ri.root_id = sv_id; ret = btrfs_get_subvol(mntfd, &get_ri); if (ret) { fprintf(stderr, "ERROR: can't find '%s'\n", svpath); goto out; } /* print the info */ printf("%s\n", fullpath); printf("\tName: \t\t\t%s\n", get_ri.name); if (uuid_is_null(get_ri.uuid)) strcpy(uuidparse, "-"); else uuid_unparse(get_ri.uuid, uuidparse); printf("\tUUID: \t\t\t%s\n", uuidparse); if (uuid_is_null(get_ri.puuid)) strcpy(uuidparse, "-"); else uuid_unparse(get_ri.puuid, uuidparse); printf("\tParent UUID: \t\t%s\n", uuidparse); if (uuid_is_null(get_ri.ruuid)) strcpy(uuidparse, "-"); else uuid_unparse(get_ri.ruuid, uuidparse); printf("\tReceived UUID: \t\t%s\n", uuidparse); if (get_ri.otime) { struct tm tm; localtime_r(&get_ri.otime, &tm); strftime(tstr, 256, "%Y-%m-%d %X %z", &tm); } else strcpy(tstr, "-"); printf("\tCreation time: \t\t%s\n", tstr); printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id); printf("\tGeneration: \t\t%llu\n", get_ri.gen); printf("\tGen at creation: \t%llu\n", get_ri.ogen); printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree); printf("\tTop level ID: \t\t%llu\n", get_ri.top_id); if (get_ri.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"); 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); out: close_file_or_dir(fd, dirstream1); close_file_or_dir(mntfd, dirstream2); free(mnt); free(fullpath); return !!ret; }
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_list(int argc, char **argv) { struct btrfs_list_filter_set *filter_set; struct btrfs_list_comparer_set *comparer_set; u64 flags = 0; int fd = -1; u64 top_id; int ret = -1, uerr = 0; char *subvol; int is_tab_result = 0; int is_list_all = 0; int is_only_in_path = 0; DIR *dirstream = NULL; filter_set = btrfs_list_alloc_filter_set(); comparer_set = btrfs_list_alloc_comparer_set(); optind = 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) break; switch(c) { case 'p': btrfs_list_setup_print_column(BTRFS_LIST_PARENT); break; case 'a': is_list_all = 1; break; case 'c': btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION); break; case 'd': btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_DELETED, 0); break; case 'g': btrfs_list_setup_print_column(BTRFS_LIST_GENERATION); break; case 'o': is_only_in_path = 1; break; case 't': is_tab_result = 1; break; case 's': btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_SNAPSHOT_ONLY, 0); btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION); btrfs_list_setup_print_column(BTRFS_LIST_OTIME); break; case 'u': btrfs_list_setup_print_column(BTRFS_LIST_UUID); break; case 'q': btrfs_list_setup_print_column(BTRFS_LIST_PUUID); break; case 'R': btrfs_list_setup_print_column(BTRFS_LIST_RUUID); break; case 'r': flags |= BTRFS_ROOT_SUBVOL_RDONLY; break; case 'G': btrfs_list_setup_print_column(BTRFS_LIST_GENERATION); ret = btrfs_list_parse_filter_string(optarg, &filter_set, BTRFS_LIST_FILTER_GEN); if (ret) { uerr = 1; goto out; } break; case 'C': btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION); ret = btrfs_list_parse_filter_string(optarg, &filter_set, BTRFS_LIST_FILTER_CGEN); if (ret) { uerr = 1; goto out; } break; case 'S': ret = btrfs_list_parse_sort_string(optarg, &comparer_set); if (ret) { uerr = 1; goto out; } break; default: uerr = 1; goto out; } } 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]; fd = open_file_or_dir(subvol, &dirstream); if (fd < 0) { ret = -1; fprintf(stderr, "ERROR: can't access '%s'\n", 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); goto out; } if (is_list_all) btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FULL_PATH, top_id); else if (is_only_in_path) btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_TOPID_EQUAL, top_id); /* 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); 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); out: close_file_or_dir(fd, dirstream); if (filter_set) btrfs_list_free_filter_set(filter_set); if (comparer_set) btrfs_list_free_comparer_set(comparer_set); if (uerr) usage(cmd_subvol_list_usage); return !!ret; }
static int cmd_subvol_delete(int argc, char **argv) { int res, 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 verbose = 0; int commit_mode = 0; optind = 1; while (1) { int c; static const struct option long_options[] = { {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */ {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */ {NULL, 0, NULL, 0} }; c = getopt_long(argc, argv, "cC", long_options, NULL); if (c < 0) break; switch(c) { case 'c': commit_mode = 1; break; case 'C': commit_mode = 2; break; case 'v': verbose++; break; default: usage(cmd_subvol_delete_usage); } } if (check_argc_min(argc - optind, 1)) usage(cmd_subvol_delete_usage); if (verbose > 0) { printf("Transaction commit: %s\n", !commit_mode ? "none (default)" : commit_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); 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/%s'\n", commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc) ? "commit" : "no-commit", dname, vname); 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)); 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", 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 (commit_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; }
static int cmd_subvol_sync(int argc, char **argv) { int fd = -1; int i; int ret = 1; DIR *dirstream = NULL; u64 *ids = NULL; int id_count; int sleep_interval = 1; optind = 1; while (1) { int c = getopt(argc, argv, "s:"); if (c < 0) break; switch (c) { case 's': sleep_interval = atoi(argv[optind]); if (sleep_interval < 1) { fprintf(stderr, "ERROR: invalid sleep interval %s\n", argv[optind]); ret = 1; goto out; } break; default: 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); if (fd < 0) { fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]); ret = 1; goto out; } optind++; 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); if (id_count < 0) { fprintf(stderr, "ERROR: can't enumerate dead subvolumes: %s\n", strerror(-id_count)); ret = 1; goto out; } if (id_count == 0) { ret = 0; goto out; } } else { ids = (u64*)malloc(id_count * sizeof(u64)); 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); 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); ret = 1; goto out; } ids[i] = id; } } ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval); out: free(ids); close_file_or_dir(fd, dirstream); return !!ret; }