diff options
author | Daniil Cherednik <dan.cherednik@gmail.com> | 2020-12-03 00:37:14 +0300 |
---|---|---|
committer | Daniil Cherednik <dan.cherednik@gmail.com> | 2020-12-03 00:37:14 +0300 |
commit | c7b09f57b650d1a22fd537d3f45d85b18d5aa253 (patch) | |
tree | 6264de6a9aeb28f6072819b68ea034868e293d95 | |
download | ufs_ffinfo-c7b09f57b650d1a22fd537d3f45d85b18d5aa253.tar.gz |
ufs_ffinfo - tool to get free space fragmentation info for
FreeBSD UFS file system. First commit.
-rw-r--r-- | readme.txt | 16 | ||||
-rw-r--r-- | src/Makefile | 11 | ||||
-rw-r--r-- | src/ufs_ffinfo.c | 275 |
3 files changed, 302 insertions, 0 deletions
diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..e2813c2 --- /dev/null +++ b/readme.txt @@ -0,0 +1,16 @@ +// // //////// /////// //////// //////// // ////// +// // // // // // // // +// // // // // // // ////// // ////// +// // //////// ////// //////// //////// // // // //// // // +// // // // // // // // // // // // +// // // // // // // // // // // // + ////// // /////// // // // // // // ////// + +================================================================================ + + ufs_ffinfo (UFS Free Frag Information) - tool to get free space information +for UFS file system. Current version supports FreeBSD UFS. + +Build from source: +1. Make sure freebsd source installed. +2. Run 'make' command in the src directory diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..4adfe0e --- /dev/null +++ b/src/Makefile @@ -0,0 +1,11 @@ + +PACKAGE=runtime +PROG= ufs_ffinfo +SRCS= ufs_ffinfo.c +LIBADD= ufs +WARNS?= 6 +CFLAGS+= -I${.CURDIR} + +.PATH: ${SRCTOP}/sys/ufs/ffs + +.include <bsd.prog.mk> diff --git a/src/ufs_ffinfo.c b/src/ufs_ffinfo.c new file mode 100644 index 0000000..9851547 --- /dev/null +++ b/src/ufs_ffinfo.c @@ -0,0 +1,275 @@ + +#include <ufs/ffs/fs.h> +#include <libufs.h> + +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> +#include <strings.h> +#include <stdlib.h> + +#define MAXBINS 20 + +#ifndef max +# define max(a,b) ((a) > (b) ? (a) : (b)) +#endif + +struct ffinfo { + uint64_t bins[MAXBINS]; + uint64_t free_blocks[MAXBINS]; + uint64_t blocks_num; +}; + +struct ffinfo_ctx { + struct fs *fs; + struct ffinfo* info_cg; + struct ffinfo info_sum; + uint8_t verbose; +}; + +static int +rdfs(ufs2_daddr_t bno, size_t size, void *bf, int fsi) +{ + ssize_t n; + + if (bno < 0) { + fprintf(stderr, "rdfs: attempting to read negative block number"); + return -1; + } + + if (lseek(fsi, (off_t)bno * DEV_BSIZE, 0) < 0) { + fprintf(stderr, "rdfs: seek error: %jd", (intmax_t)bno); + return -1; + } + + n = read(fsi, bf, size); + + if (n != (ssize_t)size) { + fprintf(stderr, "rdfs: read error: %jd", (intmax_t)bno); + return -1; + } + + return 0; +} + +static int +isblock(const struct fs *fs, unsigned char *cp, int h) +{ + unsigned char mask; + + switch (fs->fs_frag) { + case 8: + return (cp[h] == 0xff); + case 4: + mask = 0x0f << ((h & 0x1) << 2); + return ((cp[h >> 1] & mask) == mask); + case 2: + mask = 0x03 << ((h & 0x3) << 1); + return ((cp[h >> 2] & mask) == mask); + case 1: + mask = 0x01 << (h & 0x7); + return ((cp[h >> 3] & mask) == mask); + default: + fprintf(stderr, "isblock bad fs_frag %d\n", fs->fs_frag); + return (0); + } +} + +/* + * Read the superblock and init the work ctx + */ + +static struct ffinfo_ctx * +ffinfo_ctx_init(int fd, uint8_t verbose) +{ + struct fs *fs; + struct ffinfo_ctx *ctx; + int ret; + + // Read the superblock + if ((ret = sbget(fd, &fs, -1)) != 0) { + fprintf(stderr, "Unable to read superblock\n"); + return NULL; + } + + ctx = calloc(1, sizeof(struct ffinfo_ctx)); + if (!ctx) { + fprintf(stderr, "Unable to allocate memory for ctx\n"); + goto err_free_sb_mem; + } + + ctx->fs = fs; + ctx->verbose = verbose; + + if (verbose) { + fprintf(stdout, "Superblock found,\n" + "size (KB) total: %llu, CG num: %d, block size: %d, frags per block: %d, max_bpg: %d\n", + fs->fs_size * fs->fs_fsize / 1024llu, fs->fs_ncg, fs->fs_bsize, fs->fs_frag, fs->fs_maxbpg); + } + + ctx->info_cg = calloc(fs->fs_ncg, sizeof(struct ffinfo)); + if (!ctx->info_cg) { + fprintf(stderr, "Unable to allocate memory for info_cg\n"); + goto err_free_ctx; + } + + return ctx; + +err_free_ctx: + free(ctx); + +err_free_sb_mem: + free(fs); + + return NULL; +} + +static void +ffinfo_ctx_free(struct ffinfo_ctx *ctx) +{ + free(ctx->info_cg); + free(ctx->fs); + free(ctx); +} + +static void +ffinfo_print_hysto(uint64_t hysto_blocks_num, uint64_t total_blocks_num, const uint64_t *bins, const uint64_t *free_blocks) +{ + uint64_t blocks; + int e; + for (blocks = 1, e = 0; blocks < hysto_blocks_num && e < MAXBINS; blocks <<= 1, e++) { + fprintf(stdout, " [%lu ... %lu) - %lu %lu %.2f\n", blocks, blocks << 1, bins[e], free_blocks[e], 100 * free_blocks[e]/(float)total_blocks_num); + } +} + +static void +ffinfo_print(const struct ffinfo_ctx *ctx) +{ + uint32_t c; + uint32_t blocks; + uint32_t e; + uint64_t free_blocks; + + struct ffinfo* info; + + uint64_t sum_bins[MAXBINS] = {0}; + uint64_t sum_free_blocks[MAXBINS] = {0}; + uint64_t max_blocks_num = 0; + uint64_t total_free_blocks_num = 0; + + for (c = 0; c < ctx->fs->fs_ncg; c++) { + free_blocks = 0; + info = &ctx->info_cg[c]; + max_blocks_num = max(max_blocks_num, ctx->info_cg[c].blocks_num); + for (blocks = 1, e = 0; blocks < ctx->info_cg[c].blocks_num && e < MAXBINS; blocks <<= 1, e++) { + free_blocks += info->free_blocks[e]; + sum_bins[e] += info->bins[e]; + sum_free_blocks[e] += info->free_blocks[e]; + } + total_free_blocks_num += free_blocks; + + fprintf(stdout, "Cylinder group %d, total blocks: %lu, free blocks: %lu\n", c, ctx->info_cg[c].blocks_num, free_blocks); + ffinfo_print_hysto(ctx->info_cg[c].blocks_num, free_blocks, info->bins, info->free_blocks); + } + + fprintf(stdout, "Summary\n"); + + ffinfo_print_hysto(max_blocks_num, total_free_blocks_num, sum_bins, sum_free_blocks); + + +} + +static union { + struct cg cg; + char pad[MAXBSIZE]; +} cgun2; +#define aocg cgun2.cg + +static void +ffinfo_scan_freemap(const struct fs* fs, char* map, uint32_t size, struct ffinfo* res) +{ + uint32_t d; + uint32_t blocks = size / fs->fs_frag; + uint32_t bin; + uint32_t free_count= 0; + + for (d = 0; d < blocks; d++) { + if (isblock(fs, map, d)) { + free_count++; + } else { + if (free_count) { + bin = fls(free_count) - 1; + + if (bin >= MAXBINS) + break; + + res->bins[bin]++; + res->free_blocks[bin] += free_count;; + + free_count = 0; + } + } + } + + if (free_count) { + bin = fls(free_count) - 1; + if (bin >= MAXBINS) + return; + res->bins[bin]++; + res->free_blocks[bin] += free_count; + } +} + +static int +ffinfo_run(int fd) +{ + uint32_t c; + char* map; + struct ffinfo_ctx *ctx; + + ctx = ffinfo_ctx_init(fd, 1); + if (!ctx) { + return -1; + } + + for (c = 0; c < ctx->fs->fs_ncg; c++) { + if (rdfs(fsbtodb(ctx->fs, cgtod(ctx->fs, c)), + (size_t)ctx->fs->fs_cgsize, (void*)&aocg, fd) < 0) { + goto err_free_ctx; + } + + ctx->info_cg[c].blocks_num = aocg.cg_ndblk / ctx->fs->fs_frag; + + map = cg_blksfree(&aocg); + + ffinfo_scan_freemap(ctx->fs, map, aocg.cg_ndblk, &ctx->info_cg[c]); + } + + ffinfo_print(ctx); + + ffinfo_ctx_free(ctx); + + return 0; + +err_free_ctx: + + ffinfo_ctx_free(ctx); + return -1; +} + +int +main(int argc, char *argv[]) +{ + const char* special; + int fsfd; + + if (argc != 2) + fprintf(stderr, "no block device given\n"); + + special = argv[1]; + + if ((fsfd = open(special, O_RDONLY)) < 0 || ffinfo_run(fsfd) < 0) + fprintf(stderr, "err\n"); + + return 0; +} |