Exemple #1
0
/*
 * Find the mount point for a mounted device.
 * On success, returns 0 with mountpoint in *mp.
 * On failure, returns -errno (not mounted yields -EINVAL)
 * Is noisy on failures, expects to be given a mounted device.
 */
int get_btrfs_mount(const char *dev, char *mp, size_t mp_size)
{
	int ret;
	int fd = -1;

	ret = is_block_device(dev);
	if (ret <= 0) {
		if (!ret) {
			fprintf(stderr, "%s is not a block device\n", dev);
			ret = -EINVAL;
		} else {
			fprintf(stderr, "Could not check %s: %s\n",
				dev, strerror(-ret));
		}
		goto out;
	}

	fd = open(dev, O_RDONLY);
	if (fd < 0) {
		ret = -errno;
		fprintf(stderr, "Could not open %s: %s\n", dev, strerror(errno));
		goto out;
	}

	ret = check_mounted_where(fd, dev, mp, mp_size, NULL);
	if (!ret) {
		ret = -EINVAL;
	} else { /* mounted, all good */
		ret = 0;
	}
out:
	if (fd != -1)
		close(fd);
	return ret;
}
Exemple #2
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;
}
Exemple #3
0
static int cmd_ready_dev(int argc, char **argv)
{
	struct	btrfs_ioctl_vol_args args;
	int	fd;
	int	ret;
	char	*path;

	if (check_argc_min(argc, 2))
		usage(cmd_ready_dev_usage);

	fd = open("/dev/btrfs-control", O_RDWR);
	if (fd < 0) {
		perror("failed to open /dev/btrfs-control");
		return 1;
	}

	path = canonicalize_path(argv[argc - 1]);
	if (!path) {
		fprintf(stderr,
			"ERROR: Could not canonicalize pathname '%s': %s\n",
			argv[argc - 1], strerror(errno));
		ret = 1;
		goto out;
	}

	if (!is_block_device(path)) {
		fprintf(stderr,
			"ERROR: %s is not a block device\n", 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) {
		fprintf(stderr, "ERROR: unable to determine if the device '%s'"
			" is ready for mounting - %s\n", path,
			strerror(errno));
		ret = 1;
	}

out:
	free(path);
	close(fd);
	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;
}
Exemple #5
0
/*
 * Given a pathname, return a filehandle to:
 * 	the original pathname or,
 * 	if the pathname is a mounted btrfs device, to its mountpoint.
 *
 * On error, return -1, errno should be set.
 */
int open_path_or_dev_mnt(const char *path, DIR **dirstream)
{
	char mp[BTRFS_PATH_NAME_MAX + 1];
	int fdmnt;

	if (is_block_device(path)) {
		int ret;

		ret = get_btrfs_mount(path, mp, sizeof(mp));
		if (ret < 0) {
			/* not a mounted btrfs dev */
			errno = EINVAL;
			return -1;
		}
		fdmnt = open_file_or_dir(mp, dirstream);
	} else {
		fdmnt = open_file_or_dir(path, dirstream);
	}

	return fdmnt;
}
Exemple #6
0
int main(int ac, char **av)
{
	char *file;
	struct btrfs_root *root;
	struct btrfs_trans_handle *trans;
	char *label = NULL;
	char *first_file;
	u64 block_count = 0;
	u64 dev_block_count = 0;
	u64 blocks[7];
	u64 alloc_start = 0;
	u64 metadata_profile = 0;
	u64 data_profile = 0;
	u32 leafsize = sysconf(_SC_PAGESIZE);
	u32 sectorsize = 4096;
	u32 nodesize = leafsize;
	u32 stripesize = 4096;
	int zero_end = 1;
	int option_index = 0;
	int fd;
	int ret;
	int i;
	int mixed = 0;
	int data_profile_opt = 0;
	int metadata_profile_opt = 0;
	int discard = 1;
	int ssd = 0;
	int force_overwrite = 0;

	char *source_dir = NULL;
	int source_dir_set = 0;
	u64 num_of_meta_chunks = 0;
	u64 size_of_data = 0;
	u64 source_dir_size = 0;
	int dev_cnt = 0;
	int saved_optind;
	char estr[100];
	u64 features = 0;

	while(1) {
		int c;
		c = getopt_long(ac, av, "A:b:fl:n:s:m:d:L:O:r:VMK",
				long_options, &option_index);
		if (c < 0)
			break;
		switch(c) {
			case 'A':
				alloc_start = parse_size(optarg);
				break;
			case 'f':
				force_overwrite = 1;
				break;
			case 'd':
				data_profile = parse_profile(optarg);
				data_profile_opt = 1;
				break;
			case 'l':
			case 'n':
				nodesize = parse_size(optarg);
				leafsize = parse_size(optarg);
				break;
			case 'L':
				label = parse_label(optarg);
				break;
			case 'm':
				metadata_profile = parse_profile(optarg);
				metadata_profile_opt = 1;
				break;
			case 'M':
				mixed = 1;
				break;
			case 'O': {
				char *orig = strdup(optarg);
				char *tmp = orig;

				tmp = parse_fs_features(tmp, &features);
				if (tmp) {
					fprintf(stderr,
						"Unrecognized filesystem feature '%s'\n",
							tmp);
					free(orig);
					exit(1);
				}
				free(orig);
				if (features & BTRFS_FEATURE_LIST_ALL) {
					list_all_fs_features();
					exit(0);
				}
				break;
				}
			case 's':
				sectorsize = parse_size(optarg);
				break;
			case 'b':
				block_count = parse_size(optarg);
				if (block_count <= 1024*1024*1024) {
					printf("SMALL VOLUME: forcing mixed "
					       "metadata/data groups\n");
					mixed = 1;
				}
				zero_end = 0;
				break;
			case 'V':
				print_version();
				break;
			case 'r':
				source_dir = optarg;
				source_dir_set = 1;
				break;
			case 'K':
				discard = 0;
				break;
			default:
				print_usage();
		}
	}
	sectorsize = max(sectorsize, (u32)sysconf(_SC_PAGESIZE));
	if (check_leaf_or_node_size(leafsize, sectorsize))
		exit(1);
	if (check_leaf_or_node_size(nodesize, sectorsize))
		exit(1);
	saved_optind = optind;
	dev_cnt = ac - optind;
	if (dev_cnt == 0)
		print_usage();

	if (source_dir_set && dev_cnt > 1) {
		fprintf(stderr,
			"The -r option is limited to a single device\n");
		exit(1);
	}
	while (dev_cnt-- > 0) {
		file = av[optind++];
		if (is_block_device(file))
			if (test_dev_for_mkfs(file, force_overwrite, estr)) {
				fprintf(stderr, "Error: %s", estr);
				exit(1);
			}
	}

	optind = saved_optind;
	dev_cnt = ac - optind;

	file = av[optind++];
	ssd = is_ssd(file);

	if (is_vol_small(file)) {
		printf("SMALL VOLUME: forcing mixed metadata/data groups\n");
		mixed = 1;
		if (metadata_profile != data_profile) {
			if (metadata_profile_opt || data_profile_opt) {
				fprintf(stderr,
	"With mixed block groups data and metadata profiles must be the same\n");
				exit(1);
			}
		}
	}
	/*
	* Set default profiles according to number of added devices.
	* For mixed groups defaults are single/single.
	*/
	if (!mixed) {
		if (!metadata_profile_opt) {
			if (dev_cnt == 1 && ssd)
				printf("Detected a SSD, turning off metadata "
				"duplication.  Mkfs with -m dup if you want to "
				"force metadata duplication.\n");

			metadata_profile = (dev_cnt > 1) ?
					BTRFS_BLOCK_GROUP_RAID1 : (ssd) ?
					0: BTRFS_BLOCK_GROUP_DUP;
		}
		if (!data_profile_opt) {
			data_profile = (dev_cnt > 1) ?
				BTRFS_BLOCK_GROUP_RAID0 : 0; /* raid0 or single */
		}
	} else {
		metadata_profile = 0;
		data_profile = 0;
	}

	ret = test_num_disk_vs_raid(metadata_profile, data_profile,
			dev_cnt, mixed, estr);
	if (ret) {
		fprintf(stderr, "Error: %s\n", estr);
		exit(1);
	}

	/* if we are here that means all devs are good to btrfsify */
	printf("\nWARNING! - %s IS EXPERIMENTAL\n", BTRFS_BUILD_VERSION);
	printf("WARNING! - see http://btrfs.wiki.kernel.org before using\n\n");

	dev_cnt--;

	if (!source_dir_set) {
		/*
		 * open without O_EXCL so that the problem should not
		 * occur by the following processing.
		 * (btrfs_register_one_device() fails if O_EXCL is on)
		 */
		fd = open(file, O_RDWR);
		if (fd < 0) {
			fprintf(stderr, "unable to open %s: %s\n", file,
				strerror(errno));
			exit(1);
		}
		first_file = file;
		ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count,
					   block_count, &mixed, discard);
		if (block_count && block_count > dev_block_count) {
			fprintf(stderr, "%s is smaller than requested size\n", file);
			exit(1);
		}
	} else {
		fd = open_target(file);
		if (fd < 0) {
			fprintf(stderr, "unable to open the %s\n", file);
			exit(1);
		}

		first_file = file;
		source_dir_size = size_sourcedir(source_dir, sectorsize,
					     &num_of_meta_chunks, &size_of_data);
		if(block_count < source_dir_size)
			block_count = source_dir_size;
		ret = zero_output_file(fd, block_count, sectorsize);
		if (ret) {
			fprintf(stderr, "unable to zero the output file\n");
			exit(1);
		}
		/* our "device" is the new image file */
		dev_block_count = block_count;
	}

	/* To create the first block group and chunk 0 in make_btrfs */
	if (dev_block_count < BTRFS_MKFS_SYSTEM_GROUP_SIZE) {
		fprintf(stderr, "device is too small to make filesystem\n");
		exit(1);
	}

	blocks[0] = BTRFS_SUPER_INFO_OFFSET;
	for (i = 1; i < 7; i++) {
		blocks[i] = BTRFS_SUPER_INFO_OFFSET + 1024 * 1024 +
			leafsize * i;
	}

	/*
	 * FS features that can be set by other means than -O
	 * just set the bit here
	 */
	if (mixed)
		features |= BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS;

	if ((data_profile | metadata_profile) &
	    (BTRFS_BLOCK_GROUP_RAID5 | BTRFS_BLOCK_GROUP_RAID6)) {
		features |= BTRFS_FEATURE_INCOMPAT_RAID56;
	}

	process_fs_features(features);

	ret = make_btrfs(fd, file, label, blocks, dev_block_count,
			 nodesize, leafsize,
			 sectorsize, stripesize, features);
	if (ret) {
		fprintf(stderr, "error during mkfs: %s\n", strerror(-ret));
		exit(1);
	}

	root = open_ctree(file, 0, OPEN_CTREE_WRITES);
	if (!root) {
		fprintf(stderr, "Open ctree failed\n");
		close(fd);
		exit(1);
	}
	root->fs_info->alloc_start = alloc_start;

	ret = make_root_dir(root, mixed);
	if (ret) {
		fprintf(stderr, "failed to setup the root directory\n");
		exit(1);
	}

	trans = btrfs_start_transaction(root, 1);

	if (dev_cnt == 0)
		goto raid_groups;

	btrfs_register_one_device(file);

	zero_end = 1;
	while (dev_cnt-- > 0) {
		int old_mixed = mixed;

		file = av[optind++];

		/*
		 * open without O_EXCL so that the problem should not
		 * occur by the following processing.
		 * (btrfs_register_one_device() fails if O_EXCL is on)
		 */
		fd = open(file, O_RDWR);
		if (fd < 0) {
			fprintf(stderr, "unable to open %s: %s\n", file,
				strerror(errno));
			exit(1);
		}
		ret = btrfs_device_already_in_root(root, fd,
						   BTRFS_SUPER_INFO_OFFSET);
		if (ret) {
			fprintf(stderr, "skipping duplicate device %s in FS\n",
				file);
			close(fd);
			continue;
		}
		ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count,
					   block_count, &mixed, discard);
		mixed = old_mixed;
		BUG_ON(ret);

		ret = btrfs_add_to_fsid(trans, root, fd, file, dev_block_count,
					sectorsize, sectorsize, sectorsize);
		BUG_ON(ret);
		btrfs_register_one_device(file);
	}

