commit - /dev/null
commit + 4338ef2e71b1ccd38b98a8a41011a0e57d9b61c2
blob - /dev/null
blob + 2c0ceeb5195f6a0010a8b62c59309876c7d44ab9 (mode 644)
--- /dev/null
+++ .gitignore
+mp
+newfs_sufs
+fuse_sufs
+dumpfs_sufs
+*.core
+*.swp
blob - /dev/null
blob + 9f4355b83655b045e4a56d4c98bdf76c463ead83 (mode 644)
--- /dev/null
+++ Makefile
+.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
+#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
+#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
+#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
+#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