--- /dev/null
+extern char *argv0;
+#define USED(x) ((void)x)
+#define SET(x) ((x)=0)
+
+#define ARGBEGIN for((argv0||(argv0=*argv)),argv++,argc--;\
+ argv[0] && argv[0][0]=='-' && argv[0][1];\
+ argc--, argv++) {\
+ char *_args, *_argt;\
+ char _argc;\
+ _args = &argv[0][1];\
+ if(_args[0]=='-' && _args[1]==0){\
+ argc--; argv++; break;\
+ }\
+ _argc = 0;\
+ while(*_args && (_argc = *_args++))\
+ switch(_argc)
+#define ARGEND SET(_argt);USED(_argt);USED(_argc);USED(_args);}USED(argv);USED(argc);
+#define ARGF() (_argt=_args, _args=(char*)"",\
+ (*_argt? _argt: argv[1]? (argc--, *++argv): 0))
+#define EARGF(x) (_argt=_args, _args=(char*)"",\
+ (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0)))
+
+#define ARGC() _argc
+
--- /dev/null
+#define FUSE_USE_VERSION 31
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <time.h>
+#include <sys/sysmacros.h>
+
+
+#define nil NULL
+typedef int8_t int8;
+typedef uint8_t uint8;
+typedef uint16_t uint16;
+typedef int16_t int16;
+typedef uint32_t uint32;
+typedef int32_t int32;
+typedef unsigned int uint;
+
+#define USED(x) ((void)x)
+
+/* the data containing our file system */
+extern uint8 *fsdata;
+extern int fslen;
+
+void panic(char *fmt, ...);
+void *emalloc(int size);
+FILE *mustopen(const char *name, const char *mode);
+
+typedef struct Dirbuf Dirbuf;
+struct Dirbuf
+{
+ char *p;
+ size_t size;
+};
+
+typedef struct DInode DInode;
+struct DInode;
+
+typedef struct Inode Inode;
+struct Inode
+{
+ int count;
+ int ino; /* not really needed */
+ DInode *i;
+};
+
+void fs_init(void);
+Inode *fs_iget(uint ino);
+void fs_iput(Inode *ip);
+int fs_open(uint ino, int flags);
+int fs_stat(uint ino, struct stat *stbuf);
+int fs_read(uint ino, void *vdst, int offset, int len);
+int fs_write(uint ino, void *vsrc, int offset, int len);
+struct dirent *fs_readdir(uint ino);
+int fs_mknod(uint ino, const char *name, mode_t mode, dev_t rdev, uint *newino);
+int fs_mkdir(uint ino, const char *name, mode_t mode);
+int fs_unlink(uint parent, const char *name);
+int fs_link(uint ino, uint parent, const char *name);
+int fs_atime(uint ino, int32 time);
+int fs_mtime(uint ino, int32 time);
+int fs_uid(uint ino, int32 uid);
+int fs_gid(uint ino, int32 gid);
+
+void dcheck(void);
--- /dev/null
+#include "fs.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <fuse_lowlevel.h>
+
+#include "args.h"
+
+uint8 *fsdata;
+int fslen;
+
+void
+panic(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(1);
+}
+
+void*
+emalloc(int size)
+{
+ void *p;
+ p = malloc(size);
+ if(p == nil)
+ panic("error: no memory");
+ return p;
+}
+
+FILE*
+mustopen(const char *name, const char *mode)
+{
+ FILE *f;
+ if(f = fopen(name, mode), f == nil)
+ panic("couldn't open file: %s", name);
+ return f;
+}
+
+
+typedef struct Options Options;
+struct Options
+{
+ int singlethread;
+ int foreground;
+ int debug;
+ int nodefault_subtype;
+ char *device;
+ char *mountpoint;
+ int show_version;
+ int show_help;
+ int clone_fd;
+ unsigned int max_idle_threads;
+};
+static Options options;
+
+#define offsetof(type, field) ((long)&((type*)0)->field)
+#define OPTION(t, p) \
+ { t, offsetof(Options, p), 1 }
+
+static const struct fuse_opt myopts[] = {
+ OPTION("-h", show_help),
+ OPTION("--help", show_help),
+ OPTION("-V", show_version),
+ OPTION("--version", show_version),
+ OPTION("-d", debug),
+ OPTION("debug", debug),
+ OPTION("-d", foreground),
+ OPTION("debug", foreground),
+ FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
+ OPTION("-f", foreground),
+ OPTION("-s", singlethread),
+ OPTION("clone_fd", clone_fd),
+ OPTION("max_idle_threads=%u", max_idle_threads),
+ FUSE_OPT_END
+};
+
+static int
+opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs)
+{
+ (void)data; (void)outargs;
+ switch(key){
+ case FUSE_OPT_KEY_NONOPT:
+ if(options.device == nil){
+ options.device = strdup(arg);
+ return 0;
+ }
+ if(options.mountpoint == nil){
+ options.mountpoint = strdup(arg);
+ return 0;
+ }
+ return -1;
+ default:
+ panic("invalid option");
+ }
+ return -1;
+}
+
+void
+usage(void)
+{
+ fprintf(stderr,
+ " usage: asdf [options] filesystem mountpoint\n"
+ " -h --help print help\n"
+ " -V --version print version\n"
+ " -d -o debug enable debug output (implies -f)\n"
+ " -f foreground operation\n"
+ " -s disable multi-threaded operation\n"
+ " -o clone_fd use separate fuse device fd for each thread\n"
+ " (may improve performance)\n"
+ " -o max_idle_threads the maximum number of idle worker threads\n"
+ " allowed (default: 10)\n");
+ exit(1);
+}
+
+
+
+
+
+void
+dirbuf_add(fuse_req_t req, Dirbuf *b, const char *name, fuse_ino_t ino)
+{
+ struct stat stbuf;
+ size_t oldsize = b->size;
+ b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
+ b->p = (char *) realloc(b->p, b->size);
+ memset(&stbuf, 0, sizeof(stbuf));
+ stbuf.st_ino = ino;
+ fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
+ b->size);
+}
+
+#define min(x, y) ((x) < (y) ? (x) : (y))
+
+int
+reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
+ off_t off, size_t maxsize)
+{
+ if(off < bufsize)
+ return fuse_reply_buf(req, buf + off,
+ min(bufsize - off, maxsize));
+ else
+ return fuse_reply_buf(req, NULL, 0);
+}
+
+static void
+vfs_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
+{
+ struct stat stbuf;
+ USED(fi);
+
+ memset(&stbuf, 0, sizeof(stbuf));
+ if(fs_stat(ino, &stbuf) == -1)
+ fuse_reply_err(req, ENOENT);
+ else
+ fuse_reply_attr(req, &stbuf, 1.0);
+}
+
+static void
+vfs_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi)
+{
+ int ret = 0;
+ USED(fi);
+
+ if(to_set & FUSE_SET_ATTR_ATIME)
+ ret = fs_atime(ino, attr->st_atime);
+ if(to_set & FUSE_SET_ATTR_MTIME)
+ ret = fs_mtime(ino, attr->st_mtime);
+ if(to_set & FUSE_SET_ATTR_GID)
+ ret = fs_gid(ino, attr->st_gid);
+ if(to_set & FUSE_SET_ATTR_UID)
+ ret = fs_uid(ino, attr->st_uid);
+
+ if(ret)
+ fuse_reply_err(req, ret);
+ else
+ vfs_getattr(req, ino, fi);
+}
+
+void
+vfs_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
+{
+ int e;
+
+ e = fs_open(ino, fi->flags);
+ if(e)
+ fuse_reply_err(req, e);
+ else
+ fuse_reply_open(req, fi);
+}
+
+void
+vfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
+ off_t off, struct fuse_file_info *fi)
+{
+ USED(fi);
+ char *buf;
+ int n;
+
+ buf = malloc(size);
+ n = fs_read(ino, buf, off, size);
+ reply_buf_limited(req, buf, n, off, size);
+ free(buf);
+}
+
+void
+vfs_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
+ size_t size, off_t off, struct fuse_file_info *fi)
+{
+ int n;
+ USED(fi);
+ n = fs_write(ino, (void*)buf, off, size);
+ fuse_reply_write(req, n);
+}
+
+void
+vfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
+ off_t off, struct fuse_file_info *fi)
+{
+ struct dirent *dirs, *dp;
+ Dirbuf b;
+
+ USED(fi);
+ dirs = fs_readdir(ino);
+ if(dirs == nil){
+ fuse_reply_err(req, ENOTDIR);
+ return;
+ }
+ memset(&b, 0, sizeof(b));
+ for(dp = dirs; dp->d_ino; dp++)
+ dirbuf_add(req, &b, dp->d_name, dp->d_ino);
+ free(dirs);
+ reply_buf_limited(req, b.p, b.size, off, size);
+ free(b.p);
+}
+
+/* returns static pointer! */
+struct dirent*
+lookup(uint ino, const char *name)
+{
+ struct dirent *dirs, *dp;
+ static struct dirent de;
+
+ dirs = fs_readdir(ino);
+ if(dirs == nil)
+ return nil;
+ for(dp = dirs; dp->d_ino; dp++)
+ if(strcmp(dp->d_name, name) == 0){
+ de = *dp;
+ free(dirs);
+ return &de;
+ }
+ free(dirs);
+ return nil;
+}
+
+void
+vfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
+{
+ struct fuse_entry_param e;
+ struct dirent *de;
+
+ de = lookup(parent, name);
+ if(de == nil){
+ fuse_reply_err(req, ENOENT);
+ return;
+ }
+ memset(&e, 0, sizeof(e));
+ e.ino = de->d_ino;
+ e.attr_timeout = 1.0;
+ e.entry_timeout = 1.0;
+ fs_stat(e.ino, &e.attr);
+
+ /* increment ref count */
+ fs_iget(e.ino);
+ fuse_reply_entry(req, &e);
+}
+
+void
+vfs_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
+{
+ Inode *ip;
+
+ printf("forgetting %d\n", nlookup);
+ ip = fs_iget(ino);
+ fs_iput(ip);
+ while(nlookup--)
+ fs_iput(ip);
+ fuse_reply_none(req);
+}
+
+void
+vfs_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev)
+{
+ int ret;
+
+ ret = fs_mknod(parent, name, mode, rdev, nil);
+ if(ret)
+ fuse_reply_err(req, ret);
+ else
+ vfs_lookup(req, parent, name);
+}
+
+void
+vfs_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
+{
+ int ret;
+
+ ret = fs_mkdir(parent, name, mode);
+ if(ret)
+ fuse_reply_err(req, ret);
+ else
+ vfs_lookup(req, parent, name);
+}
+
+void
+vfs_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
+{
+ int ret;
+
+ ret = fs_unlink(parent, name);
+ fuse_reply_err(req, ret);
+}
+
+int
+isdirempty(struct dirent *de)
+{
+ for(; de->d_ino; de++)
+ if(strcmp(de->d_name, ".") != 0 &&
+ strcmp(de->d_name, "..") != 0)
+ return 0;
+ return 1;
+}
+
+void
+vfs_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
+{
+ struct dirent *dir, *de;
+ int ret;
+ int dino;
+
+ ret = 0;
+ de = lookup(parent, name);
+ /* this shouldn't happen */
+ if(de == nil)
+ return;
+ dino = de->d_ino;
+ dir = fs_readdir(dino);
+ assert(dir);
+ if(!isdirempty(dir)){
+ free(dir);
+ fuse_reply_err(req, ENOTEMPTY);
+ return;
+ }
+printf("dir to delete: %d %s\n", dino, name);
+ for(de = dir; de->d_ino; de++)
+ fs_unlink(dino, de->d_name);
+ free(dir);
+ fs_unlink(parent, name);
+
+ fuse_reply_err(req, ret);
+}
+
+void
+vfs_link(fuse_req_t req, fuse_ino_t ino,
+ fuse_ino_t newparent, const char *newname)
+{
+ int ret;
+
+ ret = fs_link(ino, newparent, newname);
+ if(ret)
+ fuse_reply_err(req, ret);
+ else
+ vfs_lookup(req, newparent, newname);
+}
+
+static struct fuse_lowlevel_ops hello_ll_oper = {
+ .lookup = vfs_lookup,
+ .getattr = vfs_getattr,
+ .setattr = vfs_setattr,
+ .readdir = vfs_readdir,
+ .open = vfs_open,
+ .read = vfs_read,
+ .write = vfs_write,
+ .mknod = vfs_mknod,
+ .mkdir = vfs_mkdir,
+ .unlink = vfs_unlink,
+ .rmdir = vfs_rmdir,
+ .link = vfs_link,
+ .forget = vfs_forget,
+ // TODO
+ // rename
+};
+
+
+void
+fsload(const char *filename)
+{
+ FILE *f = mustopen(filename, "rb");
+ fseek(f, 0, SEEK_END);
+ fslen = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ fsdata = emalloc(fslen);
+ fread(fsdata, 1, fslen, f);
+ fclose(f);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+ struct fuse_session *se;
+ char *m;
+ int ret = -1;
+
+ if(fuse_opt_parse(&args, &options, myopts, opt_proc) == -1 ||
+ options.device == nil || options.mountpoint == nil)
+ usage();
+
+ m = options.mountpoint;
+ options.mountpoint = realpath(m, nil);
+ if(options.mountpoint == nil)
+ panic("bad mount point %s", m);
+
+ printf("device: %s\n", options.device);
+ printf("mountpoint: %s\n", options.mountpoint);
+
+ if(options.show_help){
+ usage();
+ return 0;
+ }
+ if(options.show_version){
+ printf("FUSE library version %s\n", fuse_pkgversion());
+ fuse_lowlevel_version();
+ return 0;
+ }
+
+ fsload(options.device);
+ fs_init();
+ dcheck();
+
+ se = fuse_session_new(&args, &hello_ll_oper,
+ sizeof(hello_ll_oper), NULL);
+ if(se == NULL)
+ return 1;
+
+ if(fuse_set_signal_handlers(se) != 0)
+ goto err_out2;
+
+ if(fuse_session_mount(se, options.mountpoint) != 0)
+ goto err_out3;
+
+ fuse_daemonize(options.foreground);
+
+ /* Block until ctrl+c or fusermount -u */
+ if(options.singlethread)
+ ret = fuse_session_loop(se);
+ else
+ ret = fuse_session_loop_mt(se, options.clone_fd);
+
+ fuse_session_unmount(se);
+err_out3:
+ fuse_remove_signal_handlers(se);
+err_out2:
+ fuse_session_destroy(se);
+ free(options.mountpoint);
+ fuse_opt_free_args(&args);
+
+ return ret ? 1 : 0;
+}
--- /dev/null
+#include "fs.h"
+
+/* NB: not portable! assumes little endian (and probably other things) */
+
+#define NIPB (512/sizeof(DInode))
+#define NDPB (512/sizeof(Dirent))
+
+/* v6 superblock */
+typedef struct Filsys Filsys;
+struct Filsys
+{
+ int16 s_isize; /* size in blocks of I list */
+ int16 s_fsize; /* size in blocks of entire volume */
+ int16 s_nfree; /* number of in core free blocks (0-100) */
+ int16 s_free[100]; /* in core free blocks */
+ int16 s_ninode; /* number of in core I nodes (0-100) */
+ int16 s_inode[100]; /* in core free I nodes */
+ uint8 s_flock; /* lock during free list manipulation */
+ uint8 s_ilock; /* lock during I list manipulation */
+ uint8 s_fmod; /* super block modified flag */
+ uint8 s_ronly; /* mounted read-only flag */
+ int16 s_time[2]; /* current date of last update */
+ int16 pad[48];
+};
+
+/* v6 and PWB/1.0 disk inode */
+struct DInode
+{
+ uint16 i_mode;
+ int8 i_nlink;
+ uint8 i_uid;
+ uint8 i_gid;
+ uint8 i_size0;
+ uint16 i_size1;
+ uint16 i_addr[8];
+ uint16 i_atime[2];
+ uint16 i_mtime[2];
+};
+
+/* modes */
+#define IALLOC 0100000
+#define IFMT 060000
+#define IFDIR 040000
+#define IFCHR 020000
+#define IFBLK 060000
+#define ILARG 010000
+#define ISUID 04000
+#define ISGID 02000
+#define ISVTX 01000
+#define IREAD 0400
+#define IWRITE 0200
+#define IEXEC 0100
+
+typedef struct Dirent Dirent;
+struct Dirent
+{
+ uint16 inode;
+ char name[14];
+};
+
+Filsys *filesys;
+DInode *dinodes;
+Inode *inodes;
+
+#define ISIZE(ip) ((ip)->i_size0<<16 | (ip)->i_size1)
+int32
+pdplong(void *p)
+{
+ uint8 *up;
+ uint32 ui;
+ up = p;
+ ui = up[1]<<24 | up[0]<<16 | up[3]<<8 | up[2];
+ return *(int32*)&ui;
+}
+
+void
+setpdplong(void *p, int32 i)
+{
+ uint8 *up;
+ uint32 ui;
+ up = p;
+ ui = *(uint32*)&i;
+ up[1] = ui>>24;
+ up[0] = ui>>16;
+ up[3] = ui>>8;
+ up[2] = ui;
+}
+
+void
+setint24(void *p, int32 i)
+{
+ uint8 *up;
+ uint32 ui;
+ up = p;
+ ui = *(uint32*)&i;
+ up[0] = ui>>16;
+ up[2] = ui>>8;
+ up[1] = ui;
+}
+
+void *bget(int n) { return &fsdata[n*512]; }
+
+/* returns a free block or 0 */
+int
+balloc(void)
+{
+ int i, bno;
+ uint16 *b;
+ filesys->s_nfree--;
+ if(filesys->s_nfree < 0 || filesys->s_nfree >= 100){
+ printf("bad free count\n");
+ return 0;
+ }
+ bno = filesys->s_free[filesys->s_nfree];
+ filesys->s_free[filesys->s_nfree] = 0;
+ if(bno == 0)
+ return 0;
+ if(bno < filesys->s_isize+2 || bno >= filesys->s_fsize){
+ printf("bad free block (%d)\n", bno);
+ return 0;
+ }
+ if(filesys->s_nfree <= 0){
+ b = bget(bno);
+ filesys->s_nfree = b[0];
+ for(i = 0; i < 100; i++)
+ filesys->s_free[i] = b[i+1];
+ }
+ return bno;
+}
+
+void
+bfree(int bno)
+{
+ int i;
+ uint16 *b;
+
+printf("freeing block\n");
+ if(filesys->s_nfree >= 100){
+ b = bget(bno);
+ b[0] = filesys->s_nfree;
+ for(i = 0; i < 100; i++)
+ b[i+1] = filesys->s_free[i];
+ filesys->s_nfree = 0;
+ }
+ filesys->s_free[filesys->s_nfree] = bno;
+ filesys->s_nfree++;
+}
+
+int
+zalloc(void)
+{
+ int bn;
+ bn = balloc();
+ if(bn)
+ memset(bget(bn), 0, 512);
+ return bn;
+}
+
+/* returns a free inode or 0 */
+int
+ialloc(void)
+{
+ int ino;
+ DInode *ip;
+ int i, j;
+
+ if(filesys->s_ninode <= 0){
+ ino = 0;
+ for(i = 0; i < filesys->s_isize; i++)
+ for(j = 0; j < NIPB; j++){
+ ino++;
+ ip = &dinodes[ino];
+ if(ip->i_mode)
+ continue;
+ filesys->s_inode[filesys->s_ninode++] = ino;
+ if(filesys->s_ninode >= 100)
+ goto brk;
+ }
+ }
+brk:
+ if(filesys->s_ninode > 0){
+ ino = filesys->s_inode[--filesys->s_ninode];
+ return ino;
+ }
+ return 0;
+}
+
+void
+ifree(int ino)
+{
+printf("freeing inode\n");
+ if(filesys->s_ninode >= 100)
+ return;
+ filesys->s_inode[filesys->s_ninode++] = ino;
+}
+
+void
+itrunc(DInode *ip)
+{
+ int i;
+ uint16 *bp, *cp, *dp, *ep;
+
+ if(ip->i_mode & (IFCHR|IFBLK))
+ return;
+ for(i = 7; i >= 0; i--)
+ if(ip->i_addr[i]){
+ if(ip->i_mode & ILARG){
+ bp = bget(ip->i_addr[i]);
+ for(cp = &bp[255]; cp >= bp; cp--)
+ if(*cp){
+ if(i == 7){
+ dp = bget(*cp);
+ for(ep = &dp[255]; ep >= dp; ep--)
+ if(*ep)
+ bfree(*ep);
+ }
+ bfree(*cp);
+ }
+ }
+ bfree(ip->i_addr[i]);
+ ip->i_addr[i] = 0;
+ }
+ ip->i_mode &= ~ILARG;
+ setint24(&ip->i_size0, 0);
+}
+
+void
+dcheck(void)
+{
+ Filsys fs;
+ int i;
+
+ fs = *filesys;
+ i = 0;
+ while(balloc())
+ i++;
+ printf("free: %d\n", i);
+ *filesys = fs;
+}
+
+int
+bmap(DInode *ip, uint bn)
+{
+ uint i;
+ uint nb;
+ uint16 *b;
+
+ if(bn & ~077777)
+ return 0;
+
+ if((ip->i_mode & ILARG) == 0){
+
+ /* small file, direct fetch */
+
+ if(bn & ~7){
+ /* convert to large */
+ nb = zalloc();
+ if(nb == 0)
+ return 0;
+ b = bget(nb);
+ for(i = 0; i < 8; i++){
+ b[i] = ip->i_addr[i];
+ ip->i_addr[i] = 0;
+ }
+ ip->i_addr[0] = nb;
+ ip->i_mode |= ILARG;
+ goto large;
+ }
+
+ nb = ip->i_addr[bn];
+ if(nb == 0){
+ nb = zalloc();
+ if(nb == 0)
+ return 0;
+ ip->i_addr[bn] = nb;
+ }
+ return nb;
+ }
+
+ /* large file, 7 indirect blocks */
+
+large:
+ i = bn>>8;
+ if(i > 7)
+ i = 7;
+ nb = ip->i_addr[i];
+ if(nb == 0){
+ nb = zalloc();
+ if(nb == 0)
+ return 0;
+ ip->i_addr[bn] = nb;
+ }
+ b = bget(nb);
+
+ if(i == 7){
+
+ /* huge file, double indirect last block */
+
+ i = (bn>>8) - 7;
+ nb = b[i&0377];
+ if(nb == 0){
+ nb = zalloc();
+ if(nb == 0)
+ return 0;
+ b[i&0377] = nb;
+ }
+ b = bget(nb);
+ }
+
+ nb = b[bn&0377];
+ return nb;
+};
+
+uint8*
+getblock(DInode *ip, uint bn)
+{
+ bn = bmap(ip, bn);
+ if(bn == 0)
+ return nil;
+ return bget(bn);
+}
+
+void
+fs_init(void)
+{
+ int i, ni;
+
+ filesys = bget(1);
+ dinodes = (DInode*)bget(2) - 1;
+ ni = filesys->s_isize*NIPB + 1;
+ inodes = malloc(ni*sizeof(Inode));
+ for(i = 0; i < ni; i++){
+ inodes[i].ino = i;
+ inodes[i].count = 0;
+ inodes[i].i = &dinodes[i];
+ }
+}
+
+int
+fs_open(uint ino, int flags)
+{
+ DInode *ip;
+
+ ip = &dinodes[ino];
+ if((ip->i_mode & IFMT) == IFDIR)
+ return EISDIR;
+// if((flags & 3) != O_RDONLY)
+// return EACCES;
+ return 0;
+}
+
+int
+fs_stat(uint ino, struct stat *stbuf)
+{
+ DInode *ip;
+ ip = &dinodes[ino];
+
+ stbuf->st_ino = ino;
+ if((ip->i_mode & IFMT) == IFDIR)
+ stbuf->st_mode = S_IFDIR;
+ else if((ip->i_mode & IFMT) == IFCHR)
+ stbuf->st_mode = S_IFCHR;
+ else if((ip->i_mode & IFMT) == IFBLK)
+ stbuf->st_mode = S_IFBLK;
+ else
+ stbuf->st_mode = S_IFREG;
+ stbuf->st_mode |= ip->i_mode & 0777;
+ stbuf->st_nlink = ip->i_nlink;
+ stbuf->st_size = ISIZE(ip);
+ if((ip->i_mode & IFMT) == IFCHR ||
+ (ip->i_mode & IFMT) == IFBLK)
+ stbuf->st_rdev = makedev(ip->i_addr[0]>>8 & 0xFF, ip->i_addr[0]&0xFF);
+ stbuf->st_uid = ip->i_uid;
+ stbuf->st_gid = ip->i_gid;
+ stbuf->st_atime = pdplong(ip->i_atime);
+ stbuf->st_mtime = pdplong(ip->i_mtime);
+
+ return 0;
+}
+
+int
+fs_atime(uint ino, int32 time)
+{
+ DInode *ip;
+ ip = &dinodes[ino];
+ setpdplong(ip->i_atime, time);
+ return 0;
+}
+
+int
+fs_mtime(uint ino, int32 time)
+{
+ DInode *ip;
+ ip = &dinodes[ino];
+ setpdplong(ip->i_mtime, time);
+ return 0;
+}
+
+int
+fs_uid(uint ino, int32 uid)
+{
+ DInode *ip;
+ ip = &dinodes[ino];
+ ip->i_uid = uid;
+ return 0;
+}
+
+int
+fs_gid(uint ino, int32 gid)
+{
+ DInode *ip;
+ ip = &dinodes[ino];
+ ip->i_gid = gid;
+ return 0;
+}
+
+int
+fs_read(uint ino, void *vdst, int offset, int len)
+{
+ DInode *ip;
+ int bn;
+ uint8 *b;
+ int isize;
+ int n;
+ uint8 *dst;
+
+ ip = &dinodes[ino];
+ dst = vdst;
+ isize = ISIZE(ip);
+ if(offset + len > isize)
+ len = isize - offset;
+ if(len <= 0)
+ return 0;
+
+ bn = offset/512;
+ offset %= 512;
+
+ b = getblock(ip, bn);
+ if(b == nil)
+ /* TODO: handle gracefully */
+ panic("no block");
+
+ if(len < 512-offset){
+ /* smaller than block */
+ memcpy(dst, b+offset, len);
+ return len;
+ }
+
+ /* at least one block */
+ memcpy(dst, b+offset, 512-offset);
+ n = 512-offset;
+ len -= n;
+ dst += n;
+ while(len > 0){
+ bn++;
+ b = getblock(ip, bn);
+ if(b == nil)
+ /* TODO: handle gracefully */
+ panic("no block");
+ memcpy(dst, b, len > 512 ? 512 : len);
+ n += len > 512 ? 512 : len;
+ len -= 512;
+ dst += 512;
+ }
+ return n;
+}
+
+int
+fs_write(uint ino, void *vsrc, int offset, int len)
+{
+ DInode *ip;
+ int bn;
+ uint8 *b;
+ int n;
+ int32 size;
+ uint8 *src;
+
+ ip = &dinodes[ino];
+ src = vsrc;
+ size = ISIZE(ip);
+ // TODO: make this better:
+ if(offset+len > size)
+ setint24(&ip->i_size0, offset+len);
+ if(len <= 0)
+ return 0;
+
+ bn = offset/512;
+ offset %= 512;
+
+ b = getblock(ip, bn);
+ if(b == nil)
+ /* TODO: handle gracefully */
+ panic("no block");
+
+ if(len < 512-offset){
+ /* smaller than block */
+ memcpy(b+offset, src, len);
+ return len;
+ }
+
+ /* at least one block */
+ memcpy(b+offset, src, 512-offset);
+ n = 512-offset;
+ len -= n;
+ src += n;
+ while(len > 0){
+ bn++;
+ b = getblock(ip, bn);
+ if(b == nil)
+ /* TODO: handle gracefully */
+ panic("no block");
+ memcpy(b, src, len > 512 ? 512 : len);
+ n += len > 512 ? 512 : len;
+ len -= 512;
+ src += 512;
+ }
+ return n;
+}
+
+/* return a malloc'd array of dirents for a given inode */
+struct dirent*
+fs_readdir(uint ino)
+{
+ DInode *ip;
+ struct dirent *des, *dp;
+ int size;
+ int ndes;
+ Dirent de;
+ int offset;
+
+ ip = &dinodes[ino];
+ if((ip->i_mode & IFDIR) == 0)
+ return nil;
+
+ size = ISIZE(ip);
+ ndes = size/sizeof(Dirent);
+ des = malloc((ndes+1)*sizeof(struct dirent));
+
+ offset = 0;
+ dp = des;
+ while(fs_read(ino, &de, offset, sizeof(Dirent)) == sizeof(Dirent)){
+ if(de.inode){
+ dp->d_ino = de.inode;
+ memcpy(dp->d_name, de.name, 14);
+ dp->d_name[14] = '\0';
+ dp++;
+ }
+ offset += sizeof(Dirent);
+ }
+ dp->d_ino = 0;
+ dp->d_name[0] = '\0';
+
+ return des;
+}
+
+static int
+lookup(uint ino, const char *name, Dirent *de)
+{
+ int offset;
+
+ assert(de);
+ offset = 0;
+ while(fs_read(ino, de, offset, sizeof(Dirent)) == sizeof(Dirent)){
+ if(de->inode)
+ if(strcmp(de->name, name) == 0)
+ return offset;
+ offset += sizeof(Dirent);
+ }
+ memset(de, 0, sizeof(Dirent));
+ return -1;
+}
+
+static int
+allocdirent(uint ino)
+{
+ Dirent de;
+ int offset;
+
+ offset = 0;
+ while(fs_read(ino, &de, offset, sizeof(Dirent)) == sizeof(Dirent)){
+ if(de.inode == 0)
+ return offset;
+ offset += sizeof(Dirent);
+ }
+ memset(&de, 0, sizeof(Dirent));
+ fs_write(ino, &de, offset, sizeof(Dirent));
+ return offset;
+}
+
+int
+fs_link(uint ino, uint parent, const char *name)
+{
+ Dirent de;
+ int offset;
+
+ offset = allocdirent(parent);
+ /* TODO: can't happen right now */
+ if(offset < 0)
+ return ENOSPC;
+ memset(&de, 0, sizeof(Dirent));
+ de.inode = ino;
+ strncpy(de.name, name, 14);
+ fs_write(parent, &de, offset, sizeof(de));
+ fs_mtime(ino, time(nil));
+ fs_atime(ino, time(nil));
+ dinodes[ino].i_nlink++;
+
+ return 0;
+}
+
+int
+fs_mknod(uint parent, const char *name, mode_t mode, dev_t rdev, uint *newino)
+{
+ uint ino;
+ DInode *ip, *ipp;
+ int offset;
+ Dirent de;
+
+ USED(rdev);
+ // TODO: support more files
+ if((mode & S_IFMT) != S_IFREG &&
+ (mode & S_IFMT) != S_IFDIR)
+ return EACCES;
+
+ ino = ialloc();
+ if(ino == 0)
+ return ENOSPC;
+ ip = &dinodes[ino];
+ memset(ip, 0, sizeof(DInode));
+ ip->i_mode = (mode&0777) | IALLOC;
+ if((mode & S_IFMT) == S_IFDIR)
+ ip->i_mode |= IFDIR;
+ else if((mode & S_IFMT) == S_IFCHR)
+ ip->i_mode |= IFCHR;
+ else if((mode & S_IFMT) == S_IFBLK)
+ ip->i_mode |= IFBLK;
+ if((ip->i_mode & IFMT) == IFCHR ||
+ (ip->i_mode & IFMT) == IFBLK)
+ ip->i_addr[0] =
+ major(rdev)<<8 & 0xFF00 | minor(rdev) & 0xFF;
+
+ ipp = &dinodes[parent];
+ assert((ipp->i_mode & IFMT) == IFDIR);
+
+ offset = allocdirent(parent);
+ /* TODO: can't happen right now */
+ if(offset < 0){
+ ip->i_mode = 0;
+ return ENOSPC;
+ }
+
+ ip->i_nlink = 1;
+ memset(&de, 0, sizeof(Dirent));
+ de.inode = ino;
+ strncpy(de.name, name, 14);
+ fs_write(parent, &de, offset, sizeof(de));
+ fs_mtime(ino, time(nil));
+ fs_atime(ino, time(nil));
+
+ if(newino)
+ *newino = ino;
+ printf("making node %s %o\n", name, mode);
+ return 0;
+}
+
+int
+fs_mkdir(uint parent, const char *name, mode_t mode)
+{
+ int bn;
+ int ino;
+ int ret;
+ DInode *ip;
+ Dirent des[2];
+
+ /* Make sure we have space */
+ bn = balloc();
+ if(bn == 0)
+ return ENOSPC;
+ ret = fs_mknod(parent, name, mode | S_IFDIR, 0, &ino);
+ if(ret){
+ bfree(bn);
+ return ret;
+ }
+ ip = &dinodes[ino];
+ ip->i_addr[0] = bn;
+
+ memset(des, 0, 2*sizeof(Dirent));
+ des[0].inode = ino;
+ strcpy(des[0].name, ".");
+ ip->i_nlink++;
+ des[1].inode = parent;
+ strcpy(des[1].name, "..");
+ dinodes[parent].i_nlink++;
+ fs_write(ino, des, 0, 2*sizeof(Dirent));
+ return 0;
+}
+
+Inode*
+fs_iget(uint ino)
+{
+ Inode *ip;
+ ip = &inodes[ino];
+ ip->count++;
+ return ip;
+}
+
+void
+fs_iput(Inode *ip)
+{
+ assert(ip->count > 0);
+ ip->count--;
+ if(ip->count == 0)
+ if(ip->i->i_nlink <= 0){
+ printf("truncating inode %d (%d)\n", ip->ino, ip->i->i_nlink);
+ itrunc(ip->i);
+ ip->i->i_mode = 0;
+ ifree(ip->ino);
+ }
+}
+
+int
+fs_unlink(uint parent, const char *name)
+{
+ Dirent de;
+ int offset;
+
+ offset = lookup(parent, name, &de);
+ if(offset < 0)
+ /* shouldn't happen */
+ return 1;
+ dinodes[de.inode].i_nlink--;
+
+ de.inode = 0;
+ fs_write(parent, &de, offset, sizeof(Dirent));
+ return 0;
+}