Commit Diff


commit - /dev/null
commit + 4338ef2e71b1ccd38b98a8a41011a0e57d9b61c2
blob - /dev/null
blob + 2c0ceeb5195f6a0010a8b62c59309876c7d44ab9 (mode 644)
--- /dev/null
+++ .gitignore
@@ -0,0 +1,6 @@
+mp
+newfs_sufs
+fuse_sufs
+dumpfs_sufs
+*.core
+*.swp
blob - /dev/null
blob + 9f4355b83655b045e4a56d4c98bdf76c463ead83 (mode 644)
--- /dev/null
+++ Makefile
@@ -0,0 +1,38 @@
+.POSIX:
+
+BIN = newfs_sufs fuse_sufs dumpfs_sufs
+CFLAGS += -fno-strict-aliasing
+
+all: ${BIN}
+
+clean:
+	rm -f ${BIN} *.core test.img
+
+newfs: newfs_sufs
+	rm -f test.img
+	dd if=/dev/zero of=test.img bs=1K count=64
+	./newfs_sufs -n -b 512 test.img
+	xxd -s 65536 test.img | head
+	xxd -s $$((261 * 32768)) test.img | head
+
+mount: fuse_sufs
+	mkdir -p mp
+	doas ./fuse_sufs -d -f -o allow_other -o default_permissions mp
+
+test:
+	doas chown $$(id -u) mp
+	echo 'Hello World' > mp/file
+	cat mp/file
+	rm mp/file
+
+dump: dumpfs_sufs
+	./dumpfs_sufs test.img
+
+fuse_sufs: fuse_sufs.c sufs.h
+	${CC} -o $@ fuse_sufs.c ${CFLAGS} -lfuse
+
+newfs_sufs: newfs_sufs.c sufs.h
+	${CC} -o $@ newfs_sufs.c ${CFLAGS}
+
+dumpfs_sufs: dumpfs_sufs.c sufs.h
+	${CC} -o $@ dumpfs_sufs.c ${CFLAGS}
blob - /dev/null
blob + 49f181d9bd19b04c7d1251a9a75a0d1a81e08fc5 (mode 644)
--- /dev/null
+++ dumpfs_sufs.c
@@ -0,0 +1,83 @@
+#include <inttypes.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <err.h>
+#include "sufs.h"
+
+static int fd;
+static char *block;
+static struct sufs_superblock sb;
+
+#define print(sb, field) do_print (#field, (sb).field)
+static void do_print (const char *name, uint64_t value)
+{
+	printf ("%s: %"PRIu64"\n", name, value);
+}
+
+static void read_block (uint64_t blkno)
+{
+	ssize_t x;
+	if (lseek (fd, blkno * sb.bsize, SEEK_SET) < 0)
+		err (1, "read_block(%"PRIu64"): lseek()", blkno);
+	x = read (fd, block, sb.bsize);
+	if (x != sb.bsize)
+		err (1, "read_block(%"PRIu64"): read(): %zd", blkno, x);
+}
+
+static uint64_t count (uint64_t begin, uint64_t num)
+{
+	uint64_t cnt = 0;
+	uint64_t n = (num + 7) / 8;
+
+	for (uint64_t i = 0; i < n; i += sb.bsize) {
+		read_block (begin + i);
+		for (uint64_t j = 0; j < sb.bsize && j < n; ++j) {
+			for (unsigned k = 0; k < 8 && (((i + j) * 8 + k) < num); ++k) {
+				if ((block[j] >> k) & 1)
+					++cnt;
+			}
+		}
+	}
+	
+	return cnt;
+}
+
+int main (int argc, char *argv[])
+{
+	if (argc != 2)
+		errx (1, "usage: dumpfs_sufs file");
+
+	fd = open (argv[1], O_RDONLY);
+	if (fd < 0)
+		err (1, "open()");
+
+	if (read (fd, &sb, sizeof (sb)) != sizeof (sb))
+		err (1, "read()");
+
+	if (sb.magic != SUFS_MAGIC) {
+		if (lseek (fd, SUFS_BOOTSIZE, SEEK_SET) < 0)
+			err (1, "lseek()");
+
+		if (read (fd, &sb, sizeof (sb)) != sizeof (sb))
+			err (1, "read()");
+	}
+
+	block = malloc (sb.bsize);
+
+	printf ("magic: %#08"PRIx32" (%s)\n", sb.magic, sb.magic == SUFS_MAGIC ? "OK" : "INVAL");
+	print (sb, bsize);
+	print (sb, nino);
+	print (sb, ndata);
+	print (sb, ibmoff);
+	print (sb, dbmoff);
+	print (sb, ioff);
+	print (sb, doff);
+	print (sb, clean);
+	do_print ("ifree", count (sb.ibmoff, sb.nino));
+	do_print ("dfree", count (sb.dbmoff, sb.ndata));
+
+	close (fd);
+	return 0;
+}
blob - /dev/null
blob + f70ea5dfb3e17c3a68a4354bff38bb87dad22ed2 (mode 644)
--- /dev/null
+++ fuse_sufs.c
@@ -0,0 +1,782 @@
+#include <fuse_opt.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fuse.h>
+#include <err.h>
+#include "sufs.h"
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+enum {
+	KEY_VERSION,
+	KEY_HELP,
+};
+
+static const struct fuse_opt opts[] = {
+	FUSE_OPT_KEY("-V",		KEY_VERSION),
+	FUSE_OPT_KEY("-h",		KEY_HELP),
+	FUSE_OPT_END
+};
+static struct sufs_superblock sb;
+static char *buffer, *dbuffer;
+static int fd;
+
+static void write_inode (uint64_t, const struct sufs_inode *);
+static struct sufs_inode read_inode (uint64_t ino);
+
+void read_block (uint64_t b, char *d)
+{
+	printf ("read_block(%zu);\n", (size_t)b);
+	if (lseek (fd, b * sb.bsize, SEEK_SET) < 0)
+		err (1, "read_block(): lseek()");
+	if (read (fd, d, sb.bsize) != sb.bsize)
+		err (1, "read_block(): read()");
+}
+
+void write_block (uint64_t b, const char *d)
+{
+	printf ("write_block(%zu);\n", (size_t)b);
+	if (lseek (fd, b * sb.bsize, SEEK_SET) < 0)
+		err (1, "write_block(): lseek()");
+	if (write (fd, d, sb.bsize) != sb.bsize)
+		err (1, "write_block(): read()");
+}
+
+static uint64_t alloc_bitmap (uint64_t begin, uint64_t len)
+{
+	uint8_t *p;
+
+	for (uint64_t i = 0; i < len; i += sb.bsize) {
+		read_block (begin + i, buffer);
+		for (uint32_t j = 0; j < MIN (len, sb.bsize); ++j) {
+			p = buffer + j;
+			if (*p == 0)
+				continue;
+
+			for (unsigned k = 0; k < 8; ++k) {
+				uint8_t mask = 1 << k;
+				if ((*p & mask) == mask) {
+					printf ("i=%zu, j=%u, k=%u\n", (size_t)i, j, k);
+					*p &= ~mask;
+					write_block (begin + i, buffer);
+					return (i + j) * 8 + k;
+				}
+			}
+		}
+	}
+
+	return -1;
+}
+
+static void free_bitmap (uint64_t begin, uint64_t idx)
+{
+	uint64_t blkno;
+	unsigned off, bit;
+
+	blkno = begin + idx / (8 * sb.bsize);
+	off = (idx / 8) % sb.bsize;
+	bit = idx % 8;
+
+	printf ("free_bitmap(%zu, %zu, %zu, %u, %u);\n", (size_t)begin, (size_t)idx, (size_t)blkno, off, bit);
+
+	read_block (blkno, buffer);
+	((uint8_t *)buffer)[off] |= 1 << bit;
+	write_block (blkno, buffer);
+}
+
+static uint64_t alloc_inode (void)
+{
+	uint64_t ino = alloc_bitmap (sb.ibmoff, sb.nino);
+
+	printf ("alloc_inode(): %zu\n", (size_t)ino);
+
+	return ino;
+}
+
+static void free_dblock (uint64_t blkno)
+{
+	free_bitmap (sb.dbmoff, blkno - sb.doff);
+}
+
+static void free_inode (uint64_t ino)
+{
+	struct sufs_inode in;
+
+	printf ("free_inode(%zu);\n", (size_t)ino);
+
+	in = read_inode (ino);
+
+	--in.nlink;
+
+	if (in.nlink != 0) {
+		write_inode (ino, &in);
+		return;
+	}
+
+	for (uint64_t i = 0; i < MIN (in.blocks, SUFS_NDIRECT); ++i) {
+		free_dblock (in.direct[i]);
+	}
+
+	if (in.blocks >= SUFS_NDIRECT)
+		errx (1, "free_inode(): TODO: indirect blocks");
+
+	free_bitmap (sb.ibmoff, ino);
+}
+
+static uint64_t alloc_dblock (void)
+{
+	uint64_t b =  alloc_bitmap (sb.dbmoff, sb.ndata) + sb.doff;
+	memset (buffer, 0, sb.bsize);
+	write_block (b, buffer);
+	printf ("alloc_dblock(): %zu\n", (size_t)b);
+	return b;
+}
+
+uint64_t resolve_inode_block (const struct sufs_inode *in, uint64_t blkno)
+{
+	const size_t pbp = sb.bsize / sizeof (uint64_t);
+	volatile uint64_t *tmp = (volatile uint64_t *)buffer;
+	uint64_t i;
+
+	if (blkno < SUFS_NDIRECT) {
+		return in->direct[blkno];
+	} else if (blkno < (SUFS_NDIRECT + pbp)) {
+		read_block (in->indir[0], buffer);
+		return tmp[blkno - SUFS_NDIRECT];
+	} else if (blkno < (SUFS_NDIRECT + pbp * pbp)) {
+		read_block (in->indir[1], buffer);
+		i = blkno - SUFS_NDIRECT - pbp;
+		read_block (tmp[i / pbp], buffer);
+		return tmp[i % pbp];
+	} else if (blkno < (SUFS_NDIRECT + pbp * pbp * pbp)) {
+		read_block (in->indir[2], buffer);
+		i = blkno - SUFS_NDIRECT - pbp * pbp;
+		read_block (tmp[i / pbp / pbp], buffer);
+		read_block (tmp[i / pbp % pbp], buffer);
+		return tmp[i % pbp];
+	} else {
+		errx (1, "resolve_inode_block(): inode out of range");
+	}
+}
+
+void free_block (uint64_t block)
+{
+	if (block == 0)
+		return;
+
+	errx (1, "free_block(): TODO");
+}
+
+void map_inode (uint64_t ino, struct sufs_inode *in, uint64_t blkno, uint64_t block)
+{
+	const size_t pbp = sb.bsize / sizeof (uint64_t);
+	volatile uint64_t *tmp = (volatile uint64_t *)buffer;
+	uint64_t i;
+
+	printf ("map_inode(%zu, %zu, %zu);\n", (size_t)ino, (size_t)blkno, (size_t)block);
+
+	if (blkno < SUFS_NDIRECT) {
+		free_block (in->direct[blkno]);
+		in->direct[blkno] = block;
+	} else if (blkno < (SUFS_NDIRECT + pbp)) {
+		if (in->indir[0] == 0) {
+			in->indir[0] = alloc_dblock ();
+			write_inode (ino, in);
+		}
+		read_block (in->indir[0], buffer);
+		tmp[blkno - SUFS_NDIRECT] = block;
+		write_block (in->indir[0], buffer);
+	} else if (blkno < (SUFS_NDIRECT + pbp * pbp)) {
+		if (in->indir[1] == 0) {
+			in->indir[1] = alloc_dblock ();
+			write_inode (ino, in);
+		}
+		read_block (in->indir[1], buffer);
+		i = blkno - SUFS_NDIRECT - pbp;
+		if (tmp[i / pbp] == 0) {
+			tmp[i / pbp] = alloc_dblock ();
+			write_block (in->indir[1], buffer);
+		}
+		read_block (tmp[i / pbp], buffer);
+		tmp[i % pbp] = block;
+		write_block (tmp[i % pbp], buffer);
+	} else if (blkno < (SUFS_NDIRECT + pbp * pbp * pbp)) {
+		if (in->indir[2] == 0) {
+			in->indir[2] = alloc_dblock ();
+			write_inode (ino, in);
+		}
+
+		read_block (in->indir[2], buffer);
+		i = blkno - SUFS_NDIRECT - pbp * pbp;
+		if (tmp[i / pbp / pbp] == 0) {
+			tmp[i / pbp / pbp] = alloc_dblock ();
+			write_block (in->indir[2], buffer);
+		}
+
+		read_block (tmp[i / pbp / pbp], buffer);
+
+		if (tmp[i / pbp % pbp] == 0) {
+			tmp[i / pbp % pbp] = alloc_dblock ();
+			write_block (tmp[i / pbp / pbp], buffer);
+		}
+		read_block (tmp[i / pbp % pbp], buffer);
+		tmp[i % pbp] = block;
+		write_block (tmp[i / pbp % pbp], buffer);
+	} else {
+		errx (1, "resolve_inode_block(): inode out of range");
+	}
+}
+
+void read_block_inode (const struct sufs_inode *in, uint64_t blkno, char *buf)
+{
+	uint64_t b;
+
+	b = resolve_inode_block (in, blkno);
+	if (b != 0) {
+		read_block (b, buf);
+	} else {
+		memset (buf, 0, sb.bsize);
+	}
+}
+
+void write_block_inode (uint64_t ino, struct sufs_inode *in, uint64_t blkno, const char *buf)
+{
+	uint64_t b;
+	
+	b = resolve_inode_block (in, blkno);
+	if (b == 0) {
+		b = alloc_dblock ();
+		map_inode (ino, in, blkno, b);
+		++in->blocks;
+		write_inode (ino, in);
+	}
+	write_block (b, buf);
+}
+
+static struct sufs_inode read_inode (uint64_t ino)
+{
+	struct sufs_inode in;
+	printf ("read_inode(%zu);\n", (size_t)ino);
+	read_block (sufs_iblk (sb, ino), buffer);
+	memcpy (&in, buffer + sufs_ioff (sb, ino), sizeof (in));
+	return in;
+}
+
+static void write_inode (uint64_t ino, const struct sufs_inode *in)
+{
+	printf ("write_inode(%zu);\n", (size_t)ino);
+	read_block (sufs_iblk (sb, ino), buffer);
+	memcpy (buffer + sufs_ioff (sb, ino), in, sizeof (*in));
+	write_block (sufs_iblk (sb, ino), buffer);
+}
+
+static uint64_t searchdir (uint64_t ino, const char *name)
+{
+	struct sufs_dirent *ent;
+	struct sufs_inode in;
+	uint64_t i;
+	size_t n, len;
+
+	printf ("searchdir(%zu, %s);\n", (size_t)ino, name);
+
+	len = strlen (name);
+
+	in = read_inode (ino);
+	if ((in.mode & S_IFMT) != S_IFDIR) {
+		errno = ENOTDIR;
+		return 0;
+	}
+
+	for (i = 0; i < in.blocks; ++i) {
+		read_block_inode (&in, i, buffer);
+
+		for (n = 0; n < sb.bsize; n += ent->size) {
+			ent = (struct sufs_dirent *)(buffer + n);
+
+			if (ent->ino == 0)
+				break;
+
+			if (ent->len != len)
+				continue;
+
+			if (memcmp (name, ent->name, len) == 0)
+				return ent->ino;
+		}
+	}
+
+	errno = ENOENT;
+	return 0;
+}
+
+static uint64_t lookup2 (char *path)
+{
+	uint64_t parent;
+	char *name, *slash;
+	size_t len;
+
+	// remove trailing slashes
+	len = strlen (path);
+	while (len > 0 && path[len - 1] == '/')
+		--len;
+	path[len] = '\0';
+
+	if (len == 0 || strcmp (path, "/") == 0)
+		return SUFS_INO_ROOT;
+
+	// split path at '/'
+	slash = strrchr (path, '/');
+	if (slash == NULL)
+		errx (1, "lookup2(): failed to split path");
+	*slash = '\0';
+	name = slash + 1;
+
+	// find parent inode
+	parent = lookup2 (path);
+	if (parent == 0) {
+		return 0;
+	}
+
+	return searchdir (parent, name);
+}
+
+static uint64_t lookup (const char *path)
+{
+	uint64_t ino;
+	char *buf;
+
+	printf ("lookup(\"%s\");\n", path);
+
+	if (*path != '/') {
+		errno = EINVAL;
+		return 0;
+	}
+
+	buf = strdup (path);
+	ino = lookup2 (buf);
+
+	free (buf);
+	return ino;
+}
+
+int sufs_getattr (const char *path, struct stat *st)
+{
+	struct sufs_inode in;
+	uint64_t ino;
+
+	printf ("getattr(\"%s\");\n", path);
+
+	ino = lookup (path);
+	if (ino == 0)
+		return -errno;
+
+	in = read_inode (ino);
+
+	st->st_ino = ino;
+	st->st_mode = in.mode;
+	printf ("mode = %o\n", (unsigned)in.mode);
+	st->st_nlink = in.nlink;
+	st->st_uid = in.uid;
+	st->st_gid = in.gid;
+	st->st_atime = in.atime;
+	st->st_mtime = in.mtime;
+	st->st_ctime = in.ctime;
+	st->st_size = in.size;
+	st->st_blocks = in.blocks;
+	st->st_blksize = sb.bsize;
+	return 0;
+}
+
+int sufs_open (const char *path, struct fuse_file_info *ffi)
+{
+	uint64_t ino;
+
+	ino = lookup (path);
+	return ino > 0 ? 0 : -errno;
+}
+
+int sufs_read (const char *path, char *buf, size_t size, off_t off, struct fuse_file_info *ffi)
+{
+	struct sufs_inode in;
+	uint64_t ino;
+
+	printf ("read(\"%s\", %zu, %zu);\n", path, (size_t)off, size);
+
+	ino = lookup (path);
+	if (ino == 0)
+		return -errno;
+
+	in = read_inode (ino);
+
+	if (off > in.size)
+		return 0;
+
+	if ((off + size) >= sb.bsize)
+		size = sb.bsize - off;
+
+	if ((off + size) >= in.size)
+		size = in.size - off;
+
+	read_block_inode (&in, off / sb.bsize, dbuffer);
+	memcpy (buf, dbuffer + off % sb.bsize, size);
+
+	return size;
+}
+
+int sufs_write (const char *path, const char *buf, size_t size, off_t off, struct fuse_file_info *ffi)
+{
+	struct sufs_inode in;
+	uint64_t ino;
+
+	printf ("write(\"%s\", %zu, %zu);\n", path, size, (size_t)off);
+
+	ino = lookup (path);
+	if (ino == 0)
+		return -errno;
+
+	in = read_inode (ino);
+
+	if ((off + size) >= sb.bsize)
+		size = sb.bsize - off;
+
+	memcpy (dbuffer + off % sb.bsize, buf, size);
+	write_block_inode (ino, &in, off / sb.bsize, dbuffer);
+
+	if (off + size >= in.size)
+		in.size = off + size;
+
+	write_inode (ino, &in);
+
+	return size;
+}
+
+int sufs_readdir (const char *path, void *data, fuse_fill_dir_t filler, off_t off, struct fuse_file_info *ffi)
+{
+	char name[256];
+	struct sufs_dirent *ent;
+	struct sufs_inode in;
+	uint64_t ino;
+	size_t len;
+
+	if (off != 0)
+		return 0;
+
+	ino = lookup (path);
+	if (ino == 0)
+		return -errno;
+
+	in = read_inode (ino);
+
+	for (uint64_t i = 0; i < in.blocks; ++i) {
+		read_block_inode (&in, i, buffer);
+		for (uint32_t j = 0; j < sb.bsize; j += ent->size) {
+			ent = (struct sufs_dirent *)(buffer + j);
+			if (ent->size == 0)
+				break;
+			len = ent->len;
+			memcpy (name, ent->name, len);
+			name[len] = '\0';
+			filler (data, name, NULL, 0);
+		}
+	}
+
+
+	return 0;
+}
+
+int sufs_chown (const char *path, uid_t uid, gid_t gid)
+{
+	struct sufs_inode in;
+	uint64_t ino;
+
+	printf ("chown(\"%s\", %u, %u);\n", path, (unsigned)uid, (unsigned)gid);
+
+	ino = lookup (path);
+	if (ino == 0)
+		return -errno;
+
+	in = read_inode (ino);
+
+	if (uid != -1)
+		in.uid = uid;
+
+	if (gid != -1)
+		in.gid = gid;
+
+	write_inode (ino, &in);
+
+	return 0;
+}
+
+int sufs_chmod (const char *path, mode_t mode)
+{
+	struct sufs_inode in;
+	uint64_t ino;
+
+	ino = lookup (path);
+	if (ino == 0)
+		return -errno;
+
+	in = read_inode (ino);
+
+	in.mode &= ~07777;
+	in.mode |= mode & 07777;
+	
+	write_inode (ino, &in);
+
+	return 0;
+}
+
+
+static int add_to_dir (uint64_t pino, struct sufs_inode *pin, const char *name, uint64_t ino)
+{
+	struct sufs_dirent *ent, *new;
+	uint16_t rem;
+	uint64_t i, off;
+	size_t len;
+
+	printf ("add_to_dir(\"%s\", %zu);\n", name, (size_t)ino);
+
+	len = strlen (name);
+	if (len > 255) {
+		errno = -E2BIG;
+		return -1;
+	}
+
+	for (i = 0; i < pin->blocks; ++i) {
+		read_block_inode (pin, i, buffer);
+		for (uint64_t j = 0; j < sb.bsize; j += ent->size) {
+			ent = (struct sufs_dirent *)(buffer + j);
+			if (ent->size == 0)
+				break;
+
+			printf ("ent = { ino: %zu, len: %zu, size: %zu, name: \"%*s\" }\n",
+				(size_t)ent->ino, (size_t)ent->len, (size_t)ent->size, (int)ent->len, ent->name);
+
+			rem = ent->size - ent->len - sizeof (struct sufs_dirent);
+
+			printf ("rem = %zu\n", (size_t)rem);
+
+			if (rem < (sizeof (struct sufs_dirent) + len + 8))
+				continue;
+
+			off = j + sizeof (struct sufs_dirent) + len;
+			off = (off + 7) & ~7;
+			printf ("j = %zu, off = %zu\n", (size_t)j, (size_t)off);
+
+			new = (struct sufs_dirent *)(buffer + off);
+			new->ino = ino;
+			new->size = rem;
+			new->len = len;
+			memcpy (new->name, name, len);
+			ent->size = off - j;
+
+			write_block_inode (pino, pin, i, buffer);
+			return 0;
+		}
+	}
+
+	errx (1, "add_to_dir(): TODO: allocating new directory blocks");
+}
+
+static int find_parent (const char *path, struct sufs_inode *parent)
+{
+	uint64_t ino;
+	char *ppath, *tmp;
+
+	tmp = strdup (path);
+	ppath = dirname (tmp);
+	ino = lookup (ppath);
+	free (tmp);
+	if (ino == 0)
+		return -1;
+
+	*parent = read_inode (ino);
+	return (parent->mode & S_IFMT) == S_IFDIR ? 0 : -1;
+}
+
+int sufs_mknod (const char *path, mode_t mode, dev_t dev)
+{
+	struct fuse_context *ctx;
+	struct sufs_inode pin, in;
+	uint64_t pino, ino;
+	char *name, *ppath, *tmp;
+
+	printf ("mknod(\"%s\", %o, %u);\n", path, (unsigned)mode, (unsigned)dev);
+
+	tmp = strdup (path);
+	ppath = dirname (tmp);
+	pino = lookup (ppath);
+	if (pino == 0)
+		return -errno;
+
+	pin = read_inode (pino);
+	if ((pin.mode & S_IFMT) != S_IFDIR)
+		return -ENOTDIR;
+
+	ino = alloc_inode ();
+
+	ctx = fuse_get_context ();
+	memset (&in, 0, sizeof (in));
+	in.mode = mode;
+	in.nlink = 1;
+	in.uid = ctx->uid;
+	in.gid = pin.gid;
+	in.atime = time (NULL);
+	in.mtime = time (NULL);
+	in.ctime = time (NULL);
+
+	write_inode (ino, &in);
+
+	tmp = strdup (path);
+	name = basename (tmp);
+	add_to_dir (pino, &pin, name, ino);
+	return 0;
+}
+
+int sufs_create (const char *path, mode_t mode, struct fuse_file_info *ffi)
+{
+	return sufs_mknod (path, mode, 0);
+}
+
+int sufs_release (const char *path, struct fuse_file_info *ffi)
+{
+	return 0;
+}
+
+int sufs_unlink (const char *path)
+{
+	struct sufs_inode parent, in;
+	const char *name;
+	size_t len;
+	char *tmp;
+
+	printf ("unlink(\"%s\");\n", path);
+
+	if (find_parent (path, &parent) != 0)
+		return -errno;
+
+	tmp = strdup (path);
+	name = basename (tmp);
+	len = strlen (name);
+
+	for (uint64_t i = 0; i < parent.blocks; ++i) {
+		struct sufs_dirent *ent, *prent;
+		uint64_t blkno, ino;
+		uint32_t prev;
+		
+		blkno = resolve_inode_block (&parent, i);
+		if (blkno == 0)
+			continue;
+		read_block (blkno, buffer);
+
+		for (uint32_t j = 0, prev = -1; j < sb.bsize; prev = j, j += ent->size) {
+			ent = (struct sufs_dirent *)(buffer + j);
+			ino = ent->ino;
+
+			if (ino == 0 || len != ent->len)
+				continue;
+
+			if (memcmp (name, ent->name, len) != 0)
+				continue;
+
+			if (prev != -1) {
+				prent = (struct sufs_dirent *)(buffer + prev);
+				prent->size += ent->size;
+			} else if (ent->size == sb.bsize) {
+				ent->ino = 0;
+			} else {
+				errx (1, "sufs_unlink(): TODO: removing the first dirent in a block");
+			}
+
+			write_block (blkno, buffer);
+			free_inode (ent->ino);
+			free (tmp);
+			return 0;
+		}
+
+	}
+
+	free (tmp);
+	return -ENOENT;
+}
+
+int sufs_utime (const char *path, struct utimbuf *t)
+{
+	struct sufs_inode in;
+	uint64_t ino;
+
+	ino = lookup (path);
+	if (ino == 0)
+		return -errno;
+
+	in = read_inode (ino);
+	in.atime = t->actime;
+	in.mtime = t->modtime;
+	write_inode (ino, &in);
+	return 0;
+}
+
+struct fuse_operations fsops = {
+	.getattr	= sufs_getattr,
+	.open		= sufs_open,
+	.read 		= sufs_read,
+	.write 		= sufs_write,
+	.readdir	= sufs_readdir,
+	.chown		= sufs_chown,
+	.chmod		= sufs_chmod,
+	.mknod		= sufs_mknod,
+	.create		= sufs_create,
+	.release	= sufs_release,
+	.unlink		= sufs_unlink,
+	.utime		= sufs_utime,
+};
+
+int opt_parse (void *data, const char *arg, int key, struct fuse_args *outargs)
+{
+	switch (key) {
+	case KEY_VERSION:
+		fputs ("fuse_sufs v0\n", stderr);
+		fuse_opt_add_arg (outargs, "--version");
+		fuse_main (outargs->argc, outargs->argv, &fsops, NULL);
+		exit (0);
+		break;
+	case KEY_HELP:
+		fputs ("usage: fuse_sufs file mountpoint\n", stderr);
+		fuse_opt_add_arg (outargs, "-h");
+		fuse_main (outargs->argc, outargs->argv, &fsops, NULL);
+		exit (1);
+		break;
+	}
+	return 1;
+}
+
+int main (int argc, char *argv[])
+{
+	struct fuse_args args = FUSE_ARGS_INIT (argc, argv);
+	fuse_opt_parse (&args, NULL, opts, opt_parse);
+
+	fd = open ("test.img", O_RDWR);
+
+	if (fd < 0)
+		err (1, "open()");
+
+	read (fd, &sb, sizeof (sb));
+
+	if (sb.magic != SUFS_MAGIC) {
+		lseek (fd, SUFS_BOOTSIZE, SEEK_SET);
+		read (fd, &sb, sizeof (sb));
+
+		if (sb.magic != SUFS_MAGIC)
+			errx (1, "invalid magic");
+	}
+
+	buffer = malloc (sb.bsize);
+	dbuffer = malloc (sb.bsize);
+
+	return fuse_main (argc, argv, &fsops, NULL);
+}
blob - /dev/null
blob + abadd6e7e1ec27bb07cc98f8f87a2eb13bc06ee9 (mode 644)
--- /dev/null
+++ newfs_sufs.c
@@ -0,0 +1,183 @@
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <err.h>
+#include "sufs.h"
+
+void xwrite (int fd, const char *buf, size_t num)
+{
+	if (write (fd, buf, num) < 0)
+		err (1, "write()");
+}
+
+void xlseek (int fd, off_t pos)
+{
+	if (lseek (fd, pos, SEEK_SET) < 0)
+		err (1, "lseek()");
+}
+
+int main (int argc, char *argv[])
+{
+	struct sufs_superblock sb;
+	struct sufs_inode in;
+	struct sufs_dirent *ent;
+	struct stat st;
+	uint64_t size, nib, nino, ndata;
+	uint32_t bs = -1, nibms, ndbms, offset = SUFS_BOOTSIZE;
+	int64_t now;
+	char *endp, *buf;
+	int fd, option;
+
+	while ((option = getopt (argc, argv, "b:n")) != -1) {
+		switch (option) {
+		case 'b':
+			bs = (uint32_t)strtoul (optarg, &endp, 10);
+			if (*endp != '\0')
+				errx (1, "invalid block size");
+			if (bs < 128)
+				errx (1, "block size less than 128 is not supported");
+			break;
+		case 'n':
+			offset = 0;
+			break;
+		default:
+			return 1;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc != 1) {
+		fputs ("Usage: newfs_sufs file\n", stderr);
+		return 1;
+	}
+
+	fd = open (argv[0], O_WRONLY);
+	if (fd < 0)
+		err (1, "open()");
+
+	if (fstat (fd, &st) < 0)
+		err (1, "stat()");
+
+	if (bs == -1)
+		bs = st.st_blksize;
+
+	// Skip bootblocks
+	xlseek (fd, offset);
+
+	// total number of blocks
+	size = st.st_size / bs;
+
+	size -= offset / bs;
+
+	// number of inode blocks
+	nib = size / 1024;
+
+	if ((nib * bs / sizeof (struct sufs_inode)) < 64)
+		nib = 64 * sizeof (struct sufs_inode) / bs;
+
+	if (nib == 0)
+		nib = 1;
+
+	// number of inodes
+	nino = nib * bs / sizeof (struct sufs_inode);
+
+	// number of inode bitmap blocks
+	nibms = (nino + 8 * bs - 1) / (8 * bs);
+
+	// number of data blocks
+	ndata = size - nib - nibms - (offset / bs) - 1;
+	if (ndata > size)
+		errx (1, "overflow");
+
+	// number of data bitmap blocks
+	ndbms = (ndata + 8 * bs - 1) / (8 * bs);
+
+	// adjust for number of data bitmap blocks
+	ndata -= ndbms;
+
+	memset (&sb, 0, sizeof (sb));
+	sb.magic = SUFS_MAGIC;
+	sb.bsize = bs;
+	sb.nino = nino;
+	sb.ndata = ndata;
+	sb.clean = 1;
+	sb.ibmoff = offset / bs + 1;
+	sb.dbmoff = sb.ibmoff + nibms;
+	sb.ioff = sb.dbmoff + ndbms;
+	sb.doff = sb.ioff + nib;
+	buf = calloc (1, bs);
+	memcpy (buf, &sb, sizeof (sb));
+	xwrite (fd, buf, bs);
+
+	printf ("size = %zu\n", (size_t)size);
+	printf ("blksize = %zu\n", (size_t)bs);
+	printf ("nib = %zu\n", (size_t)nib);
+	printf ("nino = %zu\n", (size_t)nino);
+	printf ("nibms = %zu\n", (size_t)nibms);
+	printf ("ndbms = %zu\n", (size_t)ndbms);
+	printf ("ndata = %zu\n", (size_t)ndata);
+	printf ("ibmoff = %zu\n", (size_t)sb.ibmoff);
+	printf ("dbmoff = %zu\n", (size_t)sb.dbmoff);
+	printf ("ioff = %zu\n", (size_t)sb.ioff);
+	printf ("doff = %zu\n", (size_t)sb.doff);
+
+	// clear inode bitmap
+	xlseek (fd, sb.ibmoff * bs);
+	memset (buf, 0xff, bs);
+	buf[0] = 0xfc;
+	xwrite (fd, buf, bs);
+	buf[0] = 0xff;
+	for (uint32_t i = 1; i < nibms; ++i)
+		xwrite (fd, buf, bs);
+
+	// clear data bitmap
+	xlseek (fd, sb.dbmoff * bs);
+	buf[0] = 0xfe;
+	xwrite (fd, buf, bs);
+	buf[0] = 0xff;
+	for (uint32_t i = 1; i < ndbms; ++i)
+		xwrite (fd, buf, bs);
+
+	// initialize root directory
+	memset (buf, 0, bs);
+	ent = (struct sufs_dirent *)buf;
+	ent->ino = SUFS_INO_ROOT;
+	ent->len = 1;
+	ent->size = 16;
+	ent->name[0] = '.';
+	ent = (struct sufs_dirent *)(buf + 16);
+	ent->ino = SUFS_INO_ROOT;
+	ent->len = 2;
+	ent->size = bs - 16;
+	ent->name[0] = '.';
+	ent->name[1] = '.';
+	xlseek (fd, sb.doff * bs);
+	xwrite (fd, buf, bs);
+
+	// initialize inode 0 & 1
+	now = (int64_t)time (NULL);
+	memset (&in, 0, sizeof (in));
+	in.mode = S_IFDIR | 0755;
+	in.spare = 0;
+	in.uid = 0;
+	in.gid = 0;
+	in.nlink = 3;
+	in.size = 0;
+	in.blocks = 1;
+	in.atime = now;
+	in.mtime = now;
+	in.ctime = now;
+	in.direct[0] = sb.doff;
+	memset (buf, 0, bs);
+	memcpy (buf + sizeof (in), &in, sizeof (in));
+	xlseek (fd, sb.ioff * bs);
+	xwrite (fd, buf, bs);
+
+	close (fd);
+	return 0;
+}
blob - /dev/null
blob + a08bad49283aae2546fdeb38b0995ff0485c73c3 (mode 644)
--- /dev/null
+++ sufs.h
@@ -0,0 +1,60 @@
+#ifndef FILE_SUFS_H
+#define FILE_SUFS_H
+#include <stdint.h>
+
+#define SUFS_MAGIC	0x01020304
+#define SUFS_BOOTSIZE	65536
+#define SUFS_NDIRECT	6
+#define SUFS_NINDIR	3
+#define SUFS_NBLOCKS	(SUFS_NDIRECT + SUFS_NINDIR)
+#define SUFS_INO_INVAL	0
+#define SUFS_INO_ROOT	1
+
+typedef uint64_t sufs_ino_t;
+
+struct sufs_superblock {
+	uint32_t magic;		// 0	magic number
+	uint32_t bsize;		// 4	block size
+	uint64_t nino;		// 8	number of inodes
+	uint64_t ndata;		// 16	number of data blocks
+	uint32_t ibmoff;	// 24	inode bitmap offset
+	uint32_t dbmoff;	// 28	data bitmap offset
+	uint32_t ioff;		// 32	inode blocks offset
+	uint32_t doff;		// 36	data blocks offset
+	uint8_t clean;		// 40	cleanly umounted?
+
+	char spare[512 - 2 * 8 -  6 * 4 - 1];
+};
+
+struct sufs_inode {
+	uint16_t mode;			// 0	type+perms
+	uint16_t spare;			// 2	spare
+	uint32_t uid;			// 4	user ID
+	uint32_t gid;			// 8	group ID
+	uint32_t nlink;			// 12	number of links
+	uint64_t size;			// 16	size
+	uint64_t blocks;		// 24	number of blocks
+	int64_t atime;			// 32	access time
+	int64_t mtime;			// 40	modify time
+	int64_t ctime;			// 48	change time
+	uint64_t direct[SUFS_NDIRECT];	// 56	direct blocks
+	uint64_t indir[SUFS_NINDIR];	// 104	indirect blocks 
+};
+
+struct sufs_dirent {
+	uint64_t ino;			// 0	inode number
+	uint16_t size;			// 8	size of entry
+	uint8_t len;			// 10	name length
+	char name[];			// 11	name
+} __attribute__((packed));
+
+#define STASSERT(x) _Static_assert(x, #x)
+
+STASSERT(sizeof(struct sufs_superblock) == 512);
+STASSERT(sizeof(struct sufs_inode) == 128);
+
+#define sufs_ipb(sb) ((sb).bsize / sizeof (struct sufs_inode))
+#define sufs_iblk(sb, ino) ((sb).ioff + ((ino) / sufs_ipb (sb)))
+#define sufs_ioff(sb, ino) (((ino) & (sufs_ipb(sb) - 1)) * sizeof (struct sufs_inode))
+
+#endif // FILE_SUFS_H