commit 4338ef2e71b1ccd38b98a8a41011a0e57d9b61c2 from: Benjamin Stürz date: Sat May 18 14:38:37 2024 UTC import 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 +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#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 + +#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