raid_groups:
	if (!source_dir_set) {
		ret = create_raid_groups(trans, root, data_profile,
				 data_profile_opt, metadata_profile,
				 metadata_profile_opt, mixed, ssd);
		BUG_ON(ret);
	}

	ret = create_data_reloc_tree(trans, root);
	BUG_ON(ret);

	printf("fs created label %s on %s\n\tnodesize %u leafsize %u "
	    "sectorsize %u size %s\n",
	    label, first_file, nodesize, leafsize, sectorsize,
	    pretty_size(btrfs_super_total_bytes(root->fs_info->super_copy)));

	printf("%s\n", BTRFS_BUILD_VERSION);
	btrfs_commit_transaction(trans, root);

	if (source_dir_set) {
		trans = btrfs_start_transaction(root, 1);
		ret = create_chunks(trans, root,
				    num_of_meta_chunks, size_of_data);
		BUG_ON(ret);
		btrfs_commit_transaction(trans, root);

		ret = make_image(source_dir, root, fd);
		BUG_ON(ret);
	}

	ret = close_ctree(root);
	BUG_ON(ret);
	free(label);
	return 0;
}
static int cmd_device_scan(int argc, char **argv)
{
	int i;
	int devstart;
	int all = 0;
	int ret = 0;

	optind = 0;
	while (1) {
		int c;
		static const struct option long_options[] = {
			{ "all-devices", no_argument, NULL, 'd'},
			{ NULL, 0, NULL, 0}
		};

		c = getopt_long(argc, argv, "d", long_options, NULL);
		if (c < 0)
			break;
		switch (c) {
		case 'd':
			all = 1;
			break;
		default:
			usage(cmd_device_scan_usage);
		}
	}
	devstart = optind;

	if (all && check_argc_max(argc - optind, 1))
		usage(cmd_device_scan_usage);

	if (all || argc - optind == 0) {
		printf("Scanning for Btrfs filesystems\n");
		ret = btrfs_scan_devices();
		error_on(ret, "error %d while scanning", ret);
		ret = btrfs_register_all_devices();
		error_on(ret, "there are %d errors while registering devices", ret);
		goto out;
	}

	for( i = devstart ; i < argc ; i++ ){
		char *path;

		if (is_block_device(argv[i]) != 1) {
			error("not a block device: %s", argv[i]);
			ret = 1;
			goto out;
		}
		path = canonicalize_path(argv[i]);
		if (!path) {
			error("could not canonicalize path '%s': %m", argv[i]);
			ret = 1;
			goto out;
		}
		printf("Scanning for Btrfs filesystems in '%s'\n", path);
		if (btrfs_register_one_device(path) != 0) {
			ret = 1;
			free(path);
			goto out;
		}
		free(path);
	}

out:
	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;
}
Exemple #9
0
static int dev_replace_handle_sigint(int fd)
{
	struct sigaction sa = {
		.sa_handler = fd == -1 ? SIG_DFL : dev_replace_sigint_handler
	};

	dev_replace_cancel_fd = fd;
	return sigaction(SIGINT, &sa, NULL);
}

