ref: eef0fe67ee4bb8c254f0f9e61c24ef5d34d05a4f
dir: /9p.c/
#include "all.h" extern int mpstarted; s32 readfile(u64 dblkno, u64 qpath, char *rbuf, s32 rbufsize, u64 offset); s32 writefile(u64 dblkno, u64 qpath, s16 uid, char *wbuf, s32 wbufsize, u64 offset); Aux* newaux(u64 addr, u16 uid) { Aux *a; a = emalloc9p(sizeof(Aux)); a->dblkno = addr; a->uid = uid; return a; } void freeaux(Aux *a) { if(a == nil) return; free(a); } static void fsattach(Req *req) { short uid; Iobuf *dbuf; Dentry *d; if(authattach(req) < 0){ return; } if((uid = lookupid(req->ifcall.uname, 0)) <= 0){ respond(req, "no such user"); return; } dbuf = getbufchk(Broot, Breadonly, Tdentry, Qproot); if(dbuf == nil){ respond(req, "phase error"); return; } d = &dbuf->io->d; req->fid->aux = newaux(Broot, uid); req->fid->qid = (Qid){Qproot, d->qid.version, QTDIR}; putbuf(dbuf); req->ofcall.qid = req->fid->qid; respond(req, nil); } static void fsdestroyfid(Fid *fid) { if((fid->qid.type & QTAUTH) != 0){ authdestroy(fid); return; } freeaux(fid->aux); } static void fsmkdir(Dentry *d, Dir *dir, char *buf) { memset(dir, 0, sizeof(*dir)); dir->qid = (Qid){d->qid.path, d->qid.version, (d->mode&DMDIR)? QTDIR : QTFILE}; dir->mode = (d->mode & 0777) | (dir->qid.type << 24); dir->atime = time(nil); dir->mtime = d->mtime/Nsec; /* ns to seconds */ dir->length = d->size; if(dir->qid.type & QTDIR) dir->length = 0; if(buf == nil){ dir->name = estrdup9p(d->name); dir->uid = username(d->uid, 0, dir->uid); dir->gid = username(d->gid, 0, dir->gid); dir->muid = username(d->muid, 0, dir->muid); }else{ memset(buf, 0, Namelen + 3 * Userlen); strncpy(buf, d->name, Namelen - 1); dir->name = buf; dir->uid = username(d->uid, 0, buf + Namelen); dir->gid = username(d->gid, 0, buf + Namelen + Userlen); dir->muid = username(d->muid, 0, buf + Namelen + 2 * Userlen); } } static void fsstat(Req *req) { Dentry *d; Iobuf *dbuf; dbuf = getbufchk(((Aux*)req->fid->aux)->dblkno, Breadonly, Tdentry, req->fid->qid.path); if(dbuf == nil){ dprint("fsstat dbuf == nil dblkno %llud qpath %llud\n", ((Aux*)req->fid->aux)->dblkno, req->fid->qid.path); respond(req, errstring[Ephase]); return; } d = &dbuf->io->d; fsmkdir(d, &req->d, nil); putbuf(dbuf); respond(req, nil); } int emptystr(char *s) { if(s == nil) return 1; if(s[0] == '\0') return 1; return 0; } static void fswstat(Req *req) { Dentry *d; Iobuf *dbuf; s16 gid; if(shuttingdown){ respond(req, errstring[Eshutdown]); return; } if(readonly){ respond(req, errstring[Eronly]); return; } if(req->fid->qid.path == Qpctl){ respond(req, errstring[Einval]); return; } if((req->d.type & QTDIR) > 0 && req->d.length != ~0){ respond(req, errstring[Einval]); return; } if(((Aux*)req->fid->aux)->uid == None) goto noperm; /* TODO allow changing the length as per stat(5) */ if(req->d.atime != ~0 || req->d.length != ~0) goto noperm; /* stat(5) says that this is illegal */ if(emptystr(req->d.uid) == 0){ respond(req, errstring[Einval]); return; } /* TODO cwfs/9p2.c, hjfs/fs2.c and kfs64.b do more here. Get that stuff in. */ dbuf = getbufchk(((Aux*)req->fid->aux)->dblkno, Bwritable, Tdentry, req->fid->qid.path); if(dbuf == nil){ respond(req, errstring[Eperm]); return; } d = &dbuf->io->d; if(canaccess(((Aux*)req->fid->aux)->uid, d, DMWRITE) == 0){ putbuf(dbuf); respond(req, errstring[Eaccess]); return; } /* invalid to change the directory bit stat(5) */ if(req->d.mode != ~0 && (req->d.mode&DMDIR) != (d->mode&DMDIR)){ putbuf(dbuf); respond(req, errstring[Einval]); return; } if(emptystr(req->d.name) == 0){ strncpy(d->name, req->d.name, Namelen); d->mtime = nsec(); d->muid = ((Aux*)req->fid->aux)->uid; } if(d->uid == ((Aux*)req->fid->aux)->uid || leadgroup(((Aux*)req->fid->aux)->uid, d->gid) == 1){ if(req->d.mtime != ~0){ d->mtime = req->d.mtime*Nsec; d->muid = ((Aux*)req->fid->aux)->uid; } if(req->d.mode != ~0 && (req->d.mode&DMDIR) == (d->mode&DMDIR) && req->d.mode != d->mode) d->mode = req->d.mode; /* TODO gid setting needs more fine tuning to align with stat(5) */ if(emptystr(req->d.gid) == 0 && (gid = lookupid(req->d.gid, 0)) != 0){ d->gid = gid; } } putbuf(dbuf); respond(req, nil); return; noperm: respond(req, errstring[Eperm]); return; } static void fsread(Req *req) { s32 n; Iobuf *dbuf, *cbuf; Dentry *d; char nbuf[Namelen + 3 * Userlen]; Dir dir; if(shuttingdown){ respond(req, errstring[Eshutdown]); return; } if(req->fid->qid.type == QTAUTH){ authread(req); return; }else if(req->fid->qid.path == Qpctl){ ctlread(req); return; } if((req->fid->qid.type & QTDIR) != 0){ /* some directory, find the child at offset */ d = searchdir(((Aux*)req->fid->aux)->dblkno, req->fid->qid.path, ((Aux*)req->fid->aux)->uid, nil, ((Aux*)req->fid->aux)->dri, &dbuf, &cbuf); if(d == nil){ req->ofcall.offset = req->ifcall.offset; req->ofcall.count = 0; ((Aux*)req->fid->aux)->dri = 0; respond(req, nil); return; } fsmkdir(d, &dir, nbuf); req->ofcall.count = n = convD2M(&dir, (u8*)req->ofcall.data, req->ifcall.count); req->ofcall.offset = req->ifcall.offset+n; if(n == 0) ((Aux*)req->fid->aux)->dri = 0; else ((Aux*)req->fid->aux)->dri++; freesearchstate(&dbuf, &cbuf); respond(req, nil); return; } n = readfile(((Aux*)req->fid->aux)->dblkno, req->fid->qid.path, req->ofcall.data, req->ifcall.count, req->ifcall.offset); req->ofcall.count = n; req->ofcall.offset = req->ifcall.offset+n; respond(req, nil); } static void fswrite(Req *req) { int n; if(shuttingdown){ respond(req, errstring[Eshutdown]); return; } if(readonly){ respond(req, errstring[Eronly]); return; } if(req->fid->qid.type == QTAUTH){ authwrite(req); return; }else if(req->fid->qid.path == Qpctl){ ctlwrite(req); return; } if((req->fid->qid.type & QTDIR) != 0){ respond(req, errstring[Einval]); return; } n = writefile(((Aux*)req->fid->aux)->dblkno, req->fid->qid.path, ((Aux*)req->fid->aux)->uid, req->ifcall.data, req->ifcall.count, req->ifcall.offset); if(n == -3) respond(req, errstring[Efull]); else if(n == -1 || n == -2) respond(req, errstring[Ephase]); else{ req->ofcall.count = n; req->ofcall.offset = req->ifcall.offset+n; respond(req, nil); } } static void fsremove(Req *req) { Fid *fid; Aux *aux; if(shuttingdown){ respond(req, errstring[Eshutdown]); return; } if(readonly){ respond(req, errstring[Eronly]); return; } fid = req->fid; aux = fid->aux; if(aux == nil || aux->uid == None || fid->qid.path < Qproot || aux->dblkno == 0){ respond(req, errstring[Eperm]); return; } if(fid->qid.type & QTDIR) rmdirectory(fid->qid.path, aux->dblkno); else rmfile(fid->qid.path, aux->dblkno); respond(req, nil); } static char* fswalk1(Fid *fid, char *name, Qid *qid) { Aux *aux; Dentry *d, *p; Iobuf *dbuf, *pbuf, *cbuf; if(shuttingdown){ return nil; } if((fid->qid.type&QTDIR) == 0) return errstring[Enotdir]; aux = fid->aux; if(strcmp(name, "..") == 0){ switch(fid->qid.path){ case Qproot: return nil; default: if(chatty9p > 1) dprint("fswalk1 .. fid->qid.path %llud aux->dblkno %llud\n", fid->qid.path, aux->dblkno); dbuf = getbufchk(aux->dblkno, Breadonly, Tdentry, fid->qid.path); if(dbuf == nil) return errstring[Ephase]; d = &dbuf->io->d; if(chatty9p > 1) dprint("fswalk1 .. d->name %s d->qid.path %llud d->pdblkno %llud\n", d->name, d->qid.path, d->pdblkno); pbuf = getbufchk(d->pdblkno, Breadonly, Tdentry, d->pqpath); if(pbuf == nil) return errstring[Ephase]; putbuf(dbuf); p = &pbuf->io->d; *qid = (Qid){p->qid.path, p->qid.version, (p->mode&DMDIR) ? QTDIR : QTFILE}; if(aux != nil) aux->dblkno = pbuf->blkno; putbuf(pbuf); fid->qid = *qid; return nil; } } /* assuming that it will ever be here only for directories */ /* some directory, find the child with name or idx */ d = searchdir(aux->dblkno, fid->qid.path, aux->uid, name, 0, &dbuf, &cbuf); if(d == nil) return "directory entry not found"; if((d->mode&DMDIR) && canaccess(d->uid, d, DMEXEC) == 0){ freesearchstate(&dbuf, &cbuf); return errstring[Eperm]; } *qid = (Qid){d->qid.path, d->qid.version, (d->mode&DMDIR) ? QTDIR : QTFILE}; if(aux != nil) aux->dblkno = cbuf->blkno; freesearchstate(&dbuf, &cbuf); fid->qid = *qid; return nil; } static char* fsclone(Fid *oldfid, Fid *newfid) { Aux *o; if(shuttingdown){ return nil; } o = oldfid->aux; if(o == nil) return "bad fid"; newfid->aux = newaux(o->dblkno, o->uid); return nil; } /* * error(Eperm) if open permission not granted for up->user. */ int permcheck(u16 fileuid, u16 filegid, u16 uid, ulong perm, int omode) { ulong t; static int access[] = { 0400, 0200, 0600, 0100 }; if(uid == fileuid) perm <<= 0; else if(ingroup(uid, filegid, 0) == 0) perm <<= 3; else perm <<= 6; t = access[omode&3]; if((t&perm) != t) return 0; return 1; } /* read the Req.ifcall's perm, name and mode and build the Fid.omode */ static void fscreate(Req *req) { Iobuf *dbuf, *cbuf; u64 reli, blkno; Dentry *dchild, *dparent; Tag *ct; u64 zblkno; /* zero'ed dentry that can be reused */ Aux *aux; u32 perm; Fid *fid; /* no create's when shutting down */ if(shuttingdown){ respond(req, errstring[Eshutdown]); return; } fid = req->fid; aux = fid->aux; if(chatty9p >2) dprint("fscreate aux 0x%p aux->uid %d fid->qid.path %d aux->dblkno %llud\n", aux, aux->uid, fid->qid.path, aux->dblkno); if(aux == nil || aux->uid == None || fid->qid.path < Qproot || aux->dblkno == 0){ respond(req, errstring[Eaccess]); return; } if(strlen(req->ifcall.name) > Namelen){ respond(req, errstring[Etoolong]); return; } if((fid->qid.type & QTDIR) == 0){ respond(req, errstring[Enotdir]); return; } if(req->ifcall.name == nil || strlen(req->ifcall.name) == 0 || req->ifcall.name[0] == '/' || checkname9p2(req->ifcall.name) == 0){ respond(req, errstring[Ebadname]); return; } if(readonly){ respond(req, errstring[Eaccess]); return; } perm = req->ifcall.perm; cbuf = nil; USED(cbuf); zblkno = 0; dbuf = getbufchk(aux->dblkno, Bwritable, Tdentry, fid->qid.path); if(dbuf == nil){ respond(req, errstring[Ephase]); return; } dparent = &dbuf->io->d; if(canaccess(((Aux*)req->fid->aux)->uid, dparent, DMWRITE) == 0){ respond(req, errstring[Eperm]); return; } if(canaccess(aux->uid, dparent, DMWRITE) == 0){ putbuf(dbuf); respond(req, errstring[Eaccess]); return; } fid->omode = OREAD; switch(req->ifcall.mode & 7) { case OREAD: case OEXEC: fid->omode = OREAD; break; case OWRITE: fid->omode = OWRITE; break; case ORDWR: fid->omode = OREAD+OWRITE; break; default: putbuf(dbuf); respond(req, errstring[Emode]); return; } for(reli = 0, blkno = 1; blkno > 0; reli++){ blkno = rel2abs(dparent, reli); /* if(reli >= Max) error(Etoobig); */ if(blkno == 0){ /* end reached, nothing found, create */ if(zblkno != 0){ cbuf = getbufchk(zblkno, Bwritable, Tdentry, Qpnone); if(cbuf == nil){ putbuf(dbuf); respond(req, errstring[Ephase]); return; } }else{ cbuf = allocblock(Tdentry, fid->qid.path); if(cbuf == nil){ putbuf(dbuf); respond(req, errstring[Efull]); return; } } dchild = &cbuf->io->d; dchild->size = 0; dchild->pdblkno = dbuf->blkno; dchild->pqpath = dparent->qid.path; dchild->mtime = nsec(); dchild->uid = dchild->muid = aux->uid; dchild->gid = dparent->gid; /* TODO DMAPPEND and DMEXCL attributes */ if(perm&DMDIR){ if((req->ifcall.mode & OTRUNC) || (perm & DMAPPEND) || (fid->omode & OWRITE)){ putbuf(cbuf); putbuf(dbuf); respond(req, errstring[Eaccess]); return; } } if(perm&DMDIR) dchild->mode = DMDIR | (perm & (~0777 | (dparent->mode & 0777))); else dchild->mode = perm & (~0666 | (dparent->mode & 0666)); if(perm&DMAPPEND) dchild->mode |= DMAPPEND; /* if(perm&DMEXCL) TODO create a list of locks dchild->mode |= DMEXCL; */ if(canaccess(aux->uid, dchild, DMWRITE) == 0){ putbuf(cbuf); putbuf(dbuf); respond(req, errstring[Eaccess]); return; } dchild->qid.path = newqpath(); dchild->qid.version = 0; strncpy(dchild->name, req->ifcall.name, Namelen); fid->qid = (Qid){dchild->qid.path, 0, (perm&DMDIR) ? QTDIR : QTFILE}; aux->dblkno = cbuf->blkno; aux->dri = 0; req->ofcall.qid = fid->qid; req->ofcall.iounit = Iounit; settag(cbuf, Tdentry, dchild->qid.path); putbuf(cbuf); /* save Iobuf of the content */ /* only add it to the directory dentry if we are adding a new dentry block if we are reusing a zero'ed out slot, it already exists in the directory dentry */ if(zblkno == 0 && addrelative(dparent, dbuf->blkno, reli, aux->dblkno) == 0){ putbuf(dbuf); respond(req, errstring[Ephase]); return; } putbuf(dbuf); respond(req, nil); return; }else{ cbuf = getbuf(blkno, Breadonly, Bused); if(cbuf == nil){ putbuf(dbuf); respond(req, errstring[Ephase]); return; } dchild = &cbuf->io->d; ct = (Tag*)cbuf->io; /* nothing to do for already zero'ed out slots */ if(ct->path == Qpnone){ if(zblkno == 0) zblkno = cbuf->blkno; goto Nextdentry; } if(ct->type != Tdentry){ putbuf(cbuf); putbuf(dbuf); respond(req, errstring[Ephase]); return; } if(strcmp(req->ifcall.name, dchild->name) == 0){ /* req->ifcall.name matched, truncate file and use it */ if(permcheck(dchild->uid, dchild->gid, aux->uid, perm, ORDWR) == 0){ respond(req, errstring[Eperm]); return; } dchild->muid = aux->uid; fid->qid = (Qid){dchild->qid.path, dchild->qid.version, (req->ifcall.mode&DMDIR) ? QTDIR : QTFILE}; aux->dblkno = blkno; aux->dri = 0; req->ofcall.qid = fid->qid; req->ofcall.iounit = Iounit; /* save Iobuf of the child so truncate can open it with a wlock(), if needed */ putbuf(cbuf); if(req->ofcall.qid.path >= Qpusers && req->ofcall.qid.type == QTFILE) truncatefile(req->ofcall.qid.path, blkno, aux->uid); putbuf(dbuf); /* DBG("> mafscreate c->path %s mode 0x%ux omode0 0x%ux\n" " c->qid.path 0x%zux c->qid.vers %lud c->qid.type %ud 0x%ux\n" " c->aux 0x%p\n", chanpath(c), c->mode, omode, c->qid.path, c->qid.vers, c->qid.type, c->qid.type, c->aux);*/ respond(req, nil); return; } Nextdentry: putbuf(cbuf); } } } /* read the Req.ifcall.mode and build the Fid.omode based on the dentry */ static void fsopen(Req *req) { u8 mode, omode; Fid *fid; Iobuf *dbuf; Aux *aux; Dentry *d; dbuf = nil; mode = req->ifcall.mode; fid = req->fid; aux = req->fid->aux; omode = 0; if(chatty9p > 1) dprint("fsopen fid->qid.path %d mode %d readonly %d\n", fid->qid.path, mode, readonly); if(readonly && (mode & (ORCLOSE | OTRUNC | OWRITE | ORDWR)) != 0) goto inval; dbuf = getbufchk(aux->dblkno, Breadonly, Tdentry, fid->qid.path); if(dbuf == nil){ respond(req, errstring[Ephase]); return; } d = &dbuf->io->d; if((mode & OTRUNC) != 0 && canaccess(aux->uid, d, DMWRITE) == 0) goto perm; if((mode & ORCLOSE) != 0) if(canaccess(aux->uid, d, DMWRITE) == 0) goto perm; if((d->mode & DMAPPEND) != 0) mode &= ~OTRUNC; if((d->mode & DMDIR) != 0){ if((mode & (ORCLOSE | OTRUNC | OWRITE | ORDWR)) != 0) goto inval; if(canaccess(aux->uid, d, DMEXEC) == 0) goto perm; } if((d->mode & DMEXCL) != 0){ respond(req, "DMEXCL read not implemented"); return; } switch(mode & OEXEC){ case ORDWR: omode |= ORDWR; case OWRITE: omode |= OWRITE; break; case OEXEC: case OREAD: omode |= OREAD; break; } if((mode & ORCLOSE) != 0) omode |= ORCLOSE; fid->qid = (Qid){d->qid.path, d->qid.version, (d->mode&DMDIR) ? QTDIR : QTFILE}; fid->omode = omode; req->ofcall.iounit = Iounit; req->ofcall.qid = fid->qid; putbuf(dbuf); if((mode & OTRUNC) == 0){ respond(req, nil); return; } /* truncate file */ truncatefile(fid->qid.path, aux->dblkno, aux->uid); respond(req, nil); return; inval: if(dbuf != nil) putbuf(dbuf); respond(req, errstring[Einval]); return; perm: if(dbuf != nil) putbuf(dbuf); respond(req, errstring[Eaccess]); return; } /* below is from nemo's Pg 252 */ typedef struct Buffer Buffer; typedef struct Work Work; struct Work { void (*f)(Req *r); Req *r; }; struct Buffer { QLock lck; Work works[Nworks]; u16 hd, tl, nworks; Rendez isfull; /* throttling */ Rendez isempty; /* workers do not have to keep polling to find work */ }; Buffer buf; Work get(Buffer *b) { Work w; if(shuttingdown) return (Work){nil,nil}; qlock(&b->lck); if(b->nworks == 0){ rsleep(&b->isempty); if(shuttingdown){ qunlock(&b->lck); return (Work){nil,nil}; } } w = b->works[b->hd]; b->hd = ++b->hd %Nworks; b->nworks--; if(b->nworks == Nworks-1) rwakeup(&b->isfull); qunlock(&b->lck); return w; } static void stats(void); void put(Buffer *b, void (*f)(Req *r), Req *r) { Work w; if(shuttingdown){ respond(r, errstring[Eshutdown]); qlock(&b->lck); rwakeupall(&b->isempty); qunlock(&b->lck); } w.f = f; w.r = r; qlock(&b->lck); if(b->nworks == Nworks){ rsleep(&b->isfull); if(shuttingdown){ qunlock(&b->lck); respond(r, errstring[Eshutdown]); } } b->works[b->tl] = w; b->tl = ++b->tl % Nworks; b->nworks++; if(b->nworks == 1) rwakeup(&b->isempty); qunlock(&b->lck); if(chatty9p > 1) stats(); } struct { u32 pid; Work w; } worker[Nprocs]; /* keeps track of running procs to flush */ void initworks(Buffer *b) { // release all locks, set everything to null values memset(b, 0, sizeof(*b)); // set the locks used by the Rendezes b->isempty.l = &b->lck; b->isfull.l = &b->lck; } int stopworkers(void) { int i, a; a = 0; for(i = 0; i<Nprocs; i++){ if(worker[i].w.f == nil){ if(worker[i].pid > 0 && worker[i].pid != getpid()){ // rwakeup(&buf.isempty); TODO why can't I get this to work? postnote(PNPROC, worker[i].pid, "interrupt"); worker[i].pid = 0; } }else a++; } return a; } static void stats(void) { int n, w, inv, i; n = w = inv = 0; for(i = 0; i<Nprocs; i++){ if(worker[i].pid == 0) inv++; else if(worker[i].w.f == nil) n++; else if(worker[i].w.f != nil) w++; } dprint("Nprocs %d inv %d idle %d working %d buf nworks %d hd %d tl %d\n", Nprocs, inv, n, w, buf.nworks, buf.hd, buf.tl); } void shutdown(void) { u64 n; /* User *u, *v; */ if(chatty9p > 1) dprint("shutdown\n"); shuttingdown = 1; fsok(1); // showextents(&frees); // dprint("&buf.isempty %#p\n", &buf.isempty); if(mpstarted){ qlock(&buf.lck); rwakeupall(&buf.isempty); qunlock(&buf.lck); } while((n=pendingwrites())>0){ if(chatty9p > 1) dprint("shutdown: pendingwrites %llud of write queue\n", n); sleep(1000); } savefrees(Bdfrees); while((n=pendingwrites())>0){ if(chatty9p > 1) dprint("shutdown: pendingwrites %llud of frees\n", n); sleep(1000); } stopwriter(); /* free users, why bother? leave it alone */ /* u = t->users; while(u != nil){ v = u->next; free(u); u = v; } t->users = nil;*/ if(chatty9p > 1) dprint("shutdown: exiting\n"); /* chkqunlock(&superlock); exit while holding the lock */ } void work(Buffer *b, int id) { Work w; worker[id].pid = getpid(); w = get(b); while(w.f != nil){ worker[id].w = w; w.f(w.r); worker[id].w = (Work){nil, nil}; w = get(b); } worker[id].pid = 0; } void startproc(Buffer *b, int id) { char name[16]; switch(rfork(RFPROC|RFMEM|RFFDG)){ case -1: panic("can't fork"); case 0: if(chatty9p > 1) dprint("child %d pid: %d\n", id, getpid()); break; default: return; } procname = name; snprint(name, 16, "mafs worker %d", id); procsetname(name); work(b, id); if(chatty9p) dprint("%s process exited\n", name); exits(nil); } void fsstart(Srv *) { int i; mpstarted=1; initworks(&buf); if(chatty9p > 1) dprint("parent pid: %d\n", getpid()); for(i = 0; i < Nprocs; i++){ startproc(&buf, i); } } void fshalt(Srv *) { if(shuttingdown == 0) shutdown(); /* no need to bother removing /srv/service as we do for cmdhalt in ctlwrite() of ctl.c as the srv() takes care of that cleanup */ } void bfsopen(Req *r) { put(&buf, fsopen, r); } void bfscreate(Req *r) { put(&buf, fscreate, r); } void bfsread(Req *r) { if(r->fid->qid.type == QTAUTH){ authread(r); return; }else put(&buf, fsread, r); } void bfswrite(Req *r) { if(r->fid->qid.type == QTAUTH){ authwrite(r); return; }else put(&buf, fswrite, r); } void bfsstat(Req *r) { put(&buf, fsstat, r); } void bfswstat(Req *r) { put(&buf, fswstat, r); } void bfsremove(Req *r) { put(&buf, fsremove, r); } /* multi process server */ Srv mpsrv = { .auth = auth9p, .attach = fsattach, .destroyfid = fsdestroyfid, .msize = Iounit, .walk1 = fswalk1, .clone = fsclone, .open = bfsopen, .create = bfscreate, .read = bfsread, .write = bfswrite, .stat = bfsstat, .wstat = bfswstat, .remove = bfsremove, .start = fsstart, .end = fshalt, }; /* usrv is a uni process server */ Srv usrv = { .auth = auth9p, .attach = fsattach, .destroyfid = fsdestroyfid, .msize = Iounit, .walk1 = fswalk1, .clone = fsclone, .open = fsopen, .create = fscreate, .read = fsread, .write = fswrite, .stat = fsstat, .wstat = fswstat, .remove = fsremove, .end = fshalt, }; /* 08:08 < joe7> is there a way to interact with the stdin of a process using srv() from rc? I understand that it runs the srv() loop between the fd's 0 and 1. 08:13 < joe7> I wrote an userspace file server and I want to test it without using postmountsrv (avoiding the pipe interface). 08:45 < cinap_lenrek> {yourfileserver <[0=1] &} | echo 0 >/srv/service 08:46 < cinap_lenrek> then you should be able to mount /srv/service 08:47 < cinap_lenrek> the <>{} operator might also work 08:50 < cinap_lenrek> mount <{ramfs -i <[0=1]} /n/ram 08:50 < cinap_lenrek> works. */ void start9p(char **nets, int stdio) { int sfd; while(nets && *nets){ mpsrv.end = nil; /* disable shutdown */ listensrv(&mpsrv, *nets++); } if(stdio){ usrv.infd = 0; usrv.outfd = 1; srv(&usrv); }else{ sfd = postsrv(&mpsrv, service); if(sfd < 0) sysfatal("postsrv: %r"); close(sfd); } } /* static int dir9p2(Dir* dir, Dentry* dentry, void* strs) { char *op, *p; memset(dir, 0, sizeof(Dir)); mkqid(dir->qid, dentry, 1); dir->mode = (dir->qid.type<<24)|(dentry->mode & 0777); dir->mtime = dentry->mtime; dir->length = dentry->size; op = p = strs; dir->name = p; p += sprint(p, "%s", dentry->name)+1; dir->uid = p; uidtostr(p, dentry->uid); p += strlen(p)+1; dir->gid = p; uidtostr(p, dentry->gid); p += strlen(p)+1; dir->muid = p; strcpy(p, ""); p += strlen(p)+1; return p-op; } */ s32 readfilesize(u64 dblkno, u64 qpath) { u64 filesize; Iobuf *dbuf; dbuf = getbufchk(dblkno, Breadonly, Tdentry, qpath); if(dbuf == nil) return 0; filesize = dbuf->io->d.size; putbuf(dbuf); return filesize; } s32 readfile(u64 dblkno, u64 qpath, char *rbuf, s32 rbufsize, u64 offset) { Dentry *d; s64 tosend, sent, filesize; s32 n; Iobuf *dbuf, *buf; dbuf = getbufchk(dblkno, Breadonly, Tdentry, qpath); if(dbuf == nil) return 0; d = &dbuf->io->d; filesize = d->size; if(offset >= filesize){ putbuf(dbuf); return 0; } if(filesize - offset > rbufsize) tosend = rbufsize; else tosend = filesize - offset; for(sent = 0; sent < tosend && offset+sent < d->size; ){ buf = getdatablkat(d, (offset+sent)/Blocksize, Breadonly); if(buf == nil){ putbuf(dbuf); return -1; } n = min(Blocksize-((offset+sent)%Blocksize), tosend-sent); memcpy(rbuf+sent, buf->io->buf+((offset+sent)%Blocksize), n); sent += n; putbuf(buf); } putbuf(dbuf); return sent; } /* obsolete */ /*s32 updatefullspan(Dentry *d, char *wbuf, s32 wbufsize, u64 offset) { Iobuf *buf; s32 n; if(d == nil || wbuf == nil || wbufsize == 0) return 0; buf = getdatablkat(d, offset/Blocksize, Bwritable); if(buf == nil) return -1; n = min(Blocksize, wbufsize); memcpy(buf->io->buf, wbuf, n); putbuf(buf); return n; }*/ /* only for updating existing data */ s32 update(Dentry *d, u64 /* dblkno */, char *wbuf, s32 wbufsize, u64 offset) { Iobuf *buf; s32 howmuch; u64 blkno, to; if(d == nil || wbuf == nil || wbufsize == 0) return 0; if(offset+wbufsize > d->size) panic("update(): should not be here\n" " offset %llud wbufsize %d d->size %llud", offset, wbufsize, d->size); /* get the extent overlay data */ blkno = rel2abs(d, offset/Blocksize); if(chatty9p > 1) dprint("update d->name %s d->size %llud offset %llud" " rel2abs(offset/Blocksize %llud) = blkno %llud\n", d->name, d->size, offset, offset/Blocksize, blkno); buf = getbufchk(blkno, Bwritable, Tdata, d->qid.path); if(buf == nil) return -1; /* overlay the new contents */ to = offset%Blocksize; howmuch = min(Blocksize-to, wbufsize); if(chatty9p > 1){ dprint("updating buf->blkno %llud offset %llud size %llud\n", buf->blkno, to, howmuch); showbuf(buf); } memcpy(buf->io->buf+to, wbuf, howmuch); if(chatty9p > 1){ dprint("update after\n"); showbuf(buf); } putbuf(buf); return howmuch; } s32 append(Dentry *d, u64 dblkno, char *wbuf, s32 wbufsize) { Iobuf *buf; s32 howmuch; u64 blkno, lastblksize; if(d == nil || wbuf == nil || wbufsize == 0) return 0; if(chatty9p > 1) dprint("append wbufsize %d\n", wbufsize); if((lastblksize=d->size%Blocksize) == 0){ /* last block is full, use a new block */ if(chatty9p > 1) dprint("append new extent rel2abs d->name %s reli d->size %llud" " d->size/Blocksize %llud\n", d->name, d->size, d->size/Blocksize); buf = allocblock(Tdata, d->qid.path); if(buf == nil) return -1; blkno = buf->blkno; howmuch = min(Blocksize, wbufsize); memcpy(buf->io->buf, wbuf, howmuch); putbuf(buf); if(addrelative(d, dblkno, d->size/Blocksize, blkno) == 0){ freeblock(blkno, Tdata, d->qid.path); return -2; } return howmuch; }else{ /* last block is partially full, fill it up */ /* get the old block and the new Span copy from the old Span to the new addrelative the new Span free the old Span */ blkno = rel2abs(d, d->size/Blocksize); if(blkno == 0) panic("append update rel2abs blkno == 0" " d->name %s reli d->size %llud" " d->size/Blocksize %llud s.blkno %llud\n", d->name, d->size, d->size/Blocksize, blkno); if(chatty9p > 1) dprint("append update rel2abs d->name %s reli d->size %llud" " d->size/Blocksize %llud blkno %llud\n", d->name, d->size, d->size/Blocksize, blkno); buf = getbufchk(blkno, Bwritable, Tdata, d->qid.path); if(buf == nil) return -1; howmuch = min(Blocksize-lastblksize, wbufsize); if(chatty9p > 1) dprint("fill lastblksize %llud howmuch %llud\n", lastblksize, howmuch); memcpy(buf->io->buf+lastblksize, wbuf, howmuch); putbuf(buf); return howmuch; } } /* pad blanks one span at a time */ static s32 padblanks(Dentry *d, u64 dblkno, s32 size) { s8 *buf; int n; if(d == nil || size <= 0) return 0; n = min(size, Blocksize - d->size%Blocksize); buf = emalloc9p(n); n = append(d, dblkno, buf, n); free(buf); return n; } /* 3 scenarios offset < filesize && offset+wbufsize <= filesize replacing the data in existing blocks offset <= filesize && offset+wbufsize > filesize replacing the data in existing blocks add data to new blocks offset > filesize new blank blocks until offset data blocks from offset until offset+wbufsize */ s32 writefile(u64 dblkno, u64 qpath, s16 uid, char *wbuf, s32 wbufsize, u64 offset) { Dentry *d; s64 written; s32 n; Iobuf *dbuf; dbuf = getbufchk(dblkno, Bwritable, Tdentry, qpath); if(dbuf == nil) return 0; d = &dbuf->io->d; d->muid = uid; // odentry(d); for(written = 0; written < wbufsize; ){ if(chatty9p > 1) dprint("writefile(): d->name %s d->size %llud offset %llud" " written %d offset+written %llud wbufsize %ld\n", d->name, d->size, offset, written, offset+written, wbufsize); /* all the below functions only write upto the end of an extent. Hence, the need for a loop to keep repeating. */ if(offset > d->size){ /* new blank blocks until offset fill blank data upto offset */ if(chatty9p > 1) dprint("writefile(): blank blocks until offset\n"); n = padblanks(d, dblkno, offset+wbufsize-d->size); if(chatty9p > 1) dprint("writefile(): padblanks returned %d\n", n); if(n<0){ dprint("padblanks has an issue %d\n", n); goto writeend; }else d->size += n; }else if(offset+written < d->size){ /* replacing existing data no change to file size */ if(chatty9p > 1) dprint("writefile(): replace existing data\n"); n = update(d, dblkno, wbuf+written, /* from where */ /* how much */ min(wbufsize-written, d->size-(offset+written)), offset+written); /* to where */ if(chatty9p > 1) dprint("writefile(): update returned %d\n", n); if(n<0){ dprint("update has an issue %d\n", n); goto writeend; }else written += n; }else if(offset+written >= d->size){ /* append data, changes file size data blocks from offset until offset+wbufsize */ if(chatty9p > 1) dprint("writefile(): append\n"); n = append(d, dblkno, wbuf+written, /* from where */ wbufsize-written /* how much */); if(chatty9p > 1) dprint("writefile(): append returned %d\n", n); if(n<0){ dprint("append has an issue %d\n", n); goto writeend; } written += n; d->size += n; } else panic("writefile: should not be here"); } writeend: d->mtime = nsec(); putbuf(dbuf); return written; }