int cmd_inspect_dump_tree(int argc, char **argv)
{
	struct btrfs_root *root;
	struct btrfs_fs_info *info;
	struct btrfs_path path;
	struct btrfs_key key;
	struct btrfs_root_item ri;
	struct extent_buffer *leaf;
	struct btrfs_disk_key disk_key;
	struct btrfs_key found_key;
	char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
	int ret;
	int slot;
	int extent_only = 0;
	int device_only = 0;
	int uuid_tree_only = 0;
	int roots_only = 0;
	int root_backups = 0;
	u64 block_only = 0;
	struct btrfs_root *tree_root_scan;
	u64 tree_id = 0;

	while (1) {
		int c;
		static const struct option long_options[] = {
			{ "extents", no_argument, NULL, 'e'},
			{ "device", no_argument, NULL, 'd'},
			{ "roots", no_argument, NULL, 'r'},
			{ "backups", no_argument, NULL, 'R'},
			{ "uuid", no_argument, NULL, 'u'},
			{ "block", required_argument, NULL, 'b'},
			{ "tree", required_argument, NULL, 't'},
			{ NULL, 0, NULL, 0 }
		};

		c = getopt_long(argc, argv, "deb:rRut:", long_options, NULL);
		if (c < 0)
			break;
		switch (c) {
		case 'e':
			extent_only = 1;
			break;
		case 'd':
			device_only = 1;
			break;
		case 'r':
			roots_only = 1;
			break;
		case 'u':
			uuid_tree_only = 1;
			break;
		case 'R':
			roots_only = 1;
			root_backups = 1;
			break;
		case 'b':
			block_only = arg_strtou64(optarg);
			break;
		case 't':
			if (string_is_numerical(optarg)) {
				tree_id = arg_strtou64(optarg);
			} else {
				const char *end = NULL;

				tree_id = treeid_from_string(optarg, &end);

				if (*end) {
					error("unexpected tree id suffix of '%s': %s\n",
							optarg, end);
					exit(1);
				}
			}
			if (!tree_id) {
				error("unrecognized tree id: %s\n",
						optarg);
				exit(1);
			}
			break;
		default:
			usage(cmd_inspect_dump_tree_usage);
		}
	}

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

	ret = check_arg_type(argv[optind]);
	if (ret != BTRFS_ARG_BLKDEV && ret != BTRFS_ARG_REG) {
		error("not a block device or regular file: %s", argv[optind]);
		goto out;
	}

	printf("%s\n", PACKAGE_STRING);

	info = open_ctree_fs_info(argv[optind], 0, 0, 0, OPEN_CTREE_PARTIAL);
	if (!info) {
		error("unable to open %s", argv[optind]);
		goto out;
	}

	root = info->fs_root;
	if (!root) {
		error("unable to open %s", argv[optind]);
		goto out;
	}

	if (block_only) {
		leaf = read_tree_block(root,
				      block_only,
				      root->nodesize, 0);

		if (extent_buffer_uptodate(leaf) &&
		    btrfs_header_level(leaf) != 0) {
			free_extent_buffer(leaf);
			leaf = NULL;
		}

		if (!leaf) {
			leaf = read_tree_block(root,
					      block_only,
					      root->nodesize, 0);
		}
		if (!extent_buffer_uptodate(leaf)) {
			error("failed to read %llu",
				(unsigned long long)block_only);
			goto close_root;
		}
		btrfs_print_tree(root, leaf, 0);
		free_extent_buffer(leaf);
		goto close_root;
	}

	if (!(extent_only || uuid_tree_only || tree_id)) {
		if (roots_only) {
			printf("root tree: %llu level %d\n",
			     (unsigned long long)info->tree_root->node->start,
			     btrfs_header_level(info->tree_root->node));
			printf("chunk tree: %llu level %d\n",
			     (unsigned long long)info->chunk_root->node->start,
			     btrfs_header_level(info->chunk_root->node));
		} else {
			if (info->tree_root->node) {
				printf("root tree\n");
				btrfs_print_tree(info->tree_root,
						 info->tree_root->node, 1);
			}

			if (info->chunk_root->node) {
				printf("chunk tree\n");
				btrfs_print_tree(info->chunk_root,
						 info->chunk_root->node, 1);
			}
		}
	}
	tree_root_scan = info->tree_root;

	btrfs_init_path(&path);
again:
	if (!extent_buffer_uptodate(tree_root_scan->node))
		goto no_node;

	/*
	 * Tree's that are not pointed by the tree of tree roots
	 */
	if (tree_id && tree_id == BTRFS_ROOT_TREE_OBJECTID) {
		if (!info->tree_root->node) {
			error("cannot print root tree, invalid pointer");
			goto no_node;
		}
		printf("root tree\n");
		btrfs_print_tree(info->tree_root, info->tree_root->node, 1);
		goto no_node;
	}

	if (tree_id && tree_id == BTRFS_CHUNK_TREE_OBJECTID) {
		if (!info->chunk_root->node) {
			error("cannot print chunk tree, invalid pointer");
			goto no_node;
		}
		printf("chunk tree\n");
		btrfs_print_tree(info->chunk_root, info->chunk_root->node, 1);
		goto no_node;
	}

	key.offset = 0;
	key.objectid = 0;
	btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
	ret = btrfs_search_slot(NULL, tree_root_scan, &key, &path, 0, 0);
	BUG_ON(ret < 0);
	while (1) {
		leaf = path.nodes[0];
		slot = path.slots[0];
		if (slot >= btrfs_header_nritems(leaf)) {
			ret = btrfs_next_leaf(tree_root_scan, &path);
			if (ret != 0)
				break;
			leaf = path.nodes[0];
			slot = path.slots[0];
		}
		btrfs_item_key(leaf, &disk_key, path.slots[0]);
		btrfs_disk_key_to_cpu(&found_key, &disk_key);
		if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) {
			unsigned long offset;
			struct extent_buffer *buf;
			int skip = extent_only | device_only | uuid_tree_only;

			offset = btrfs_item_ptr_offset(leaf, slot);
			read_extent_buffer(leaf, &ri, offset, sizeof(ri));
			buf = read_tree_block(tree_root_scan,
					      btrfs_root_bytenr(&ri),
					      tree_root_scan->nodesize,
					      0);
			if (!extent_buffer_uptodate(buf))
				goto next;
			if (tree_id && found_key.objectid != tree_id) {
				free_extent_buffer(buf);
				goto next;
			}

			switch (found_key.objectid) {
			case BTRFS_ROOT_TREE_OBJECTID:
				if (!skip)
					printf("root");
				break;
			case BTRFS_EXTENT_TREE_OBJECTID:
				if (!device_only && !uuid_tree_only)
					skip = 0;
				if (!skip)
					printf("extent");
				break;
			case BTRFS_CHUNK_TREE_OBJECTID:
				if (!skip) {
					printf("chunk");
				}
				break;
			case BTRFS_DEV_TREE_OBJECTID:
				if (!uuid_tree_only)
					skip = 0;
				if (!skip)
					printf("device");
				break;
			case BTRFS_FS_TREE_OBJECTID:
				if (!skip) {
					printf("fs");
				}
				break;
			case BTRFS_ROOT_TREE_DIR_OBJECTID:
				skip = 0;
				printf("directory");
				break;
			case BTRFS_CSUM_TREE_OBJECTID:
				if (!skip) {
					printf("checksum");
				}
				break;
			case BTRFS_ORPHAN_OBJECTID:
				if (!skip) {
					printf("orphan");
				}
				break;
			case BTRFS_TREE_LOG_OBJECTID:
				if (!skip) {
					printf("log");
				}
				break;
			case BTRFS_TREE_LOG_FIXUP_OBJECTID:
				if (!skip) {
					printf("log fixup");
				}
				break;
			case BTRFS_TREE_RELOC_OBJECTID:
				if (!skip) {
					printf("reloc");
				}
				break;
			case BTRFS_DATA_RELOC_TREE_OBJECTID:
				if (!skip) {
					printf("data reloc");
				}
				break;
			case BTRFS_EXTENT_CSUM_OBJECTID:
				if (!skip) {
					printf("extent checksum");
				}
				break;
			case BTRFS_QUOTA_TREE_OBJECTID:
				if (!skip) {
					printf("quota");
				}
				break;
			case BTRFS_UUID_TREE_OBJECTID:
				if (!extent_only && !device_only)
					skip = 0;
				if (!skip)
					printf("uuid");
				break;
			case BTRFS_FREE_SPACE_TREE_OBJECTID:
				if (!skip)
					printf("free space");
				break;
			case BTRFS_MULTIPLE_OBJECTIDS:
				if (!skip) {
					printf("multiple");
				}
				break;
			default:
				if (!skip) {
					printf("file");
				}
			}
			if (extent_only && !skip) {
				printf(" tree ");
				btrfs_print_key(&disk_key);
				printf("\n");
				print_extents(tree_root_scan, buf);
			} else if (!skip) {
				printf(" tree ");
				btrfs_print_key(&disk_key);
				if (roots_only) {
					printf(" %llu level %d\n",
					       (unsigned long long)buf->start,
					       btrfs_header_level(buf));
				} else {
					printf(" \n");
					btrfs_print_tree(tree_root_scan, buf, 1);
				}
			}
			free_extent_buffer(buf);
		}
next:
		path.slots[0]++;
	}
no_node:
	btrfs_release_path(&path);

	if (tree_root_scan == info->tree_root &&
	    info->log_root_tree) {
		tree_root_scan = info->log_root_tree;
		goto again;
	}

	if (extent_only || device_only || uuid_tree_only)
		goto close_root;

	if (root_backups)
		print_old_roots(info->super_copy);

	printf("total bytes %llu\n",
	       (unsigned long long)btrfs_super_total_bytes(info->super_copy));
	printf("bytes used %llu\n",
	       (unsigned long long)btrfs_super_bytes_used(info->super_copy));
	uuidbuf[BTRFS_UUID_UNPARSED_SIZE - 1] = '\0';
	uuid_unparse(info->super_copy->fsid, uuidbuf);
	printf("uuid %s\n", uuidbuf);
close_root:
	ret = close_ctree(root);
out:
	return !!ret;
}
예제 #2
0
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;
}
예제 #3
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;
}