code: mafs

ref: b3e875d7db45fca0cde9f4efa12872f61afc54d7
dir: /unused.c/

View raw version
#include <u.h>
#include <libc.h>
#include "dat.h"
#include "extents.h"
#include <bio.h>

/*
	indentify block numbers that are not used.
	These blocks can be used to update /a/frees as they are free to use.

	watch -e '6\.unused' 'date; ./6.unused -D 7 <{seq 1 1 3; seq 5 1 6} '

	disk/unused -D <{seq 1 1 3; seq 5 1 6} 7
collect used +1 +2 +3 +5 +6
show used
	1 .. 3
	5 .. 6
show unused
	3 .. 5

	disk/unused \
	`{dd -if /dev/sdF1/fs -bs 512 -iseek 1 -count 1 -quiet 1 | awk '$1 == "nblocks" { print $2 }'} \
	<`{disk/used /dev/sdF1/fs}

	# this crashes the rc window, too much memory?
	disk/unused 11721040049 `{disk/used /dev/sdF1/fs}

	disk/used /dev/sdF1/fs > /mnt/term/tmp/used.blocks
	disk/unused 11721040049 /mnt/term/tmp/used.blocks

	diff <{ disk/unused -l 32 <{disk/used tests/test.0/disk}} <{ disk/free tests/test.0/disk }
 */

enum {
	FileNameLen = KiB,
};

int debug = 0;
int chatty9p = 0;

struct Stream {
	struct Extents *es;
	Biobufhdr bp;
	int fd;
	s8 *buf;
	char *file;
	s8 name[32];
};
typedef struct Stream Stream;

void collect(Stream * s);
void *emalloc(u32);
static void init(Stream *s);
void panic(char *fmt, ...);
void show(Stream * s);

static void
usage(void)
{
	fprint(2, "usage: unused -l [-D ] nblocks list_of_used_blocks_file\n");
	exits("usage");
}

void
printfd(int fd, char *fmt, ...)
{
	va_list va;

	va_start(va, fmt);
	vfprint(fd, fmt, va);
	va_end(va);
}

void
main(int argc, char *argv[])
{
	Stream u;	/* u = used */
	struct Extents unused;
	u64 nblocks;
	int listblocks = 0;
	Errenv env = {0};

	envpp = privalloc();
	*envpp = &env;
	if(waserror()){
		panic(0, "err stack %d: %lux %lux %lux %lux %lux %lux",
			env.nlabel,
			env.label[0][JMPBUFPC], env.label[1][JMPBUFPC],
			env.label[2][JMPBUFPC], env.label[3][JMPBUFPC],
			env.label[4][JMPBUFPC], env.label[5][JMPBUFPC]);
	}

	ARGBEGIN{
	default:	usage();
	case 'D':	chatty9p = ++debug;	break;
	case 'l':	listblocks++;		break;
	}ARGEND

	if(argc != 2)
		usage();

	nblocks = atoll(argv[0]);
	u.file = strdup(argv[1]);

	if(u.file == nil)
		sysfatal("no used file");
	if(nblocks == 0)
		sysfatal("nblocks == 0");

	strncpy(u.name, "used", 32);
	init(&u);

	collect(&u);
	if(debug)
		show(&u);

	/* identify unused blocks */
	initextents(&unused, "unused", 0, 0, 2, nil, fprint, panic, emalloc);
	holes(u.es, &unused);

	if(listblocks)
		showblocknos(1, &unused);
	else
		showextents(1, "", &unused);

	/* why bother? just exits(nil) as cinap suggests */
	Bterm(&u.bp);
	free(u.buf);
	close(u.fd);
	exits(nil);
}

static void
init(Stream *s)
{
	s->buf = emalloc(MiB);
	s->fd = open(s->file, OREAD);
	if(Binits(&s->bp, s->fd, OREAD, (u8*)s->buf, MiB) == Beof)
		sysfatal
		    ("%s: Binits on msin failed: status code: Beof, errstr: %r",
		     argv0);
	Blethal(&s->bp, nil);
}

void
collect(Stream * s)
{
	s8 *p, *ep;
	u64 start, end, nblocks;

	if(debug)
		print("collect %s ", s->name);
	s->es = emalloc(sizeof(Extents));
	initextents(s->es, s->name, 0, 0, 2, nil, fprint, panic, emalloc);
	while((s->buf = Brdstr(&s->bp, '\n', 1)) != nil) {
		p = s->buf;
		start = strtoull(p, &ep, 10);
		if(p == ep)
			panic("could not read");

		p = ep;
		p += 1; /* skip over the space */
		end = strtoull(p, &ep, 10);
		if(p == ep)
			panic("could not read");

		p = ep;
		p += 1; /* skip over the space */
		nblocks = strtoull(p, &ep, 10);
		if(p == ep)
			panic("could not read");
		if(end-start+1 != nblocks)
			panic("loadextents does not match up: start %llud end %llud nblocks %llud",
					start, end, nblocks);

		ufree(s->es, start, nblocks);
// show(s);
	}
	if(debug)
		print("\n");
}

void
show(Stream * s)
{
	print("show %s\n", s->name);
	if(s == nil || s->es == nil){
		print("nil\n");
		return;
	}
	showextents(1, "show stream: ", s->es);
}