summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniil Cherednik <dan.cherednik@gmail.com>2020-12-03 00:37:14 +0300
committerDaniil Cherednik <dan.cherednik@gmail.com>2020-12-03 00:37:14 +0300
commitc7b09f57b650d1a22fd537d3f45d85b18d5aa253 (patch)
tree6264de6a9aeb28f6072819b68ea034868e293d95
downloadufs_ffinfo-c7b09f57b650d1a22fd537d3f45d85b18d5aa253.tar.gz
ufs_ffinfo - tool to get free space fragmentation info for
FreeBSD UFS file system. First commit.
-rw-r--r--readme.txt16
-rw-r--r--src/Makefile11
-rw-r--r--src/ufs_ffinfo.c275
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;
+}