示例#1
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;
}
示例#2
0
int main(int ac, char **av)
{
	struct stat st;
	char *device = NULL;
	char *mnt = NULL;
	int ret;
	int option_index = 0;
	int cmd = 0;
	int fd;
	int devfd = 0;
	DIR *dirstream;
	struct btrfs_ioctl_vol_args args;
	u64 dev_block_count = 0;

	printf( "**\n"
		"** WARNING: this program is considered deprecated\n"
		"** Please consider to switch to the btrfs utility\n"
		"**\n");

	while(1) {
		int c;
		c = getopt_long(ac, av, "a:br:", long_options,
				&option_index);
		if (c < 0)
			break;
		switch(c) {
			case 'a':
				device = strdup(optarg);
				cmd = BTRFS_IOC_ADD_DEV;
				break;
			case 'b':
				cmd = BTRFS_IOC_BALANCE;
				break;
			case 'r':
				device = strdup(optarg);
				cmd = BTRFS_IOC_RM_DEV;
				break;
			default:
				print_usage();
		}
	}
	ac = ac - optind;
	if (ac == 0)
		print_usage();
	mnt = av[optind];

	if (device && strcmp(device, "missing") == 0 &&
	    cmd == BTRFS_IOC_RM_DEV) {
		fprintf(stderr, "removing missing devices from %s\n", mnt);
	} else if (cmd != BTRFS_IOC_BALANCE) {
		if (cmd == BTRFS_IOC_ADD_DEV) {
			ret = check_mounted(device);
			if (ret < 0) {
				fprintf(stderr,
					"error checking %s mount status\n",
					device);
				exit(1);
			}
			if (ret == 1) {
				fprintf(stderr, "%s is mounted\n", device);
				exit(1);
			}
		}
		devfd = open(device, O_RDWR);
		if (devfd < 0) {
			fprintf(stderr, "Unable to open device %s\n", device);
			exit(1);
		}
		ret = fstat(devfd, &st);
		if (ret) {
			fprintf(stderr, "Unable to stat %s\n", device);
			exit(1);
		}
		if (!S_ISBLK(st.st_mode)) {
			fprintf(stderr, "%s is not a block device\n", device);
			exit(1);
		}
	}
	dirstream = opendir(mnt);
	if (!dirstream) {
		fprintf(stderr, "Unable to open directory %s\n", mnt);
		exit(1);
	}
	if (cmd == BTRFS_IOC_ADD_DEV) {
		int mixed = 0;

		ret = btrfs_prepare_device(devfd, device, 1, &dev_block_count, &mixed);
		if (ret) {
			fprintf(stderr, "Unable to init %s\n", device);
			exit(1);
		}
	}
	fd = dirfd(dirstream);
	if (device)
		strcpy(args.name, device);
	else
		args.name[0] = '\0';

	ret = ioctl(fd, cmd, &args);
	printf("ioctl returns %d\n", ret);
	return 0;
}
示例#3
0
static int cmd_device_add(int argc, char **argv)
{
	char	*mntpnt;
	int i, fdmnt, ret = 0;
	DIR	*dirstream = NULL;
	int discard = 1;
	int force = 0;
	int last_dev;

	optind = 0;
	while (1) {
		int c;
		static const struct option long_options[] = {
			{ "nodiscard", optional_argument, NULL, 'K'},
			{ "force", no_argument, NULL, 'f'},
			{ NULL, 0, NULL, 0}
		};

		c = getopt_long(argc, argv, "Kf", long_options, NULL);
		if (c < 0)
			break;
		switch (c) {
		case 'K':
			discard = 0;
			break;
		case 'f':
			force = 1;
			break;
		default:
			usage(cmd_device_add_usage);
		}
	}

	if (check_argc_min(argc - optind, 2))
		usage(cmd_device_add_usage);

	last_dev = argc - 1;
	mntpnt = argv[last_dev];

	fdmnt = btrfs_open_dir(mntpnt, &dirstream, 1);
	if (fdmnt < 0)
		return 1;

	for (i = optind; i < last_dev; i++){
		struct btrfs_ioctl_vol_args ioctl_args;
		int	devfd, res;
		u64 dev_block_count = 0;
		char *path;

		res = test_dev_for_mkfs(argv[i], force);
		if (res) {
			ret++;
			continue;
		}

		devfd = open(argv[i], O_RDWR);
		if (devfd < 0) {
			error("unable to open device '%s'", argv[i]);
			ret++;
			continue;
		}

		res = btrfs_prepare_device(devfd, argv[i], &dev_block_count, 0,
				PREP_DEVICE_ZERO_END | PREP_DEVICE_VERBOSE |
				(discard ? PREP_DEVICE_DISCARD : 0));
		close(devfd);
		if (res) {
			ret++;
			goto error_out;
		}

		path = canonicalize_path(argv[i]);
		if (!path) {
			error("could not canonicalize pathname '%s': %m",
				argv[i]);
			ret++;
			goto error_out;
		}

		memset(&ioctl_args, 0, sizeof(ioctl_args));
		strncpy_null(ioctl_args.name, path);
		res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args);
		if (res < 0) {
			error("error adding device '%s': %m", path);
			ret++;
		}
		free(path);
	}

