#include "all.h" /* * Cf. /lib/rfc/rfc1094 */ static int nfsnull(int, Rpccall*, Rpccall*); static int nfsgetattr(int, Rpccall*, Rpccall*); static int nfssetattr(int, Rpccall*, Rpccall*); static int nfsroot(int, Rpccall*, Rpccall*); static int nfslookup(int, Rpccall*, Rpccall*); static int nfsreadlink(int, Rpccall*, Rpccall*); static int nfsread(int, Rpccall*, Rpccall*); static int nfswritecache(int, Rpccall*, Rpccall*); static int nfswrite(int, Rpccall*, Rpccall*); static int nfscreate(int, Rpccall*, Rpccall*); static int nfsremove(int, Rpccall*, Rpccall*); static int nfsrename(int, Rpccall*, Rpccall*); static int nfslink(int, Rpccall*, Rpccall*); static int nfssymlink(int, Rpccall*, Rpccall*); static int nfsmkdir(int, Rpccall*, Rpccall*); static int nfsrmdir(int, Rpccall*, Rpccall*); static int nfsreaddir(int, Rpccall*, Rpccall*); static int nfsstatfs(int, Rpccall*, Rpccall*); Procmap nfsproc[] = { 0, nfsnull, /* void */ 1, nfsgetattr, /* Fhandle */ 2, nfssetattr, /* Fhandle, Sattr */ 3, nfsroot, /* void */ 4, nfslookup, /* Fhandle, String */ 5, nfsreadlink, /* Fhandle */ 6, nfsread, /* Fhandle, long, long, long */ 7, nfswritecache,/* void */ 8, nfswrite, /* Fhandle, long, long, long, String */ 9, nfscreate, /* Fhandle, String, Sattr */ 10, nfsremove, /* Fhandle, String */ 11, nfsrename, /* Fhandle, String, Fhandle, String */ 12, nfslink, /* Fhandle, Fhandle, String */ 13, nfssymlink, /* Fhandle, String, String, Sattr */ 14, nfsmkdir, /* Fhandle, String, Sattr */ 15, nfsrmdir, /* Fhandle, String */ 16, nfsreaddir, /* Fhandle, long, long */ 17, nfsstatfs, /* Fhandle */ 0, 0 }; void nfsinit(int, char**); extern void mntinit(int, char**); extern Procmap mntproc[]; Progmap progmap[] = { 100005, 1, mntinit, mntproc, 100003, 2, nfsinit, nfsproc, 0, 0, 0, }; int myport = 2049; long nfstime; int conftime; void main(int argc, char *argv[]) { server(argc, argv, myport, progmap); } static void doalarm(void) { nfstime = time(0); mnttimer(nfstime); if(conftime+5*60 < nfstime){ conftime = nfstime; readunixidmaps(config); } } void nfsinit(int argc, char **argv) { USED(argc, argv); clog("nfs file server init\n"); rpcalarm = doalarm; nfstime = time(0); } static int nfsnull(int n, Rpccall *cmd, Rpccall *reply) { USED(n, reply); chat("nfsnull..."); showauth(&cmd->cred); chat("OK\n"); return 0; } static int nfsgetattr(int n, Rpccall *cmd, Rpccall *reply) { Xfid *xf; Dir dir; uchar *dataptr = reply->results; chat("getattr..."); if(n != FHSIZE) return garbage(reply, "bad count"); xf = rpc2xfid(cmd, &dir); if(xf == 0) return error(reply, NFSERR_STALE); chat("%s...", xf->xp->name); PLONG(NFS_OK); dataptr += dir2fattr(cmd->up, &dir, dataptr); chat("OK\n"); return dataptr - (uchar *)reply->results; } static int nfssetattr(int n, Rpccall *cmd, Rpccall *reply) { Xfid *xf; Dir dir, nd; Sattr sattr; int r; uchar *argptr = cmd->args; uchar *dataptr = reply->results; chat("setattr..."); if(n <= FHSIZE) return garbage(reply, "count too small"); xf = rpc2xfid(cmd, &dir); argptr += FHSIZE; argptr += convM2sattr(argptr, &sattr); if(argptr != &((uchar *)cmd->args)[n]) return garbage(reply, "bad count"); chat("mode=0%lo,u=%ld,g=%ld,size=%ld,atime=%ld,mtime=%ld...", sattr.mode, sattr.uid, sattr.gid, sattr.size, sattr.atime, sattr.mtime); if(xf == 0) return error(reply, NFSERR_STALE); if(sattr.uid != NOATTR || sattr.gid != NOATTR) return error(reply, NFSERR_PERM); if(sattr.size == 0){ if(xf->xp->s != xf->xp->parent->s){ if(xfauthremove(xf, cmd->user) < 0) return error(reply, NFSERR_PERM); }else if(dir.length && xfopen(xf, Trunc|Oread|Owrite) < 0) return error(reply, NFSERR_PERM); }else if(sattr.size != NOATTR) return error(reply, NFSERR_PERM); r = 0; nulldir(&nd); if(sattr.mode != NOATTR) ++r, nd.mode = (dir.mode & ~0777) | (sattr.mode & 0777); if(sattr.atime != NOATTR) ++r, nd.atime = sattr.atime; if(sattr.mtime != NOATTR) ++r, nd.mtime = sattr.mtime; chat("sattr.mode=%luo dir.mode=%luo nd.mode=%luo...", sattr.mode, dir.mode, nd.mode); if(r){ r = xfwstat(xf, &nd); if(r < 0) return error(reply, NFSERR_PERM); } if(xfstat(xf, &dir) < 0) return error(reply, NFSERR_STALE); PLONG(NFS_OK); dataptr += dir2fattr(cmd->up, &dir, dataptr); chat("OK\n"); return dataptr - (uchar *)reply->results; } static int nfsroot(int n, Rpccall *cmd, Rpccall *reply) { USED(n, reply); chat("nfsroot..."); showauth(&cmd->cred); chat("OK\n"); return 0; } static int nfslookup(int n, Rpccall *cmd, Rpccall *reply) { Xfile *xp; Xfid *xf, *newxf; String elem; Dir dir; uchar *argptr = cmd->args; uchar *dataptr = reply->results; chat("lookup..."); if(n <= FHSIZE) return garbage(reply, "count too small"); xf = rpc2xfid(cmd, 0); argptr += FHSIZE; argptr += string2S(argptr, &elem); if(argptr != &((uchar *)cmd->args)[n]) return garbage(reply, "bad count"); if(xf == 0) return error(reply, NFSERR_STALE); xp = xf->xp; if(!(xp->qid.type & QTDIR)) return error(reply, NFSERR_NOTDIR); chat("%s -> \"%.*s\"...", xp->name, utfnlen(elem.s, elem.n), elem.s); if(xp->s->noauth == 0 && xp->parent == xp && elem.s[0] == '#') newxf = xfauth(xp, &elem); else newxf = xfwalkcr(Twalk, xf, &elem, 0); if(newxf == 0) return error(reply, NFSERR_NOENT); if(xfstat(newxf, &dir) < 0) return error(reply, NFSERR_IO); PLONG(NFS_OK); dataptr += xp2fhandle(newxf->xp, dataptr); dataptr += dir2fattr(cmd->up, &dir, dataptr); chat("OK\n"); return dataptr - (uchar *)reply->results; } static int nfsreadlink(int n, Rpccall *cmd, Rpccall *reply) { USED(n, reply); chat("readlink..."); showauth(&cmd->cred); return error(reply, NFSERR_NOENT); } static int nfsread(int n, Rpccall *cmd, Rpccall *reply) { Session *s; Xfid *xf; Dir dir; int offset, count; uchar *argptr = cmd->args; uchar *dataptr = reply->results; uchar *readptr = dataptr + 4 + 17*4 + 4; chat("read..."); if(n != FHSIZE+12) return garbage(reply, "bad count"); xf = rpc2xfid(cmd, 0); argptr += FHSIZE; offset = GLONG(); count = GLONG(); if(xf == 0) return error(reply, NFSERR_STALE); chat("%s %d %d...", xf->xp->name, offset, count); if(xf->xp->s != xf->xp->parent->s){ count = xfauthread(xf, offset, readptr, count); }else{ if(xfopen(xf, Oread) < 0) return error(reply, NFSERR_PERM); if(count > 8192) count = 8192; s = xf->xp->s; setfid(s, xf->opfid); xf->opfid->tstale = nfstime + 60; s->f.offset = offset; s->f.count = count; if(xmesg(s, Tread) < 0) return error(reply, NFSERR_IO); count = s->f.count; memmove(readptr, s->f.data, count); } if(xfstat(xf, &dir) < 0) return error(reply, NFSERR_IO); PLONG(NFS_OK); dataptr += dir2fattr(cmd->up, &dir, dataptr); PLONG(count); dataptr += ROUNDUP(count); chat("%d OK\n", count); return dataptr - (uchar *)reply->results; } static int nfswritecache(int n, Rpccall *cmd, Rpccall *reply) { USED(n, reply); chat("writecache..."); showauth(&cmd->cred); chat("OK\n"); return 0; } static int nfswrite(int n, Rpccall *cmd, Rpccall *reply) { Session *s; Xfid *xf; Dir dir; int offset, count; uchar *argptr = cmd->args; uchar *dataptr = reply->results; chat("write..."); if(n < FHSIZE+16) return garbage(reply, "count too small"); xf = rpc2xfid(cmd, 0); argptr += FHSIZE + 4; offset = GLONG(); argptr += 4; count = GLONG(); if(xf == 0) return error(reply, NFSERR_STALE); chat("%s %d %d...", xf->xp->name, offset, count); if(xf->xp->s != xf->xp->parent->s){ if(xfauthwrite(xf, offset, argptr, count) < 0) return error(reply, NFSERR_IO); }else{ if(xfopen(xf, Owrite) < 0) return error(reply, NFSERR_PERM); s = xf->xp->s; setfid(s, xf->opfid); xf->opfid->tstale = nfstime + 60; s->f.offset = offset; s->f.count = count; s->f.data = (char *)argptr; if(xmesg(s, Twrite) < 0) return error(reply, NFSERR_IO); } if(xfstat(xf, &dir) < 0) return error(reply, NFSERR_IO); PLONG(NFS_OK); dataptr += dir2fattr(cmd->up, &dir, dataptr); chat("OK\n"); return dataptr - (uchar *)reply->results; } static int creat(int n, Rpccall *cmd, Rpccall *reply, int chdir) { Xfid *xf, *newxf; Xfile *xp; String elem; Dir dir; Sattr sattr; uchar *argptr = cmd->args; uchar *dataptr = reply->results; int trunced; if(n <= FHSIZE) return garbage(reply, "count too small"); xf = rpc2xfid(cmd, 0); argptr += FHSIZE; argptr += string2S(argptr, &elem); argptr += convM2sattr(argptr, &sattr); if(argptr != &((uchar *)cmd->args)[n]) return garbage(reply, "bad count"); if(xf == 0) return error(reply, NFSERR_STALE); xp = xf->xp; if(!(xp->qid.type & QTDIR)) return error(reply, NFSERR_NOTDIR); chat("%s/%.*s...", xp->name, utfnlen(elem.s, elem.n), elem.s); trunced = 0; if(xp->parent == xp && elem.s[0] == '#'){ newxf = xfauth(xp, &elem); if(newxf == 0) return error(reply, NFSERR_PERM); if(xfauthremove(newxf, cmd->user) < 0) return error(reply, NFSERR_PERM); trunced = 1; }else newxf = xfwalkcr(Twalk, xf, &elem, 0); if(newxf == 0){ newxf = xfwalkcr(Tcreate, xf, &elem, chdir|(sattr.mode&0777)); if(newxf) trunced = 1; else newxf = xfwalkcr(Twalk, xf, &elem, 0); } if(newxf == 0) return error(reply, NFSERR_PERM); if(!trunced && chdir) return error(reply, NFSERR_EXIST); if(!trunced && xfopen(newxf, Trunc|Oread|Owrite) < 0) return error(reply, NFSERR_PERM); if(xfstat(newxf, &dir) < 0) return error(reply, NFSERR_IO); PLONG(NFS_OK); dataptr += xp2fhandle(newxf->xp, dataptr); dataptr += dir2fattr(cmd->up, &dir, dataptr); chat("OK\n"); return dataptr - (uchar *)reply->results; } static int nfscreate(int n, Rpccall *cmd, Rpccall *reply) { chat("create..."); return creat(n, cmd, reply, 0); } static int remov(int n, Rpccall *cmd, Rpccall *reply) { Session *s; Xfile *xp; Xfid *xf, *newxf; String elem; Fid *nfid; uchar *argptr = cmd->args; uchar *dataptr = reply->results; if(n <= FHSIZE) return garbage(reply, "count too small"); xf = rpc2xfid(cmd, 0); argptr += FHSIZE; argptr += string2S(argptr, &elem); if(argptr != &((uchar *)cmd->args)[n]) return garbage(reply, "bad count"); if(xf == 0) return error(reply, NFSERR_STALE); xp = xf->xp; if(!(xp->qid.type & QTDIR)) return error(reply, NFSERR_NOTDIR); chat("%s/%.*s...", xp->name, utfnlen(elem.s, elem.n), elem.s); if(xp->s->noauth == 0 && xp->parent == xp && elem.s[0] == '#') return error(reply, NFSERR_PERM); newxf = xfwalkcr(Twalk, xf, &elem, 0); if(newxf == 0) return error(reply, NFSERR_NOENT); s = xp->s; nfid = newfid(s); setfid(s, newxf->urfid); s->f.newfid = nfid - s->fids; s->f.nwname = 0; if(xmesg(s, Twalk) < 0){ putfid(s, nfid); return error(reply, NFSERR_IO); } s->f.fid = nfid - s->fids; if(xmesg(s, Tremove) < 0){ putfid(s, nfid); return error(reply, NFSERR_PERM); } putfid(s, nfid); xpclear(newxf->xp); PLONG(NFS_OK); chat("OK\n"); return dataptr - (uchar *)reply->results; } static int nfsremove(int n, Rpccall *cmd, Rpccall *reply) { chat("remove..."); return remov(n, cmd, reply); } static int nfsrename(int n, Rpccall *cmd, Rpccall *reply) { Xfid *xf, *newxf; Xfile *xp; uchar *fromdir, *todir; String fromelem, toelem; Dir dir; uchar *argptr = cmd->args; uchar *dataptr = reply->results; chat("rename..."); if(n <= FHSIZE) return garbage(reply, "count too small"); xf = rpc2xfid(cmd, 0); fromdir = argptr; argptr += FHSIZE; argptr += string2S(argptr, &fromelem); todir = argptr; argptr += FHSIZE; argptr += string2S(argptr, &toelem); if(argptr != &((uchar *)cmd->args)[n]) return garbage(reply, "bad count"); if(xf == 0) return error(reply, NFSERR_STALE); xp = xf->xp; if(!(xp->qid.type & QTDIR)) return error(reply, NFSERR_NOTDIR); if(memcmp(fromdir, todir, FHSIZE) != 0) return error(reply, NFSERR_NXIO); newxf = xfwalkcr(Twalk, xf, &fromelem, 0); if(newxf == 0) return error(reply, NFSERR_NOENT); if(xfstat(newxf, &dir) < 0) return error(reply, NFSERR_IO); if(xp->parent == xp && toelem.s[0] == '#') return error(reply, NFSERR_PERM); nulldir(&dir); dir.name = toelem.s; if(xfwstat(newxf, &dir) < 0) return error(reply, NFSERR_PERM); PLONG(NFS_OK); chat("OK\n"); return dataptr - (uchar *)reply->results; } static int nfslink(int n, Rpccall *cmd, Rpccall *reply) { USED(n, reply); chat("link..."); showauth(&cmd->cred); return error(reply, NFSERR_NOENT); } static int nfssymlink(int n, Rpccall *cmd, Rpccall *reply) { USED(n, reply); chat("symlink..."); showauth(&cmd->cred); return error(reply, NFSERR_NOENT); } static int nfsmkdir(int n, Rpccall *cmd, Rpccall *reply) { chat("mkdir..."); return creat(n, cmd, reply, DMDIR); } static int nfsrmdir(int n, Rpccall *cmd, Rpccall *reply) { chat("rmdir..."); return remov(n, cmd, reply); } static int nfsreaddir(int n, Rpccall *cmd, Rpccall *reply) { Session *s; Xfid *xf; Dir dir; char *rdata; int k, offset, count, sfcount, entries, dsize; uchar *argptr = cmd->args; uchar *dataptr = reply->results; chat("readdir..."); if(n != FHSIZE+8) return garbage(reply, "bad count"); xf = rpc2xfid(cmd, 0); argptr += FHSIZE; offset = GLONG(); count = GLONG(); if(xf == 0) return error(reply, NFSERR_STALE); chat("%s (%ld) %d %d...", xf->xp->name, xf->offset, offset, count); s = xf->xp->s; if((xf->mode & Open) && xf->offset > offset) xfclose(xf); if(xfopen(xf, Oread) < 0) return error(reply, NFSERR_PERM); while(xf->offset < offset){ /* if we reopened, xf->offset will be zero */ sfcount = offset - xf->offset; if(sfcount > messagesize-IOHDRSZ) sfcount = messagesize-IOHDRSZ; setfid(s, xf->opfid); s->f.offset = xf->offset; s->f.count = sfcount; if(xmesg(s, Tread) < 0){ xfclose(xf); return error(reply, NFSERR_IO); } if(s->f.count <= BIT16SZ) break; xf->offset += s->f.count; } if(count > messagesize-IOHDRSZ) count = messagesize-IOHDRSZ; PLONG(NFS_OK); entries = 0; while(count > 16){ /* at least 16 bytes required; we don't know size of name */ chat("top of loop\n"); setfid(s, xf->opfid); s->f.offset = xf->offset; s->f.count = count; /* as good a guess as any */ if(xmesg(s, Tread) < 0){ xfclose(xf); return error(reply, NFSERR_IO); } sfcount = s->f.count; if(sfcount <= BIT16SZ) break; xf->offset += sfcount; chat("count %d data 0x%p\n", s->f.count, s->f.data); rdata = s->f.data; /* now have a buffer of Plan 9 directories; unpack into NFS thingies */ while(sfcount >= 0){ dsize = convM2D((uchar*)rdata, sfcount, &dir, (char*)s->statbuf); if(dsize <= BIT16SZ){ count = 0; /* force break from outer loop */ break; } offset += dsize; k = strlen(dir.name); if(count < 16+ROUNDUP(k)){ count = 0; /* force break from outer loop */ break; } PLONG(TRUE); PLONG(dir.qid.path); PLONG(k); PPTR(dir.name, k); PLONG(offset); count -= 16+ROUNDUP(k); rdata += dsize; sfcount -= dsize; } } PLONG(FALSE); if(s->f.count <= 0){ xfclose(xf); chat("eof..."); PLONG(TRUE); }else PLONG(FALSE); chat("%d OK\n", entries); return dataptr - (uchar *)reply->results; } static int nfsstatfs(int n, Rpccall *cmd, Rpccall *reply) { uchar *dataptr = reply->results; enum { Xfersize = 2048, Maxlong = (long)((1ULL<<31) - 1), Maxfreeblks = Maxlong / Xfersize, }; chat("statfs..."); showauth(&cmd->cred); if(n != FHSIZE) return garbage(reply, "bad count"); PLONG(NFS_OK); PLONG(4096); /* tsize (fs block size) */ PLONG(Xfersize); /* bsize (optimal transfer size) */ PLONG(Maxfreeblks); /* blocks in fs */ PLONG(Maxfreeblks); /* bfree to root*/ PLONG(Maxfreeblks); /* bavail (free to mortals) */ chat("OK\n"); /*conftime = 0; readunixidmaps(config);*/ return dataptr - (uchar *)reply->results; }