summaryrefslogtreecommitdiff
path: root/cmds-filesystem.c
diff options
context:
space:
mode:
Diffstat (limited to 'cmds-filesystem.c')
-rw-r--r--cmds-filesystem.c294
1 files changed, 268 insertions, 26 deletions
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index 74e3f904..f5439145 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -310,10 +310,71 @@ static int cmp_device_id(void *priv, struct list_head *a,
da->devid > db->devid ? 1 : 0;
}
+static void splice_device_list(struct list_head *seed_devices,
+ struct list_head *all_devices)
+{
+ struct btrfs_device *in_all, *next_all;
+ struct btrfs_device *in_seed, *next_seed;
+
+ list_for_each_entry_safe(in_all, next_all, all_devices, dev_list) {
+ list_for_each_entry_safe(in_seed, next_seed, seed_devices,
+ dev_list) {
+ if (in_all->devid == in_seed->devid) {
+ /*
+ * When do dev replace in a sprout fs
+ * to a dev in its seed fs, the replacing
+ * dev will reside in the sprout fs and
+ * the replaced dev will still exist
+ * in the seed fs.
+ * So pick the latest one when showing
+ * the sprout fs.
+ */
+ if (in_all->generation
+ < in_seed->generation) {
+ list_del(&in_all->dev_list);
+ free(in_all);
+ } else if (in_all->generation
+ > in_seed->generation) {
+ list_del(&in_seed->dev_list);
+ free(in_seed);
+ }
+ break;
+ }
+ }
+ }
+
+ list_splice(seed_devices, all_devices);
+}
+
+static void print_devices(struct btrfs_fs_devices *fs_devices,
+ u64 *devs_found)
+{
+ struct btrfs_device *device;
+ struct btrfs_fs_devices *cur_fs;
+ struct list_head *all_devices;
+
+ all_devices = &fs_devices->devices;
+ cur_fs = fs_devices->seed;
+ /* add all devices of seed fs to the fs to be printed */
+ while (cur_fs) {
+ splice_device_list(&cur_fs->devices, all_devices);
+ cur_fs = cur_fs->seed;
+ }
+
+ list_sort(NULL, all_devices, cmp_device_id);
+ list_for_each_entry(device, all_devices, dev_list) {
+ printf("\tdevid %4llu size %s used %s path %s\n",
+ (unsigned long long)device->devid,
+ pretty_size(device->total_bytes),
+ pretty_size(device->bytes_used), device->name);
+
+ (*devs_found)++;
+ }
+}
+
static void print_one_uuid(struct btrfs_fs_devices *fs_devices)
{
char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
- struct list_head *cur;
struct btrfs_device *device;
u64 devs_found = 0;
u64 total;
@@ -329,23 +390,13 @@ static void print_one_uuid(struct btrfs_fs_devices *fs_devices)
else
printf("Label: none ");
-
total = device->total_devs;
printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf,
(unsigned long long)total,
pretty_size(device->super_bytes_used));
- list_sort(NULL, &fs_devices->devices, cmp_device_id);
- list_for_each(cur, &fs_devices->devices) {
- device = list_entry(cur, struct btrfs_device, dev_list);
+ print_devices(fs_devices, &devs_found);
- printf("\tdevid %4llu size %s used %s path %s\n",
- (unsigned long long)device->devid,
- pretty_size(device->total_bytes),
- pretty_size(device->bytes_used), device->name);
-
- devs_found++;
- }
if (devs_found < total) {
printf("\t*** Some devices missing\n");
}
@@ -531,6 +582,194 @@ out:
return ret;
}
+static void free_fs_devices(struct btrfs_fs_devices *fs_devices)
+{
+ struct btrfs_fs_devices *cur_seed, *next_seed;
+ struct btrfs_device *device;
+
+ while (!list_empty(&fs_devices->devices)) {
+ device = list_entry(fs_devices->devices.next,
+ struct btrfs_device, dev_list);
+ list_del(&device->dev_list);
+
+ free(device->name);
+ free(device->label);
+ free(device);
+ }
+
+ /* free seed fs chain */
+ cur_seed = fs_devices->seed;
+ fs_devices->seed = NULL;
+ while (cur_seed) {
+ next_seed = cur_seed->seed;
+ free(cur_seed);
+
+ cur_seed = next_seed;
+ }
+
+ list_del(&fs_devices->list);
+ free(fs_devices);
+}
+
+static int copy_device(struct btrfs_device *dst,
+ struct btrfs_device *src)
+{
+ dst->devid = src->devid;
+ memcpy(dst->uuid, src->uuid, BTRFS_UUID_SIZE);
+ if (src->name == NULL)
+ dst->name = NULL;
+ else {
+ dst->name = strdup(src->name);
+ if (!dst->name)
+ return -ENOMEM;
+ }
+ if (src->label == NULL)
+ dst->label = NULL;
+ else {
+ dst->label = strdup(src->label);
+ if (!dst->label) {
+ free(dst->name);
+ return -ENOMEM;
+ }
+ }
+ dst->total_devs = src->total_devs;
+ dst->super_bytes_used = src->super_bytes_used;
+ dst->total_bytes = src->total_bytes;
+ dst->bytes_used = src->bytes_used;
+ dst->generation = src->generation;
+
+ return 0;
+}
+
+static int copy_fs_devices(struct btrfs_fs_devices *dst,
+ struct btrfs_fs_devices *src)
+{
+ struct btrfs_device *cur_dev, *dev_copy;
+ int ret = 0;
+
+ memcpy(dst->fsid, src->fsid, BTRFS_FSID_SIZE);
+ INIT_LIST_HEAD(&dst->devices);
+ dst->seed = NULL;
+
+ list_for_each_entry(cur_dev, &src->devices, dev_list) {
+ dev_copy = malloc(sizeof(*dev_copy));
+ if (!dev_copy) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ ret = copy_device(dev_copy, cur_dev);
+ if (ret) {
+ free(dev_copy);
+ break;
+ }
+
+ list_add(&dev_copy->dev_list, &dst->devices);
+ dev_copy->fs_devices = dst;
+ }
+
+ return ret;
+}
+
+static int find_and_copy_seed(struct btrfs_fs_devices *seed,
+ struct btrfs_fs_devices *copy,
+ struct list_head *fs_uuids) {
+ struct btrfs_fs_devices *cur_fs;
+
+ list_for_each_entry(cur_fs, fs_uuids, list)
+ if (!memcmp(seed->fsid, cur_fs->fsid, BTRFS_FSID_SIZE))
+ return copy_fs_devices(copy, cur_fs);
+
+ return 1;
+}
+
+static int map_seed_devices(struct list_head *all_uuids,
+ char *search, int *found)
+{
+ struct btrfs_fs_devices *cur_fs, *cur_seed;
+ struct btrfs_fs_devices *fs_copy, *seed_copy;
+ struct btrfs_fs_devices *opened_fs;
+ struct btrfs_device *device;
+ struct btrfs_fs_info *fs_info;
+ struct list_head *fs_uuids;
+ int ret = 0;
+
+ fs_uuids = btrfs_scanned_uuids();
+
+ /*
+ * The fs_uuids list is global, and open_ctree_* will
+ * modify it, make a private copy here
+ */
+ list_for_each_entry(cur_fs, fs_uuids, list) {
+ /* don't bother handle all fs, if search target specified */
+ if (search) {
+ if (uuid_search(cur_fs, search) == 0)
+ continue;
+ *found = 1;
+ }
+
+ fs_copy = malloc(sizeof(*fs_copy));
+ if (!fs_copy) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = copy_fs_devices(fs_copy, cur_fs);
+ if (ret) {
+ free(fs_copy);
+ goto out;
+ }
+
+ list_add(&fs_copy->list, all_uuids);
+ }
+
+ list_for_each_entry(cur_fs, all_uuids, list) {
+ device = list_first_entry(&cur_fs->devices,
+ struct btrfs_device, dev_list);
+ if (!device)
+ continue;
+ /*
+ * open_ctree_* detects seed/sprout mapping
+ */
+ fs_info = open_ctree_fs_info(device->name, 0, 0,
+ OPEN_CTREE_PARTIAL);
+ if (!fs_info)
+ continue;
+
+ /*
+ * copy the seed chain under the opened fs
+ */
+ opened_fs = fs_info->fs_devices;
+ cur_seed = cur_fs;
+ while (opened_fs->seed) {
+ seed_copy = malloc(sizeof(*seed_copy));
+ if (!seed_copy) {
+ ret = -ENOMEM;
+ goto fail_out;
+ }
+ ret = find_and_copy_seed(opened_fs->seed, seed_copy,
+ fs_uuids);
+ if (ret) {
+ free(seed_copy);
+ goto fail_out;
+ }
+
+ cur_seed->seed = seed_copy;
+
+ opened_fs = opened_fs->seed;
+ cur_seed = cur_seed->seed;
+ }
+
+ close_ctree(fs_info->chunk_root);
+ }
+
+out:
+ return ret;
+fail_out:
+ close_ctree(fs_info->chunk_root);
+ goto out;
+}
+
static const char * const cmd_show_usage[] = {
"btrfs filesystem show [options] [<path>|<uuid>|<device>|label]",
"Show the structure of a filesystem",
@@ -542,9 +781,8 @@ static const char * const cmd_show_usage[] = {
static int cmd_show(int argc, char **argv)
{
- struct list_head *all_uuids;
+ LIST_HEAD(all_uuids);
struct btrfs_fs_devices *fs_devices;
- struct list_head *cur_uuid;
char *search = NULL;
int ret;
/* default, search both kernel and udev */
@@ -645,24 +883,28 @@ devs_only:
fprintf(stderr, "ERROR: %d while scanning\n", ret);
return 1;
}
-
- all_uuids = btrfs_scanned_uuids();
- list_for_each(cur_uuid, all_uuids) {
- fs_devices = list_entry(cur_uuid, struct btrfs_fs_devices,
- list);
- if (search && uuid_search(fs_devices, search) == 0)
- continue;
- print_one_uuid(fs_devices);
- found = 1;
+ /*
+ * scan_for_btrfs() don't build seed/sprout mapping,
+ * do mapping build for each scanned fs here
+ */
+ ret = map_seed_devices(&all_uuids, search, &found);
+ if (ret) {
+ fprintf(stderr,
+ "ERROR: %d while mapping seed devices\n", ret);
+ return 1;
}
+
+ list_for_each_entry(fs_devices, &all_uuids, list)
+ print_one_uuid(fs_devices);
+
if (search && !found)
ret = 1;
- while (!list_empty(all_uuids)) {
- fs_devices = list_entry(all_uuids->next,
+ while (!list_empty(&all_uuids)) {
+ fs_devices = list_entry(all_uuids.next,
struct btrfs_fs_devices, list);
- btrfs_close_devices(fs_devices);
+ free_fs_devices(fs_devices);
}
out:
printf("%s\n", BTRFS_BUILD_VERSION);