error_out:
	close_file_or_dir(fdmnt, dirstream);
	return !!ret;
}
static int cmd_add_dev(int argc, char **argv)
{
	char	*mntpnt;
	int	i, fdmnt, ret=0, e;

	if (check_argc_min(argc, 3))
		usage(cmd_add_dev_usage);

	mntpnt = argv[argc - 1];

	fdmnt = open_file_or_dir(mntpnt);
	if (fdmnt < 0) {
		fprintf(stderr, "ERROR: can't access to '%s'\n", mntpnt);
		return 12;
	}

	for (i = 1; i < argc - 1; i++ ){
		struct btrfs_ioctl_vol_args ioctl_args;
		int	devfd, res;
		u64 dev_block_count = 0;
		struct stat st;
		int mixed = 0;

		res = check_mounted(argv[i]);
		if (res < 0) {
			fprintf(stderr, "error checking %s mount status\n",
				argv[i]);
			ret++;
			continue;
		}
		if (res == 1) {
			fprintf(stderr, "%s is mounted\n", argv[i]);
			ret++;
			continue;
		}

		devfd = open(argv[i], O_RDWR);
		if (!devfd) {
			fprintf(stderr, "ERROR: Unable to open device '%s'\n", argv[i]);
			close(devfd);
			ret++;
			continue;
		}
		res = fstat(devfd, &st);
		if (res) {
			fprintf(stderr, "ERROR: Unable to stat '%s'\n", argv[i]);
			close(devfd);
			ret++;
			continue;
		}
		if (!S_ISBLK(st.st_mode)) {
			fprintf(stderr, "ERROR: '%s' is not a block device\n", argv[i]);
			close(devfd);
			ret++;
			continue;
		}

		res = btrfs_prepare_device(devfd, argv[i], 1, &dev_block_count,
					   0, &mixed, 0);
		if (res) {
			fprintf(stderr, "ERROR: Unable to init '%s'\n", argv[i]);
			close(devfd);
			ret++;
			continue;
		}
		close(devfd);

		strncpy(ioctl_args.name, argv[i], BTRFS_PATH_NAME_MAX);
		ioctl_args.name[BTRFS_PATH_NAME_MAX-1] = 0;
		res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args);
		e = errno;
		if(res<0){
			fprintf(stderr, "ERROR: error adding the device '%s' - %s\n",
				argv[i], strerror(e));
			ret++;
		}

	}

	close(fdmnt);
	if (ret)
		return ret+20;
	else
		return 0;
}
示例#5
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_start_replace_usage[] = {
	"btrfs replace start srcdev|devid targetdev [-Bfr] 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_start_replace(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 fdsrcdev = -1;
	int fddstdev = -1;
	char *path;
	char *srcdev;
	char *dstdev;
	int avoid_reading_from_srcdev = 0;
	int force_using_targetdev = 0;
	u64 total_devs = 1;
	struct btrfs_fs_devices *fs_devices_mnt = NULL;
	struct stat st;
	u64 dstdev_block_count;
	int do_not_background = 0;
	int mixed = 0;

	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_start_replace_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_start_replace_usage);
	path = argv[optind + 2];

	fdmnt = open_path_or_dev_mnt(path);

	if (fdmnt < 0) {
		fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
			path, strerror(errno));
		goto leave_with_error;
	}

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

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

	if (status_args.status.replace_state ==
	    BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) {
		fprintf(stderr,
			"ERROR: btrfs replace on \"%s\" already started!\n",
			path);
		goto leave_with_error;
	}

	srcdev = argv[optind];
	dstdev = argv[optind + 1];

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

		if (atoi(srcdev) == 0) {
			fprintf(stderr, "Error: Failed to parse the numerical devid value '%s'\n",
				srcdev);
			goto leave_with_error;
		}
		start_args.start.srcdevid = (__u64)atoi(srcdev);

		ret = get_fs_info(path, &fi_args, &di_args);
		if (ret) {
			fprintf(stderr, "ERROR: getting dev info for devstats failed: "
					"%s\n", strerror(-ret));
			free(di_args);
			goto leave_with_error;
		}
		if (!fi_args.num_devices) {
			fprintf(stderr, "ERROR: no devices found\n");
			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;
		free(di_args);
		if (i == fi_args.num_devices) {
			fprintf(stderr, "Error: '%s' is not a valid devid for filesystem '%s'\n",
				srcdev, path);
			goto leave_with_error;
		}
	} else {
		fdsrcdev = open(srcdev, O_RDWR);
		if (fdsrcdev < 0) {
			fprintf(stderr, "Error: Unable to open device '%s'\n",
				srcdev);
			goto leave_with_error;
		}
		ret = fstat(fdsrcdev, &st);
		if (ret) {
			fprintf(stderr, "Error: Unable to stat '%s'\n", srcdev);
			goto leave_with_error;
		}
		if (!S_ISBLK(st.st_mode)) {
			fprintf(stderr, "Error: '%s' is not a block device\n",
				srcdev);
			goto leave_with_error;
		}
		strncpy((char *)start_args.start.srcdev_name, srcdev,
			BTRFS_DEVICE_PATH_NAME_MAX);
		close(fdsrcdev);
		fdsrcdev = -1;
		start_args.start.srcdevid = 0;
	}

	ret = check_mounted(dstdev);
	if (ret < 0) {
		fprintf(stderr, "Error checking %s mount status\n", dstdev);
		goto leave_with_error;
	}
	if (ret == 1) {
		fprintf(stderr,
			"Error, target device %s is in use and currently mounted!\n",
			dstdev);
		goto leave_with_error;
	}
	fddstdev = open(dstdev, O_RDWR);
	if (fddstdev < 0) {
		fprintf(stderr, "Unable to open %s\n", dstdev);
		goto leave_with_error;
	}
	ret = btrfs_scan_one_device(fddstdev, dstdev, &fs_devices_mnt,
				    &total_devs, BTRFS_SUPER_INFO_OFFSET);
	if (ret >= 0 && !force_using_targetdev) {
		fprintf(stderr,
			"Error, target device %s contains filesystem, use '-f' to force overwriting.\n",
			dstdev);
		goto leave_with_error;
	}
	ret = fstat(fddstdev, &st);
	if (ret) {
		fprintf(stderr, "Error: Unable to stat '%s'\n", dstdev);
		goto leave_with_error;
	}
	if (!S_ISBLK(st.st_mode)) {
		fprintf(stderr, "Error: '%s' is not a block device\n", dstdev);
		goto leave_with_error;
	}
	strncpy((char *)start_args.start.tgtdev_name, dstdev,
		BTRFS_DEVICE_PATH_NAME_MAX);
	if (btrfs_prepare_device(fddstdev, dstdev, 1, &dstdev_block_count, 0,
				 &mixed, 0)) {
		fprintf(stderr, "Error: Failed to prepare device '%s'\n",
			dstdev);
		goto leave_with_error;
	}
	close(fddstdev);
	fddstdev = -1;

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

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

		if (start_args.result !=
		    BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
			fprintf(stderr,
				"ERROR: ioctl(DEV_REPLACE_START) on \"%s\" returns error: %s\n",
				path,
				replace_dev_result2string(start_args.result));
			goto leave_with_error;
		}
	}
	close(fdmnt);
	return 0;

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

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