static const char *const cmd_replace_start_usage[] = {
	"btrfs replace start [-Bfr] <srcdev>|<devid> <targetdev> <mount_point>",
	"Replace device of a btrfs filesystem.",
	"On a live filesystem, duplicate the data to the target device which",
	"is currently stored on the source device. If the source device is not",
	"available anymore, or if the -r option is set, the data is built",
	"only using the RAID redundancy mechanisms. After completion of the",
	"operation, the source device is removed from the filesystem.",
	"If the <srcdev> is a numerical value, it is assumed to be the device id",
	"of the filesystem which is mounted at <mount_point>, otherwise it is",
	"the path to the source device. If the source device is disconnected,",
	"from the system, you have to use the <devid> parameter format.",
	"The <targetdev> needs to be same size or larger than the <srcdev>.",
	"",
	"-r     only read from <srcdev> if no other zero-defect mirror exists",
	"       (enable this if your drive has lots of read errors, the access",
	"       would be very slow)",
	"-f     force using and overwriting <targetdev> even if it looks like",
	"       containing a valid btrfs filesystem. A valid filesystem is",
	"       assumed if a btrfs superblock is found which contains a",
	"       correct checksum. Devices which are currently mounted are",
	"       never allowed to be used as the <targetdev>",
	"-B     do not background",
	NULL
};

