ref: 12a0ed55204affb277def2a73c1769bd76105ed4
dir: /ext4srv.c/
#include <ext4.h> #include <ext4_inode.h> #include <fcall.h> #include <thread.h> #include <9p.h> #include "group.h" #include "common.h" int mainstacksize = 65536; typedef struct Aux Aux; struct Aux { Part *p; u32int uid; char *path; union { ext4_file *file; ext4_dir *dir; }; int type; }; enum { Adir, Afile, Root = 0, }; static Opts opts = { .cachewb = 0, }; static char * linkresolve(Aux *a, char *s) { char *q; char buf[4096+1]; ulong sz; if(ext4_readlink(s, buf, sizeof(buf)-1, &sz) == 0){ buf[sz] = 0; cleanname(buf); if(buf[0] == '/'){ free(s); s = smprint("%M%s", a->p, buf); }else{ q = strrchr(s, '/'); *q = 0; q = s; s = smprint("%s/%s", q, buf); cleanname(s); free(q); } } return s; } static char * fullpath(Aux *a) { return linkresolve(a, smprint("%M/%s", a->p, a->path)); } static int haveperm(Aux *a, int p) { struct ext4_inode inode; u32int ino; Group *g; char *s; int m, fm, r; switch(p & 3){ case OREAD: p = AREAD; break; case OWRITE: p = AWRITE; break; case ORDWR: p = AREAD|AWRITE; break; case OEXEC: p = AEXEC; break; default: return 0; } s = fullpath(a); if((r = ext4_raw_inode_fill(s, &ino, &inode)) != 0){ fprint(2, "inode: %s: %s\n", s, errno2s(r)); return 0; } free(s); fm = ext4_inode_get_mode(a->p->sb, &inode); /* other */ m = fm & 7; if((p & m) == p) return 1; /* owner */ if(a->uid == Root || ((g = findgroupid(&a->p->groups, ext4_inode_get_uid(&inode))) != nil && g->id == a->uid)){ m |= (fm >> 6) & 7; if((p & m) == p) return 1; } /* group */ if(a->uid == Root || ((g = findgroupid(&a->p->groups, ext4_inode_get_gid(&inode))) != nil && g->id == a->uid)){ m |= (fm >> 3) & 7; if((p & m) == p) return 1; } return 0; } static void rattach(Req *r) { Aux *a; static char err[ERRMAX]; if((a = calloc(1, sizeof(*a))) == nil) respond(r, "memory"); else if((a->p = openpart(r->ifcall.aname, &opts)) == nil){ free(a); rerrstr(err, sizeof(err)); respond(r, err); }else{ if(findgroup(&a->p->groups, r->ifcall.uname, &a->uid) == nil) a->uid = Root; /* FIXME need external mapping */ incref(a->p); a->type = Adir; a->path = strdup(""); r->ofcall.qid = a->p->qidmask; r->fid->qid = a->p->qidmask; r->fid->aux = a; respond(r, nil); } } static u32int toext4mode(u32int mode, u32int perm, int creat) { u32int e; e = 0; mode &= ~OCEXEC; if(mode & OTRUNC){ mode &= ~OTRUNC; e |= O_TRUNC; } if(mode == OWRITE) e |= O_WRONLY; else if(mode == ORDWR) e |= O_RDWR; if(creat) e |= O_CREAT; if(perm & DMEXCL) e |= O_EXCL; if(perm & DMAPPEND) e |= O_APPEND; return e; } static void ropen(Req *r) { Aux *a; char *path; int res; a = r->fid->aux; switch(a->type){ case Adir: if(r->ifcall.mode != OREAD || !haveperm(a, r->ifcall.mode)){ respond(r, "permission denied"); return; } if(a->dir != nil){ respond(r, "double open"); return; } if((a->dir = malloc(sizeof(*a->dir))) == nil) goto Nomem; if((path = smprint("%M/%s", a->p, a->path)) == nil){ free(a->dir); a->dir = nil; goto Nomem; } res = ext4_dir_open(a->dir, path); free(path); if(res != 0){ free(a->dir); a->dir = nil; respond(r, errno2s(res)); return; } break; case Afile: if(!haveperm(a, r->ifcall.mode)){ respond(r, "permission denied"); return; } if(a->file != nil){ respond(r, "double open"); return; } if((a->file = malloc(sizeof(*a->file))) == nil) goto Nomem; if((path = smprint("%M/%s", a->p, a->path)) == nil){ free(a->file); a->file = nil; goto Nomem; } res = ext4_fopen2(a->file, path, toext4mode(r->ifcall.mode, 0, 0)); free(path); if(res != 0){ free(a->file); a->file = nil; respond(r, errno2s(res)); return; } break; Nomem: respond(r, "memory"); return; } respond(r, nil); } static void rcreate(Req *r) { respond(r, "nope"); } static int dirfill(Dir *dir, Aux *a, char *path) { struct ext4_inode inode; u32int t, ino; char *s, *q; Group *g; int r; memset(dir, 0, sizeof(*dir)); if(path == nil){ path = a->path; s = smprint("%M/%s", a->p, a->path); }else{ if(*a->path == 0 && *path == 0) path = "/"; s = smprint("%M%s%s/%s", a->p, *a->path ? "/" : "", a->path, path); } s = linkresolve(a, s); if((r = ext4_raw_inode_fill(s, &ino, &inode)) != 0) fprint(2, "inode: %s: %s\n", s, errno2s(r)); dir->mode = ext4_inode_get_mode(a->p->sb, &inode); dir->qid.path = a->p->qidmask.path | ino; dir->qid.vers = ext4_inode_get_generation(&inode); t = ext4_inode_type(a->p->sb, &inode); if(t & EXT4_INODE_MODE_DIRECTORY){ dir->qid.type |= QTDIR; dir->mode |= DMDIR; }else dir->length = ext4_inode_get_size(a->p->sb, &inode); if(ext4_inode_get_flags(&inode) & EXT4_INODE_FLAG_APPEND){ dir->qid.type |= QTAPPEND; dir->mode |= DMAPPEND; } if((q = strrchr(path, '/')) != nil) path = q+1; dir->name = estrdup9p(path); dir->atime = ext4_inode_get_access_time(&inode); dir->mtime = ext4_inode_get_modif_time(&inode); if((g = findgroupid(&a->p->groups, ext4_inode_get_uid(&inode))) != nil) dir->uid = estrdup9p(g->name); if((g = findgroupid(&a->p->groups, ext4_inode_get_gid(&inode))) != nil) dir->gid = estrdup9p(g->name); free(s); return 0; } static int dirgen(int n, Dir *dir, void *aux) { const ext4_direntry *e; Aux *a; a = aux; if(n == 0) ext4_dir_entry_rewind(a->dir); do{ if((e = ext4_dir_entry_next(a->dir)) == nil) return -1; }while(strcmp((char*)e->name, ".") == 0 || strcmp((char*)e->name, "..") == 0); return dirfill(dir, a, (char*)e->name); } static void rread(Req *r) { Aux *a; ulong n; a = r->fid->aux; if(a->type == Adir && a->dir != nil){ dirread9p(r, dirgen, a); respond(r, nil); return; }else if(a->type == Afile && a->file != nil){ ext4_fseek(a->file, r->ifcall.offset, 0); if(ext4_fread(a->file, r->ofcall.data, r->ifcall.count, &n) != 0){ respond(r, "i/o error"); }else{ r->ofcall.count = n; respond(r, nil); } return; } respond(r, "eh?"); } static void rwrite(Req *r) { Aux *a; ulong n; int res; a = r->fid->aux; if(a->type == Adir){ respond(r, "can't write to dir"); return; }else if(a->type == Afile){ ext4_fseek(a->file, r->ifcall.offset, SEEK_SET); if((res = ext4_fwrite(a->file, r->ifcall.data, r->ifcall.count, &n)) != 0) respond(r, errno2s(res)); else{ r->ofcall.count = n; respond(r, nil); } return; } respond(r, "eh?"); } static void rremove(Req *r) { respond(r, "nope"); } static void rstat(Req *r) { Aux *a; a = r->fid->aux; dirfill(&r->d, a, nil); respond(r, nil); } static void rwstat(Req *r) { respond(r, "nope"); } static char * rwalk1(Fid *fid, char *name, Qid *qid) { Aux *a; char *s, *q; u32int ino, t; struct ext4_inode inode; int r; a = fid->aux; s = smprint("%M/%s", a->p, a->path); s = linkresolve(a, s); q = s; s = smprint("%s/%s", q, name); cleanname(s); free(q); s = linkresolve(a, s); if((r = ext4_raw_inode_fill(s, &ino, &inode)) != 0) goto error; qid->type = 0; qid->path = a->p->qidmask.path | ino; qid->vers = ext4_inode_get_generation(&inode); t = ext4_inode_type(a->p->sb, &inode); if(t & EXT4_INODE_MODE_DIRECTORY){ qid->type |= QTDIR; a->type = Adir; }else a->type = Afile; if(ext4_inode_get_flags(&inode) & EXT4_INODE_FLAG_APPEND) qid->type |= QTAPPEND; free(a->path); a->path = strdup(strchr(s+1, '/')+1); free(s); fid->qid = *qid; return nil; error: free(s); return errno2s(r); } static char * rclone(Fid *oldfid, Fid *newfid) { Aux *a, *c; a = oldfid->aux; switch(a->type){ case Afile: case Adir: if((c = calloc(1, sizeof(*c))) == nil) return "memory"; memmove(c, a, sizeof(*c)); c->path = strdup(a->path); c->file = nil; c->dir = nil; break; default: return "unknown aux type"; } incref(c->p); newfid->aux = c; return nil; } static void rdestroyfid(Fid *fid) { Aux *a; a = fid->aux; if(a == nil) return; fid->aux = nil; if(a->type == Adir){ if(a->dir != nil){ ext4_dir_close(a->dir); free(a->dir); } }else if(a->type == Afile){ if(a->file != nil){ ext4_fclose(a->file); free(a->file); } }else{ /* that would be a BUG */ return; } if(decref(a->p) == 0) closepart(a->p); free(a->path); free(a); } static int note(void *, char *s) { if(strncmp(s, "sys:", 4) != 0){ closeallparts(); return 1; } return 0; } static void rstart(Srv *) { threadnotify(note, 1); } static void rend(Srv *) { closeallparts(); threadexitsall(nil); } static Srv fs = { .attach = rattach, .open = ropen, .create = rcreate, .read = rread, .write = rwrite, .remove = rremove, .stat = rstat, .wstat = rwstat, .walk1 = rwalk1, .clone = rclone, .destroyfid = rdestroyfid, .start = rstart, .end = rend, }; static void usage(void) { fprint(2, "usage: %s [-C] [-s srvname]\n", argv0); threadexitsall("usage"); } void threadmain(int argc, char **argv) { char *srv; srv = "ext4"; ARGBEGIN{ case 'D': chatty9p++; break; case 'C': opts.cachewb = 1; break; case 's': srv = EARGF(usage()); }ARGEND if(argc != 0) usage(); threadpostmountsrv(&fs, srv, nil, 0); threadexits(nil); }