static int cmd_status_replace(int argc, char **argv)
{
	int fd;
	int e;
	int c;
	char *path;
	int once = 0;
	int ret;

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

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

	path = argv[optind];
	fd = open_file_or_dir(path);
	e = errno;
	if (fd < 0) {
		fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
			path, strerror(e));
		return -1;
	}

	ret = print_replace_status(fd, path, once);
	close(fd);
	return ret;
}
示例#6
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;
}
示例#7
0
static int cmd_add_dev(int argc, char **argv)
{
	char	*mntpnt;
	int	i, fdmnt, ret=0, e;
	DIR	*dirstream = NULL;
	int discard = 1;
	int force = 0;

	while (1) {
		int c;
		static const struct option long_options[] = {
			{ "nodiscard", optional_argument, NULL, 'K'},
			{ "force", no_argument, NULL, 'f'},
			{ NULL, 0, NULL, 0}
		};

		c = getopt_long(argc, argv, "Kf", long_options, NULL);
		if (c < 0)
			break;
		switch (c) {
		case 'K':
			discard = 0;
			break;
		case 'f':
			force = 1;
			break;
		default:
			usage(cmd_add_dev_usage);
		}
	}

	argc = argc - optind;

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

	mntpnt = argv[optind + 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 = optind; i < optind + argc - 1; i++){
		struct btrfs_ioctl_vol_args ioctl_args;
		int	devfd, res;
		u64 dev_block_count = 0;
		int mixed = 0;
		char *path;

		res = test_dev_for_mkfs(argv[i], force);
		if (res) {
			ret++;
			continue;
		}

		devfd = open(argv[i], O_RDWR);
		if (devfd < 0) {
			fprintf(stderr, "ERROR: Unable to open device '%s'\n", argv[i]);
			ret++;
			continue;
		}

		res = btrfs_prepare_device(devfd, argv[i], 1, &dev_block_count,
					   0, &mixed, discard);
		close(devfd);
		if (res) {
			ret++;
			goto error_out;
		}

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

		memset(&ioctl_args, 0, sizeof(ioctl_args));
		strncpy_null(ioctl_args.name, path);
		res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args);
		e = errno;
		if (res < 0) {
			fprintf(stderr, "ERROR: error adding the device '%s' - %s\n",
				path, strerror(e));
			ret++;
		}
		free(path);
	}

error_out:
	close_file_or_dir(fdmnt, dirstream);
	return !!ret;
}