static int cmd_replace_start(int argc, char **argv)
{
	struct btrfs_ioctl_dev_replace_args start_args = {0};
	struct btrfs_ioctl_dev_replace_args status_args = {0};
	int ret;
	int i;
	int c;
	int fdmnt = -1;
	int fddstdev = -1;
	char *path;
	char *srcdev;
	char *dstdev = NULL;
	int avoid_reading_from_srcdev = 0;
	int force_using_targetdev = 0;
	u64 dstdev_block_count;
	int do_not_background = 0;
	DIR *dirstream = NULL;
	u64 srcdev_size;
	u64 dstdev_size;

	while ((c = getopt(argc, argv, "Brf")) != -1) {
		switch (c) {
		case 'B':
			do_not_background = 1;
			break;
		case 'r':
			avoid_reading_from_srcdev = 1;
			break;
		case 'f':
			force_using_targetdev = 1;
			break;
		case '?':
		default:
			usage(cmd_replace_start_usage);
		}
	}

	start_args.start.cont_reading_from_srcdev_mode =
		avoid_reading_from_srcdev ?
		 BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID :
		 BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS;
	if (check_argc_exact(argc - optind, 3))
		usage(cmd_replace_start_usage);
	path = argv[optind + 2];

	fdmnt = open_path_or_dev_mnt(path, &dirstream, 1);
	if (fdmnt < 0)
		goto leave_with_error;

	/* check for possible errors before backgrounding */
	status_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
	status_args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
	ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &status_args);
	if (ret < 0) {
		fprintf(stderr,
			"ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s",
			path, strerror(errno));
		if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
			fprintf(stderr, ", %s\n",
				replace_dev_result2string(status_args.result));
		else
			fprintf(stderr, "\n");
		goto leave_with_error;
	}

	if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
		error("ioctl(DEV_REPLACE_STATUS) on '%s' returns error: %s",
			path, replace_dev_result2string(status_args.result));
		goto leave_with_error;
	}

	if (status_args.status.replace_state ==
	    BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) {
		error("device replace on '%s' already started", path);
		goto leave_with_error;
	}

	srcdev = argv[optind];
	dstdev = canonicalize_path(argv[optind + 1]);
	if (!dstdev) {
		error("cannot canonicalize path '%s': %s",
			argv[optind + 1], strerror(errno));
		goto leave_with_error;
	}

	if (string_is_numerical(srcdev)) {
		struct btrfs_ioctl_fs_info_args fi_args;
		struct btrfs_ioctl_dev_info_args *di_args = NULL;

		start_args.start.srcdevid = arg_strtou64(srcdev);

		ret = get_fs_info(path, &fi_args, &di_args);
		if (ret) {
			error("failed to get device info: %s", strerror(-ret));
			free(di_args);
			goto leave_with_error;
		}
		if (!fi_args.num_devices) {
			error("no devices found");
			free(di_args);
			goto leave_with_error;
		}

		for (i = 0; i < fi_args.num_devices; i++)
			if (start_args.start.srcdevid == di_args[i].devid)
				break;
		srcdev_size = di_args[i].total_bytes;
		free(di_args);
		if (i == fi_args.num_devices) {
			error("'%s' is not a valid devid for filesystem '%s'",
				srcdev, path);
			goto leave_with_error;
		}
	} else if (is_block_device(srcdev) > 0) {
		strncpy((char *)start_args.start.srcdev_name, srcdev,
			BTRFS_DEVICE_PATH_NAME_MAX);
		start_args.start.srcdevid = 0;
		srcdev_size = get_partition_size(srcdev);
	} else {
		error("source device must be a block device or a devid");
		goto leave_with_error;
	}

	ret = test_dev_for_mkfs(dstdev, force_using_targetdev);
	if (ret)
		goto leave_with_error;

	dstdev_size = get_partition_size(dstdev);
	if (srcdev_size > dstdev_size) {
		error("target device smaller than source device (required %llu bytes)",
			srcdev_size);
		goto leave_with_error;
	}

	fddstdev = open(dstdev, O_RDWR);
	if (fddstdev < 0) {
		error("unable to open %s: %s", dstdev, strerror(errno));
		goto leave_with_error;
	}
	strncpy((char *)start_args.start.tgtdev_name, dstdev,
		BTRFS_DEVICE_PATH_NAME_MAX);
	ret = btrfs_prepare_device(fddstdev, dstdev, &dstdev_block_count, 0,
			PREP_DEVICE_ZERO_END | PREP_DEVICE_VERBOSE);
	if (ret)
		goto leave_with_error;

	close(fddstdev);
	fddstdev = -1;
	free(dstdev);
	dstdev = NULL;

	dev_replace_handle_sigint(fdmnt);
	if (!do_not_background) {
		if (daemon(0, 0) < 0) {
			error("backgrounding failed: %s", strerror(errno));
			goto leave_with_error;
		}
	}

	start_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_START;
	start_args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
	ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &start_args);
	if (do_not_background) {
		if (ret < 0) {
			fprintf(stderr,
				"ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %s",
				path, strerror(errno));
			if (start_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
				fprintf(stderr, ", %s\n",
					replace_dev_result2string(start_args.result));
			else
				fprintf(stderr, "\n");

			if (errno == EOPNOTSUPP)
				warning("device replace of RAID5/6 not supported with this kernel");

			goto leave_with_error;
		}

		if (start_args.result !=
		    BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
			error("ioctl(DEV_REPLACE_START) on '%s' returns error: %s",
				path,
				replace_dev_result2string(start_args.result));
			goto leave_with_error;
		}
	}
	close_file_or_dir(fdmnt, dirstream);
	return 0;

leave_with_error:
	if (dstdev)
		free(dstdev);
	if (fdmnt != -1)
		close(fdmnt);
	if (fddstdev != -1)
		close(fddstdev);
	return 1;
}

