From 845dfc673d1c2f936fae827d30f6352538db2820 Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Sun, 22 Nov 2020 02:28:28 -0800 Subject: [PATCH] Initial commit. This is a FUSE driver I found somewhere for V6 UNIX filesystems. WIP. --- args.h | 24 ++ fs.h | 69 ++++++ unixfs.c | 473 +++++++++++++++++++++++++++++++++++ v6.c | 736 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1302 insertions(+) create mode 100644 args.h create mode 100644 fs.h create mode 100644 unixfs.c create mode 100644 v6.c diff --git a/args.h b/args.h new file mode 100644 index 0000000..0e666c1 --- /dev/null +++ b/args.h @@ -0,0 +1,24 @@ +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 + diff --git a/fs.h b/fs.h new file mode 100644 index 0000000..1fedcb2 --- /dev/null +++ b/fs.h @@ -0,0 +1,69 @@ +#define FUSE_USE_VERSION 31 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#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); diff --git a/unixfs.c b/unixfs.c new file mode 100644 index 0000000..67f07e5 --- /dev/null +++ b/unixfs.c @@ -0,0 +1,473 @@ +#include "fs.h" +#include +#include +#include +#include + +#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; +} diff --git a/v6.c b/v6.c new file mode 100644 index 0000000..69780b8 --- /dev/null +++ b/v6.c @@ -0,0 +1,736 @@ +#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; +} -- 2.20.1