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 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_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_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_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_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_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_balance_resume(int argc, char **argv) { struct btrfs_ioctl_balance_args args; const char *path; DIR *dirstream = NULL; int fd; int ret; int e; clean_args_no_options(argc, argv, cmd_balance_resume_usage); if (check_argc_exact(argc - optind, 1)) usage(cmd_balance_resume_usage); 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); 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) { error("balance resume on '%s' failed: %s", path, (e == ENOTCONN) ? "Not in progress" : "Already running"); if (e == ENOTCONN) return 2; else return 1; } else { error("error during balancing '%s': %s\n" "There may be more info in syslog - try dmesg | tail", 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; }
static int cmd_qgroup_destroy(int argc, char **argv) { int ret; clean_args_no_options(argc, argv, cmd_qgroup_destroy_usage); ret = _cmd_qgroup_create(0, argc, argv); if (ret < 0) usage(cmd_qgroup_destroy_usage); return ret; }
static int cmd_quota_disable(int argc, char **argv) { int ret; clean_args_no_options(argc, argv, cmd_quota_disable_usage); ret = quota_ctl(BTRFS_QUOTA_CTL_DISABLE, argc, argv); if (ret < 0) usage(cmd_quota_disable_usage); return ret; }
static int cmd_device_ready(int argc, char **argv) { struct btrfs_ioctl_vol_args args; int fd; int ret; char *path; clean_args_no_options(argc, argv, cmd_device_ready_usage); if (check_argc_exact(argc - optind, 1)) usage(cmd_device_ready_usage); fd = open("/dev/btrfs-control", O_RDWR); if (fd < 0) { perror("failed to open /dev/btrfs-control"); return 1; } path = canonicalize_path(argv[optind]); if (!path) { error("could not canonicalize pathname '%s': %m", argv[optind]); ret = 1; goto out; } if (is_block_device(path) != 1) { error("not a block device: %s", path); ret = 1; goto out; } memset(&args, 0, sizeof(args)); strncpy_null(args.name, path); ret = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args); if (ret < 0) { error("unable to determine if device '%s' is ready for mount: %m", path); ret = 1; } out: free(path); close(fd); return ret; }
static int cmd_rescue_zero_log(int argc, char **argv) { struct btrfs_root *root; struct btrfs_trans_handle *trans; struct btrfs_super_block *sb; char *devname; int ret; clean_args_no_options(argc, argv, cmd_rescue_zero_log_usage); if (check_argc_exact(argc, 2)) usage(cmd_rescue_zero_log_usage); devname = argv[optind]; ret = check_mounted(devname); if (ret < 0) { errno = -ret; error("could not check mount status: %m"); goto out; } else if (ret) { error("%s is currently mounted", devname); ret = -EBUSY; goto out; } root = open_ctree(devname, 0, OPEN_CTREE_WRITES | OPEN_CTREE_PARTIAL); if (!root) { error("could not open ctree"); return 1; } sb = root->fs_info->super_copy; printf("Clearing log on %s, previous log_root %llu, level %u\n", devname, (unsigned long long)btrfs_super_log_root(sb), (unsigned)btrfs_super_log_root_level(sb)); trans = btrfs_start_transaction(root, 1); BUG_ON(IS_ERR(trans)); btrfs_set_super_log_root(sb, 0); btrfs_set_super_log_root_level(sb, 0); btrfs_commit_transaction(trans, root); close_ctree(root); out: 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_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_rescue_fix_device_size(int argc, char **argv) { struct btrfs_fs_info *fs_info; char *devname; int ret; clean_args_no_options(argc, argv, cmd_rescue_fix_device_size_usage); if (check_argc_exact(argc, 2)) usage(cmd_rescue_fix_device_size_usage); devname = argv[optind]; ret = check_mounted(devname); if (ret < 0) { errno = -ret; error("could not check mount status: %m"); goto out; } else if (ret) { error("%s is currently mounted", devname); ret = -EBUSY; goto out; } fs_info = open_ctree_fs_info(devname, 0, 0, 0, OPEN_CTREE_WRITES | OPEN_CTREE_PARTIAL); if (!fs_info) { error("could not open btrfs"); ret = -EIO; goto out; } ret = btrfs_fix_device_and_super_size(fs_info); if (ret > 0) ret = 0; close_ctree(fs_info->tree_root); out: return !!ret; }
static int _cmd_qgroup_assign(int assign, int argc, char **argv, const char * const *usage_str) { int ret = 0; int fd; int rescan = 0; char *path; struct btrfs_ioctl_qgroup_assign_args args; DIR *dirstream = NULL; if (assign) { while (1) { enum { GETOPT_VAL_RESCAN = 256, GETOPT_VAL_NO_RESCAN }; static const struct option long_options[] = { { "rescan", no_argument, NULL, GETOPT_VAL_RESCAN }, { "no-rescan", no_argument, NULL, GETOPT_VAL_NO_RESCAN }, { NULL, 0, NULL, 0 } }; int c = getopt_long(argc, argv, "", long_options, NULL); if (c < 0) break; switch (c) { case GETOPT_VAL_RESCAN: rescan = 1; break; case GETOPT_VAL_NO_RESCAN: rescan = 0; break; default: /* Usage printed by the caller */ return -1; } } } else { clean_args_no_options(argc, argv, usage_str); } if (check_argc_exact(argc - optind, 3)) usage(usage_str); memset(&args, 0, sizeof(args)); args.assign = assign; args.src = parse_qgroupid(argv[optind]); args.dst = parse_qgroupid(argv[optind + 1]); path = argv[optind + 2]; /* * FIXME src should accept subvol path */ if (btrfs_qgroup_level(args.src) >= btrfs_qgroup_level(args.dst)) { error("bad relation requested: %s", path); return 1; } fd = btrfs_open_dir(path, &dirstream, 1); if (fd < 0) return 1; ret = ioctl(fd, BTRFS_IOC_QGROUP_ASSIGN, &args); if (ret < 0) { error("unable to assign quota group: %s", strerror(errno)); close_file_or_dir(fd, dirstream); return 1; } /* * If ret > 0, it means assign caused qgroup data inconsistent state. * Schedule a quota rescan if requested. * * The return value change only happens in newer kernel. But will not * cause problem since old kernel has a bug that will never clear * INCONSISTENT bit. */ if (ret > 0) { if (rescan) { struct btrfs_ioctl_quota_rescan_args qargs; printf("Quota data changed, rescan scheduled\n"); memset(&qargs, 0, sizeof(qargs)); ret = ioctl(fd, BTRFS_IOC_QUOTA_RESCAN, &qargs); if (ret < 0) error("quota rescan failed: %s", strerror(errno)); } else { warning("quotas may be inconsistent, rescan needed"); } } close_file_or_dir(fd, dirstream); return ret; }
static int _cmd_device_remove(int argc, char **argv, const char * const *usagestr) { char *mntpnt; int i, fdmnt, ret = 0; DIR *dirstream = NULL; clean_args_no_options(argc, argv, usagestr); if (check_argc_min(argc - optind, 2)) usage(usagestr); mntpnt = argv[argc - 1]; fdmnt = btrfs_open_dir(mntpnt, &dirstream, 1); if (fdmnt < 0) return 1; for(i = optind; i < argc - 1; i++) { struct btrfs_ioctl_vol_args arg; struct btrfs_ioctl_vol_args_v2 argv2 = {0}; int is_devid = 0; int res; if (string_is_numerical(argv[i])) { argv2.devid = arg_strtou64(argv[i]); argv2.flags = BTRFS_DEVICE_SPEC_BY_ID; is_devid = 1; } else if (is_block_device(argv[i]) == 1 || strcmp(argv[i], "missing") == 0) { strncpy_null(argv2.name, argv[i]); } else { error("not a block device: %s", argv[i]); ret++; continue; } /* * Positive values are from BTRFS_ERROR_DEV_*, * otherwise it's a generic error, one of errnos */ res = ioctl(fdmnt, BTRFS_IOC_RM_DEV_V2, &argv2); /* * If BTRFS_IOC_RM_DEV_V2 is not supported we get ENOTTY and if * argv2.flags includes a flag which kernel doesn't understand then * we shall get EOPNOTSUPP */ if (res < 0 && (errno == ENOTTY || errno == EOPNOTSUPP)) { if (is_devid) { error("device delete by id failed: %m"); ret++; continue; } memset(&arg, 0, sizeof(arg)); strncpy_null(arg.name, argv[i]); res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg); } if (res) { const char *msg; if (res > 0) msg = btrfs_err_str(res); else msg = strerror(errno); if (is_devid) { error("error removing devid %llu: %s", (unsigned long long)argv2.devid, msg); } else { error("error removing device '%s': %s", argv[i], msg); } ret++; } } 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 = NULL; char tstr[256]; char uuidparse[BTRFS_UUID_UNPARSED_SIZE]; char *fullpath = NULL; char raw_prefix[] = "\t\t\t\t"; int fd = -1; int ret = 1; DIR *dirstream1 = NULL; clean_args_no_options(argc, argv, cmd_subvol_show_usage); if (check_argc_exact(argc - optind, 1)) usage(cmd_subvol_show_usage); memset(&get_ri, 0, sizeof(get_ri)); fullpath = realpath(argv[optind], NULL); if (!fullpath) { error("cannot find real path for '%s': %s", argv[optind], strerror(errno)); goto out; } ret = get_subvol_info(fullpath, &get_ri); if (ret == 2) { /* * Since the top level btrfs was given don't * take that as error */ printf("%s is toplevel subvolume\n", fullpath); ret = 0; goto out; } if (ret) { if (ret < 0) { error("Failed to get subvol info %s: %s\n", fullpath, strerror(-ret)); } else { error("Failed to get subvol info %s: %d\n", fullpath, ret); } return ret; } /* 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); fd = open_file_or_dir(fullpath, &dirstream1); if (fd < 0) { fprintf(stderr, "ERROR: can't access '%s'\n", fullpath); goto out; } btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW, 1, raw_prefix); out: /* clean up */ free(get_ri.path); free(get_ri.name); free(get_ri.full_path); free(filter_set); close_file_or_dir(fd, dirstream1); free(fullpath); return !!ret; }