static const char *const cmd_replace_status_usage[] = {
	"btrfs replace status [-1] <mount_point>",
	"Print status and progress information of a running device replace",
	"operation",
	"",
	"-1     print once instead of print continuously until the replace",
	"       operation finishes (or is canceled)",
	NULL
};

static int cmd_replace_status(int argc, char **argv)
{
	int fd;
	int c;
	char *path;
	int once = 0;
	int ret;
	DIR *dirstream = NULL;

	while ((c = getopt(argc, argv, "1")) != -1) {
		switch (c) {
		case '1':
			once = 1;
			break;
		case '?':
		default:
			usage(cmd_replace_status_usage);
		}
	}

	if (check_argc_exact(argc - optind, 1))
		usage(cmd_replace_status_usage);

	path = argv[optind];
	fd = btrfs_open_dir(path, &dirstream, 1);
	if (fd < 0)
		return 1;

	ret = print_replace_status(fd, path, once);
	close_file_or_dir(fd, dirstream);
	return !!ret;
}
Exemple #10
0
static int cmd_scan_dev(int argc, char **argv)
{
	int i;
	int devstart = 1;
	int all = 0;
	int ret = 0;

	optind = 1;
	while (1) {
		int c;
		static const struct option long_options[] = {
			{ "all-devices", no_argument, NULL, 'd'},
			{ NULL, 0, NULL, 0}
		};

		c = getopt_long(argc, argv, "d", long_options, NULL);
		if (c < 0)
			break;
		switch (c) {
		case 'd':
			all = 1;
			break;
		default:
			usage(cmd_scan_dev_usage);
		}
	}

	if (all && check_argc_max(argc, 2))
		usage(cmd_scan_dev_usage);

	if (all || argc == 1) {
		printf("Scanning for Btrfs filesystems\n");
		ret = btrfs_scan_lblkid();
		if (ret)
			fprintf(stderr, "ERROR: error %d while scanning\n", ret);
		ret = btrfs_register_all_devices();
		if (ret)
			fprintf(stderr, "ERROR: error %d while registering\n", ret);
		goto out;
	}

	for( i = devstart ; i < argc ; i++ ){
		char *path;

		if (!is_block_device(argv[i])) {
			fprintf(stderr,
				"ERROR: %s is not a block device\n", argv[i]);
			ret = 1;
			goto out;
		}
		path = canonicalize_path(argv[i]);
		if (!path) {
			fprintf(stderr,
				"ERROR: Could not canonicalize path '%s': %s\n",
				argv[i], strerror(errno));
			ret = 1;
			goto out;
		}
		printf("Scanning for Btrfs filesystems in '%s'\n", path);
		if (btrfs_register_one_device(path) != 0) {
			ret = 1;
			free(path);
			goto out;
		}
		free(path);
	}

out:
	return !!ret;
}