From cec572daccafa1e912cbed363df6f84687778c6f Mon Sep 17 00:00:00 2001 From: Nicholas D Steeves Date: Sat, 23 Apr 2016 00:41:30 +0100 Subject: btrfs-progs (4.4.1-1.1) unstable; urgency=medium * Non-maintainer upload. * New upstream release. * Rename package to btrfs-progs (Closes: #780081) * Update standards version to 3.9.7 (no changes needed). * debian/control: Add "Breaks" per Gianfranco Costamagna's suggestion * Change lintian override to reflect package rename * Switch from using postinst and postrm to using triggers per Christian Seiler's recommendation. # imported from the archive --- btrfs-fragments.c | 452 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 452 insertions(+) create mode 100644 btrfs-fragments.c (limited to 'btrfs-fragments.c') diff --git a/btrfs-fragments.c b/btrfs-fragments.c new file mode 100644 index 00000000..17768c3f --- /dev/null +++ b/btrfs-fragments.c @@ -0,0 +1,452 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#undef ULONG_MAX + +#include "kerncompat.h" +#include "ctree.h" +#include "ioctl.h" +#include "utils.h" + +static int use_color; +static void +push_im(gdImagePtr im, char *name, char *dir) +{ + char fullname[2000]; + FILE *pngout; + + if (!im) + return; + + snprintf(fullname, sizeof(fullname), "%s/%s", dir, name); + pngout = fopen(fullname, "w"); + if (!pngout) { + printf("unable to create file %s\n", fullname); + exit(1); + } + + gdImagePng(im, pngout); + + fclose(pngout); + gdImageDestroy(im); +} + +static char * +chunk_type(u64 flags) +{ + switch (flags & (BTRFS_BLOCK_GROUP_SYSTEM | BTRFS_BLOCK_GROUP_DATA | + BTRFS_BLOCK_GROUP_METADATA)) { + case BTRFS_BLOCK_GROUP_SYSTEM: + return "system"; + case BTRFS_BLOCK_GROUP_DATA: + return "data"; + case BTRFS_BLOCK_GROUP_METADATA: + return "metadata"; + case BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA: + return "mixed"; + default: + return "invalid"; + } +} + +static void +print_bg(FILE *html, char *name, u64 start, u64 len, u64 used, u64 flags, + u64 areas) +{ + double frag = (double)areas / (len / 4096) * 2; + + fprintf(html, "

%s chunk starts at %lld, size is %s, %.2f%% used, " + "%.2f%% fragmented

\n", chunk_type(flags), start, + pretty_size(len), 100.0 * used / len, 100.0 * frag); + fprintf(html, "\n", name); +} + +enum tree_colors { + COLOR_ROOT = 0, + COLOR_EXTENT, + COLOR_CHUNK, + COLOR_DEV, + COLOR_FS, + COLOR_CSUM, + COLOR_RELOC, + COLOR_DATA, + COLOR_UNKNOWN, + COLOR_MAX +}; + +static int +get_color(struct btrfs_extent_item *item, int len) +{ + u64 refs; + u64 flags; + u8 type; + u64 offset; + struct btrfs_extent_inline_ref *ref; + + refs = btrfs_stack_extent_refs(item); + flags = btrfs_stack_extent_flags(item); + + if (flags & BTRFS_EXTENT_FLAG_DATA) + return COLOR_DATA; + if (refs > 1) { + /* this must be an fs tree */ + return COLOR_FS; + } + + ref = (void *)item + sizeof(struct btrfs_extent_item) + + sizeof(struct btrfs_tree_block_info); + type = btrfs_stack_extent_inline_ref_type(ref); + offset = btrfs_stack_extent_inline_ref_offset(ref); + + switch (type) { + case BTRFS_EXTENT_DATA_REF_KEY: + return COLOR_DATA; + case BTRFS_SHARED_BLOCK_REF_KEY: + case BTRFS_SHARED_DATA_REF_KEY: + return COLOR_FS; + case BTRFS_TREE_BLOCK_REF_KEY: + break; + default: + return COLOR_UNKNOWN; + } + + switch (offset) { + case BTRFS_ROOT_TREE_OBJECTID: + return COLOR_ROOT; + case BTRFS_EXTENT_TREE_OBJECTID: + return COLOR_EXTENT; + case BTRFS_CHUNK_TREE_OBJECTID: + return COLOR_CHUNK; + case BTRFS_DEV_TREE_OBJECTID: + return COLOR_DEV; + case BTRFS_FS_TREE_OBJECTID: + return COLOR_FS; + case BTRFS_CSUM_TREE_OBJECTID: + return COLOR_CSUM; + case BTRFS_DATA_RELOC_TREE_OBJECTID: + return COLOR_RELOC; + } + + return COLOR_UNKNOWN; +} + +static void +init_colors(gdImagePtr im, int *colors) +{ + colors[COLOR_ROOT] = gdImageColorAllocate(im, 255, 0, 0); + colors[COLOR_EXTENT] = gdImageColorAllocate(im, 0, 255, 0); + colors[COLOR_CHUNK] = gdImageColorAllocate(im, 255, 0, 0); + colors[COLOR_DEV] = gdImageColorAllocate(im, 255, 0, 0); + colors[COLOR_FS] = gdImageColorAllocate(im, 0, 0, 0); + colors[COLOR_CSUM] = gdImageColorAllocate(im, 0, 0, 255); + colors[COLOR_RELOC] = gdImageColorAllocate(im, 128, 128, 128); + colors[COLOR_DATA] = gdImageColorAllocate(im, 100, 0, 0); + colors[COLOR_UNKNOWN] = gdImageColorAllocate(im, 50, 50, 50); +} + +int +list_fragments(int fd, u64 flags, char *dir) +{ + int ret; + struct btrfs_ioctl_search_args args; + struct btrfs_ioctl_search_key *sk = &args.key; + int i; + struct btrfs_ioctl_search_header *sh; + unsigned long off = 0; + int bgnum = 0; + u64 bgstart = 0; + u64 bglen = 0; + u64 bgend = 0; + u64 bgflags = 0; + u64 bgused = 0; + u64 saved_extent = 0; + u64 saved_len = 0; + int saved_color = 0; + u64 last_end = 0; + u64 areas = 0; + long px; + char name[1000]; + FILE *html; + int colors[COLOR_MAX]; + + gdImagePtr im = NULL; + int black = 0; + int width = 800; + + snprintf(name, sizeof(name), "%s/index.html", dir); + html = fopen(name, "w"); + if (!html) { + printf("unable to create %s\n", name); + exit(1); + } + + fprintf(html, "
\n"); + fprintf(html, "Btrfs Block Group Allocation Map\n"); + fprintf(html, "\n"); + fprintf(html, "
\n"); + + memset(&args, 0, sizeof(args)); + + sk->tree_id = 2; + sk->max_type = -1; + sk->min_type = 0; + sk->max_objectid = (u64)-1; + sk->max_offset = (u64)-1; + sk->max_transid = (u64)-1; + + /* just a big number, doesn't matter much */ + sk->nr_items = 4096; + + while(1) { + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); + if (ret < 0) { + fprintf(stderr, "ERROR: can't perform the search\n"); + goto out_close; + } + /* the ioctl returns the number of item it found in nr_items */ + if (sk->nr_items == 0) + break; + + off = 0; + for (i = 0; i < sk->nr_items; i++) { + int j; + + sh = (struct btrfs_ioctl_search_header *)(args.buf + + off); + off += sizeof(*sh); + if (sh->type == BTRFS_BLOCK_GROUP_ITEM_KEY) { + struct btrfs_block_group_item *bg; + + if (im) { + push_im(im, name, dir); + im = NULL; + + print_bg(html, name, bgstart, bglen, + bgused, bgflags, areas); + } + + ++bgnum; + + bg = (struct btrfs_block_group_item *) + (args.buf + off); + bgflags = btrfs_block_group_flags(bg); + bgused = btrfs_block_group_used(bg); + + printf("found block group %lld len %lld " + "flags %lld\n", sh->objectid, + sh->offset, bgflags); + if (!(bgflags & flags)) { + /* skip this block group */ + sk->min_objectid = sh->objectid + + sh->offset; + sk->min_type = 0; + sk->min_offset = 0; + break; + } + im = gdImageCreate(width, + (sh->offset / 4096 + 799) / width); + + black = gdImageColorAllocate(im, 0, 0, 0); + + for (j = 0; j < ARRAY_SIZE(colors); ++j) + colors[j] = black; + + init_colors(im, colors); + bgstart = sh->objectid; + bglen = sh->offset; + bgend = bgstart + bglen; + + snprintf(name, sizeof(name), "bg%d.png", bgnum); + + last_end = bgstart; + if (saved_len) { + px = (saved_extent - bgstart) / 4096; + for (j = 0; j < saved_len / 4096; ++j) { + int x = (px + j) % width; + int y = (px + j) / width; + gdImageSetPixel(im, x, y, + saved_color); + } + last_end += saved_len; + } + areas = 0; + saved_len = 0; + } + if (im && sh->type == BTRFS_EXTENT_ITEM_KEY) { + int c; + struct btrfs_extent_item *item; + + item = (struct btrfs_extent_item *) + (args.buf + off); + + if (use_color) + c = colors[get_color(item, sh->len)]; + else + c = black; + if (sh->objectid > bgend) { + printf("WARN: extent %lld is without " + "block group\n", sh->objectid); + goto skip; + } + if (sh->objectid == bgend) { + saved_extent = sh->objectid; + saved_len = sh->offset; + saved_color = c; + goto skip; + } + px = (sh->objectid - bgstart) / 4096; + for (j = 0; j < sh->offset / 4096; ++j) { + int x = (px + j) % width; + int y = (px + j) / width; + gdImageSetPixel(im, x, y, c); + } + if (sh->objectid != last_end) + ++areas; + last_end = sh->objectid + sh->offset; +skip:; + } + off += sh->len; + + /* + * record the mins in sk so we can make sure the + * next search doesn't repeat this root + */ + sk->min_objectid = sh->objectid; + sk->min_type = sh->type; + sk->min_offset = sh->offset; + } + sk->nr_items = 4096; + + /* increment by one */ + if (++sk->min_offset == 0) + if (++sk->min_type == 0) + if (++sk->min_objectid == 0) + break; + } + + if (im) { + push_im(im, name, dir); + print_bg(html, name, bgstart, bglen, bgused, bgflags, areas); + } + + if (use_color) { + fprintf(html, "

"); + fprintf(html, "data - dark red, "); + fprintf(html, "fs tree - black, "); + fprintf(html, "extent tree - green, "); + fprintf(html, "csum tree - blue, "); + fprintf(html, "reloc tree - grey, "); + fprintf(html, "other trees - red, "); + fprintf(html, "unknown tree - dark grey"); + fprintf(html, "

"); + } + fprintf(html, "\n"); + +out_close: + fclose(html); + + return ret; +} + +void +usage(void) +{ + printf("usage: btrfs-fragments [options] \n"); + printf(" -c use color\n"); + printf(" -d print data chunks\n"); + printf(" -m print metadata chunks\n"); + printf(" -s print system chunks\n"); + printf(" (default is data+metadata)\n"); + printf(" -o output directory, default is html\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + char *path; + int fd; + int ret; + u64 flags = 0; + char *dir = "html"; + DIR *dirstream = NULL; + + while (1) { + int c = getopt(argc, argv, "cmso:h"); + if (c < 0) + break; + switch (c) { + case 'c': + use_color = 1; + break; + case 'd': + flags |= BTRFS_BLOCK_GROUP_DATA; + break; + case 'm': + flags |= BTRFS_BLOCK_GROUP_METADATA; + break; + case 's': + flags |= BTRFS_BLOCK_GROUP_SYSTEM; + break; + case 'o': + dir = optarg; + break; + case 'h': + default: + usage(); + } + } + + set_argv0(argv); + argc = argc - optind; + if (check_argc_min(argc, 1)) { + usage(); + exit(1); + } + + path = argv[optind++]; + + fd = btrfs_open_dir(path, &dirstream, 1); + if (fd < 0) + exit(1); + + if (flags == 0) + flags = BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA; + + ret = list_fragments(fd, flags, dir); + close_file_or_dir(fd, dirstream); + if (ret) + exit(1); + + exit(0); +} -- cgit v1.2.3