It needs a lot more information about the snapshots if snapshot's life cycle has to be all auto managed by scripts _some day_. this patch is a step towards that. This patch provides the size which would be freed if the subvol/snapshot is deleted. preview: --------------------- btrfs su show /btrfs/sv1 :: Unshared space: 89.09MiB --------------------- v2: rename to 'unshared space' and edit commit text worked on review comments Signed-off-by: Anand Jain <anand.jain@xxxxxxxxxx> --- cmds-subvolume.c | 2 + utils.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ utils.h | 1 + 3 files changed, 157 insertions(+) diff --git a/cmds-subvolume.c b/cmds-subvolume.c index de246ab..d801e00 100644 --- a/cmds-subvolume.c +++ b/cmds-subvolume.c @@ -915,6 +915,8 @@ static int cmd_subvol_show(int argc, char **argv) else printf("\tFlags: \t\t\t-\n"); + printf("\tUnshared space: \t%s\n", + pretty_size(get_subvol_freeable_bytes(fd))); /* print the snapshots of the given subvol if any*/ printf("\tSnapshot(s):\n"); filter_set = btrfs_list_alloc_filter_set(); diff --git a/utils.c b/utils.c index ccb5199..ca30485 100644 --- a/utils.c +++ b/utils.c @@ -2062,3 +2062,157 @@ int lookup_ino_rootid(int fd, u64 *rootid) return 0; } + +/* gets the ref count for given extent + * 0 = didn't find the item + * n = number of references +*/ +u64 get_extent_refcnt(int fd, u64 disk_blk) +{ + int ret = 0, i, e; + struct btrfs_ioctl_search_args args; + struct btrfs_ioctl_search_key *sk = &args.key; + struct btrfs_ioctl_search_header sh; + unsigned long off = 0; + + memset(&args, 0, sizeof(args)); + + sk->tree_id = BTRFS_EXTENT_TREE_OBJECTID; + + sk->min_type = BTRFS_EXTENT_ITEM_KEY; + sk->max_type = BTRFS_EXTENT_ITEM_KEY; + + sk->min_objectid = disk_blk; + sk->max_objectid = disk_blk; + + sk->max_offset = (u64)-1; + sk->max_transid = (u64)-1; + + while (1) { + sk->nr_items = 4096; + + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); + e = errno; + if (ret < 0) { + fprintf(stderr, "ERROR: search failed - %s\n", + strerror(e)); + return 0; + } + if (sk->nr_items == 0) + break; + + off = 0; + for (i = 0; i < sk->nr_items; i++) { + struct btrfs_extent_item *ei; + u64 ref; + + memcpy(&sh, args.buf + off, sizeof(sh)); + off += sizeof(sh); + + if (sh.type != BTRFS_EXTENT_ITEM_KEY) { + off += sh.len; + continue; + } + + ei = (struct btrfs_extent_item *)(args.buf + off); + ref = btrfs_stack_extent_refs(ei); + return ref; + } + sk->min_objectid = sh.objectid; + sk->min_offset = sh.offset; + sk->min_type = sh.type; + if (sk->min_offset < (u64)-1) + sk->min_offset++; + else if (sk->min_objectid < (u64)-1) { + sk->min_objectid++; + sk->min_offset = 0; + sk->min_type = 0; + } else + break; + } + return 0; +} + +u64 get_subvol_freeable_bytes(int fd) +{ + int ret = 0, i, e; + struct btrfs_ioctl_search_args args; + struct btrfs_ioctl_search_key *sk = &args.key; + struct btrfs_ioctl_search_header sh; + unsigned long off = 0; + u64 size_bytes = 0; + + memset(&args, 0, sizeof(args)); + + sk->tree_id = 0; + + sk->min_type = BTRFS_EXTENT_DATA_KEY; + sk->max_type = BTRFS_EXTENT_DATA_KEY; + + sk->max_objectid = (u64) -1; + sk->max_offset = (u64)-1; + sk->max_transid = (u64)-1; + + while (1) { + sk->nr_items = 4096; + + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); + e = errno; + if (ret < 0) { + fprintf(stderr, "ERROR: search failed - %s\n", + strerror(e)); + return 0; + } + if (sk->nr_items == 0) + break; + + off = 0; + for (i = 0; i < sk->nr_items; i++) { + struct btrfs_file_extent_item *efi; + u64 disk_bytenr = 0; + u64 num_bytes = 0; + u64 refcnt; + u8 type; + + memcpy(&sh, args.buf + off, sizeof(sh)); + off += sizeof(sh); + + if (sh.type != BTRFS_EXTENT_DATA_KEY) { + off += sh.len; + continue; + } + + efi = (struct btrfs_file_extent_item *)(args.buf + off); + type = btrfs_stack_file_extent_type(efi); + + if (type == BTRFS_FILE_EXTENT_INLINE) { + size_bytes += + btrfs_stack_file_extent_ram_bytes(efi); + goto skip_extent_data; + } + disk_bytenr = btrfs_stack_file_extent_disk_bytenr(efi); + num_bytes = btrfs_stack_file_extent_num_bytes(efi); + + if (disk_bytenr) { + refcnt = get_extent_refcnt(fd, disk_bytenr); + if (refcnt == 1) + size_bytes += num_bytes; + } +skip_extent_data: + off += sh.len; + } + sk->min_objectid = sh.objectid; + sk->min_offset = sh.offset; + sk->min_type = sh.type; + + if (sk->min_offset < (u64)-1) + sk->min_offset++; + else if (sk->min_objectid < (u64)-1) { + sk->min_objectid++; + sk->min_offset = 0; + sk->min_type = 0; + } else + break; + } + return size_bytes; +} diff --git a/utils.h b/utils.h index 0f31db7..4ddcf09 100644 --- a/utils.h +++ b/utils.h @@ -91,5 +91,6 @@ int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf, int ask_user(char *question); int lookup_ino_rootid(int fd, u64 *rootid); int btrfs_scan_lblkid(int update_kernel); +u64 get_subvol_freeable_bytes(int fd); #endif -- 1.8.4.rc4.1.g0d8beaa -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html