diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/lnfs.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/lnfs.c')
-rwxr-xr-x | sys/src/cmd/lnfs.c | 781 |
1 files changed, 781 insertions, 0 deletions
diff --git a/sys/src/cmd/lnfs.c b/sys/src/cmd/lnfs.c new file mode 100755 index 000000000..c34669b94 --- /dev/null +++ b/sys/src/cmd/lnfs.c @@ -0,0 +1,781 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <auth.h> +#include <fcall.h> +#include <String.h> +#include <libsec.h> + +enum +{ + OPERM = 0x3, /* mask of all permission types in open mode */ + Maxsize = 512*1024*1024, + Maxfdata = 8192, + NAMELEN = 28, +}; + +typedef struct Fid Fid; +struct Fid +{ + short busy; + int fid; + Fid *next; + char *user; + String *path; /* complete path */ + + int fd; /* set on open or create */ + Qid qid; /* set on open or create */ + int attach; /* this is an attach fd */ + + ulong diroff; /* directory offset */ + Dir *dir; /* directory entries */ + int ndir; /* number of directory entries */ +}; + +Fid *fids; +int mfd[2]; +char *user; +uchar mdata[IOHDRSZ+Maxfdata]; +uchar rdata[Maxfdata]; /* buffer for data in reply */ +uchar statbuf[STATMAX]; +Fcall thdr; +Fcall rhdr; +int messagesize = sizeof mdata; +int readonly; +char *srvname; +int debug; + +Fid * newfid(int); +void io(void); +void *erealloc(void*, ulong); +void *emalloc(ulong); +char *estrdup(char*); +void usage(void); +void fidqid(Fid*, Qid*); +char* short2long(char*); +char* long2short(char*, int); +void readnames(void); +void post(char*, int); + +char *rflush(Fid*), *rversion(Fid*), *rauth(Fid*), + *rattach(Fid*), *rwalk(Fid*), + *ropen(Fid*), *rcreate(Fid*), + *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*), + *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*); + +char *(*fcalls[])(Fid*) = { + [Tversion] rversion, + [Tflush] rflush, + [Tauth] rauth, + [Tattach] rattach, + [Twalk] rwalk, + [Topen] ropen, + [Tcreate] rcreate, + [Tread] rread, + [Twrite] rwrite, + [Tclunk] rclunk, + [Tremove] rremove, + [Tstat] rstat, + [Twstat] rwstat, +}; + +char Eperm[] = "permission denied"; +char Enotdir[] = "not a directory"; +char Enoauth[] = "lnfs: authentication not required"; +char Enotexist[] = "file does not exist"; +char Einuse[] = "file in use"; +char Eexist[] = "file exists"; +char Eisdir[] = "file is a directory"; +char Enotowner[] = "not owner"; +char Eisopen[] = "file already open for I/O"; +char Excl[] = "exclusive use file already open"; +char Ename[] = "illegal name"; +char Eversion[] = "unknown 9P version"; + +void +usage(void) +{ + fprint(2, "usage: %s [-r] [-s srvname] mountpoint\n", argv0); + exits("usage"); +} + +void +notifyf(void *a, char *s) +{ + USED(a); + if(strncmp(s, "interrupt", 9) == 0) + noted(NCONT); + noted(NDFLT); +} + +void +main(int argc, char *argv[]) +{ + char *defmnt; + int p[2]; + Dir *d; + + ARGBEGIN{ + case 'r': + readonly = 1; + break; + case 'd': + debug = 1; + break; + case 's': + srvname = ARGF(); + if(srvname == nil) + usage(); + break; + default: + usage(); + }ARGEND + + if(argc < 1) + usage(); + defmnt = argv[0]; + d = dirstat(defmnt); + if(d == nil || !(d->qid.type & QTDIR)) + sysfatal("mountpoint must be an accessible directory"); + free(d); + + if(pipe(p) < 0) + sysfatal("pipe failed"); + mfd[0] = p[0]; + mfd[1] = p[0]; + + user = getuser(); + notify(notifyf); + + if(srvname != nil) + post(srvname, p[1]); + + if(debug) + fmtinstall('F', fcallfmt); + switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){ + case -1: + sysfatal("fork: %r"); + case 0: + close(p[1]); + chdir(defmnt); + io(); + break; + default: + close(p[0]); /* don't deadlock if child fails */ + if(mount(p[1], -1, defmnt, MREPL|MCREATE, "") < 0) + sysfatal("mount failed: %r"); + } + exits(0); +} + +void +post(char *srvname, int pfd) +{ + char name[128]; + int fd; + + snprint(name, sizeof name, "#s/%s", srvname); + fd = create(name, OWRITE, 0666); + if(fd < 0) + sysfatal("create of %s failed: %r", srvname); + sprint(name, "%d", pfd); + if(write(fd, name, strlen(name)) < 0) + sysfatal("writing %s: %r", srvname); +} +char* +rversion(Fid*) +{ + Fid *f; + + for(f = fids; f; f = f->next) + if(f->busy) + rclunk(f); + if(thdr.msize > sizeof mdata) + rhdr.msize = sizeof mdata; + else + rhdr.msize = thdr.msize; + messagesize = rhdr.msize; + if(strncmp(thdr.version, "9P2000", 6) != 0) + return Eversion; + rhdr.version = "9P2000"; + return nil; +} + +char* +rauth(Fid*) +{ + return Enoauth; +} + +char* +rflush(Fid *f) +{ + USED(f); + return nil; +} + +char* +rattach(Fid *f) +{ + /* no authentication! */ + f->busy = 1; + if(thdr.uname[0]) + f->user = estrdup(thdr.uname); + else + f->user = "none"; + if(strcmp(user, "none") == 0) + user = f->user; + if(f->path) + s_free(f->path); + f->path = s_copy("."); + fidqid(f, &rhdr.qid); + f->attach = 1; + return nil; +} + +char* +clone(Fid *f, Fid **nf) +{ + if(f->fd >= 0) + return Eisopen; + *nf = newfid(thdr.newfid); + (*nf)->busy = 1; + if((*nf)->path) + s_free((*nf)->path); + (*nf)->path = s_clone(f->path); + (*nf)->fd = -1; + (*nf)->user = f->user; + (*nf)->attach = 0; + return nil; +} + +char* +rwalk(Fid *f) +{ + char *name; + Fid *nf; + char *err; + int i; + String *npath; + Dir *d; + char *cp; + Qid qid; + + err = nil; + nf = nil; + rhdr.nwqid = 0; + if(rhdr.newfid != rhdr.fid){ + err = clone(f, &nf); + if(err) + return err; + f = nf; /* walk the new fid */ + } + readnames(); + npath = s_clone(f->path); + if(thdr.nwname > 0){ + for(i=0; i<thdr.nwname && i<MAXWELEM; i++){ + name = long2short(thdr.wname[i], 0); + if(strcmp(name, ".") == 0){ + ; + } else if(strcmp(name, "..") == 0){ + cp = strrchr(s_to_c(npath), '/'); + if(cp != nil){ + *cp = 0; + npath->ptr = cp; + } + } else { + s_append(npath, "/"); + s_append(npath, name); + } + d = dirstat(s_to_c(npath)); + if(d == nil) + break; + rhdr.nwqid++; + qid = d->qid; + rhdr.wqid[i] = qid; + free(d); + } + if(i==0 && err == nil) + err = Enotexist; + } + + /* if there was an error and we cloned, get rid of the new fid */ + if(nf != nil && (err!=nil || rhdr.nwqid<thdr.nwname)){ + f->busy = 0; + s_free(npath); + } + + /* update the fid after a successful walk */ + if(rhdr.nwqid == thdr.nwname){ + s_free(f->path); + f->path = npath; + } + return err; +} + +static char* +passerror(void) +{ + static char err[256]; + + rerrstr(err, sizeof err); + return err; +} + +char* +ropen(Fid *f) +{ + if(readonly && (thdr.mode & 3)) + return Eperm; + if(f->fd >= 0) + return Eisopen; + f->fd = open(s_to_c(f->path), thdr.mode); + if(f->fd < 0) + return passerror(); + fidqid(f, &rhdr.qid); + f->qid = rhdr.qid; + rhdr.iounit = messagesize-IOHDRSZ; + return nil; +} + +char* +rcreate(Fid *f) +{ + char *name; + + if(readonly) + return Eperm; + readnames(); + name = long2short(thdr.name, 1); + if(f->fd >= 0) + return Eisopen; + s_append(f->path, "/"); + s_append(f->path, name); + f->fd = create(s_to_c(f->path), thdr.mode, thdr.perm); + if(f->fd < 0) + return passerror(); + fidqid(f, &rhdr.qid); + f->qid = rhdr.qid; + rhdr.iounit = messagesize-IOHDRSZ; + return nil; +} + +char* +rreaddir(Fid *f) +{ + int i; + int n; + + /* reread the directory */ + if(thdr.offset == 0){ + if(f->dir) + free(f->dir); + f->dir = nil; + if(f->diroff != 0) + seek(f->fd, 0, 0); + f->ndir = dirreadall(f->fd, &f->dir); + f->diroff = 0; + if(f->ndir < 0) + return passerror(); + readnames(); + for(i = 0; i < f->ndir; i++) + f->dir[i].name = short2long(f->dir[i].name); + } + + /* copy in as many directory entries as possible */ + for(n = 0; f->diroff < f->ndir; n += i){ + i = convD2M(&f->dir[f->diroff], rdata+n, thdr.count - n); + if(i <= BIT16SZ) + break; + f->diroff++; + } + rhdr.data = (char*)rdata; + rhdr.count = n; + return nil; +} + +char* +rread(Fid *f) +{ + long n; + + if(f->fd < 0) + return Enotexist; + if(thdr.count > messagesize-IOHDRSZ) + thdr.count = messagesize-IOHDRSZ; + if(f->qid.type & QTDIR) + return rreaddir(f); + n = pread(f->fd, rdata, thdr.count, thdr.offset); + if(n < 0) + return passerror(); + rhdr.data = (char*)rdata; + rhdr.count = n; + return nil; +} + +char* +rwrite(Fid *f) +{ + long n; + + if(readonly || (f->qid.type & QTDIR)) + return Eperm; + if(f->fd < 0) + return Enotexist; + if(thdr.count > messagesize-IOHDRSZ) /* shouldn't happen, anyway */ + thdr.count = messagesize-IOHDRSZ; + n = pwrite(f->fd, thdr.data, thdr.count, thdr.offset); + if(n < 0) + return passerror(); + rhdr.count = n; + return nil; +} + +char* +rclunk(Fid *f) +{ + f->busy = 0; + close(f->fd); + f->fd = -1; + f->path = s_reset(f->path); + if(f->attach){ + free(f->user); + f->user = nil; + } + f->attach = 0; + if(f->dir != nil){ + free(f->dir); + f->dir = nil; + } + f->diroff = f->ndir = 0; + return nil; +} + +char* +rremove(Fid *f) +{ + if(remove(s_to_c(f->path)) < 0) + return passerror(); + return nil; +} + +char* +rstat(Fid *f) +{ + int n; + Dir *d; + + d = dirstat(s_to_c(f->path)); + if(d == nil) + return passerror(); + d->name = short2long(d->name); + n = convD2M(d, statbuf, sizeof statbuf); + free(d); + if(n <= BIT16SZ) + return passerror(); + rhdr.nstat = n; + rhdr.stat = statbuf; + return nil; +} + +char* +rwstat(Fid *f) +{ + int n; + Dir d; + + if(readonly) + return Eperm; + convM2D(thdr.stat, thdr.nstat, &d, (char*)rdata); + d.name = long2short(d.name, 1); + n = dirwstat(s_to_c(f->path), &d); + if(n < 0) + return passerror(); + return nil; +} + +Fid * +newfid(int fid) +{ + Fid *f, *ff; + + ff = 0; + for(f = fids; f; f = f->next) + if(f->fid == fid) + return f; + else if(!ff && !f->busy) + ff = f; + if(ff){ + ff->fid = fid; + return ff; + } + f = emalloc(sizeof *f); + f->path = s_reset(f->path); + f->fd = -1; + f->fid = fid; + f->next = fids; + fids = f; + return f; +} + +void +io(void) +{ + char *err; + int n, pid; + + pid = getpid(); + + for(;;){ + /* + * reading from a pipe or a network device + * will give an error after a few eof reads. + * however, we cannot tell the difference + * between a zero-length read and an interrupt + * on the processes writing to us, + * so we wait for the error. + */ + n = read9pmsg(mfd[0], mdata, messagesize); + if(n < 0) + sysfatal("mount read"); + if(n == 0) + continue; + if(convM2S(mdata, n, &thdr) == 0) + continue; + + if(debug) + fprint(2, "%s %d:<-%F\n", argv0, pid, &thdr); + + if(!fcalls[thdr.type]) + err = "bad fcall type"; + else + err = (*fcalls[thdr.type])(newfid(thdr.fid)); + if(err){ + rhdr.type = Rerror; + rhdr.ename = err; + }else{ + rhdr.type = thdr.type + 1; + rhdr.fid = thdr.fid; + } + rhdr.tag = thdr.tag; + if(debug) + fprint(2, "%s %d:->%F\n", argv0, pid, &rhdr);/**/ + n = convS2M(&rhdr, mdata, messagesize); + if(n == 0) + sysfatal("convS2M error on write"); + if(write(mfd[1], mdata, n) != n) + sysfatal("mount write"); + } +} + +void * +emalloc(ulong n) +{ + void *p; + + p = malloc(n); + if(!p) + sysfatal("out of memory"); + memset(p, 0, n); + return p; +} + +void * +erealloc(void *p, ulong n) +{ + p = realloc(p, n); + if(!p) + sysfatal("out of memory"); + return p; +} + +char * +estrdup(char *q) +{ + char *p; + int n; + + n = strlen(q)+1; + p = malloc(n); + if(!p) + sysfatal("out of memory"); + memmove(p, q, n); + return p; +} + +void +fidqid(Fid *f, Qid *q) +{ + Dir *d; + + d = dirstat(s_to_c(f->path)); + if(d == nil) + *q = (Qid){0, 0, QTFILE}; + else { + *q = d->qid; + free(d); + } +} + +/* + * table of name translations + * + * the file ./.longnames contains all the known long names. + * the short name is the first NAMELEN-1 bytes of the base64 + * encoding of the MD5 hash of the longname. + */ + +typedef struct Name Name; +struct Name +{ + Name *next; + char shortname[NAMELEN]; + char *longname; +}; + +Dir *dbstat; /* last stat of the name file */ +char *namefile = "./.longnames"; +char *namebuf; +Name *names; + +Name* +newname(char *longname, int writeflag) +{ + Name *np; + int n; + uchar digest[MD5dlen]; + int fd; + + /* chain in new name */ + n = strlen(longname); + np = emalloc(sizeof(*np)+n+1); + np->longname = (char*)&np[1]; + strcpy(np->longname, longname); + md5((uchar*)longname, n, digest, nil); + enc32(np->shortname, sizeof(np->shortname), digest, MD5dlen); + np->next = names; + names = np; + + /* don't change namefile if we're read only */ + if(!writeflag) + return np; + + /* add to namefile */ + fd = open(namefile, OWRITE); + if(fd >= 0){ + seek(fd, 0, 2); + fprint(fd, "%s\n", longname); + close(fd); + } + return np; +} + +void +freenames(void) +{ + Name *np, *next; + + for(np = names; np != nil; np = next){ + next = np->next; + free(np); + } + names = nil; +} + +/* + * reread the file if the qid.path has changed. + * + * read any new entries if length has changed. + */ +void +readnames(void) +{ + Dir *d; + int fd; + vlong offset; + Biobuf *b; + char *p; + + d = dirstat(namefile); + if(d == nil){ + if(readonly) + return; + + /* create file if it doesn't exist */ + fd = create(namefile, OREAD, DMAPPEND|0666); + if(fd < 0) + return; + if(dbstat != nil) + free(dbstat); + dbstat = nil; + close(fd); + return; + } + + /* up to date? */ + offset = 0; + if(dbstat != nil){ + if(d->qid.path == dbstat->qid.path){ + if(d->length <= dbstat->length){ + free(d); + return; + } + offset = dbstat->length; + } else { + freenames(); + } + free(dbstat); + dbstat = nil; + } + + /* read file */ + b = Bopen(namefile, OREAD); + if(b == nil){ + free(d); + return; + } + Bseek(b, offset, 0); + while((p = Brdline(b, '\n')) != nil){ + p[Blinelen(b)-1] = 0; + newname(p, 0); + } + Bterm(b); + dbstat = d; +} + +/* + * look up a long name, if it doesn't exist in the + * file, add an entry to the file if the writeflag is + * non-zero. Return a pointer to the short name. + */ +char* +long2short(char *longname, int writeflag) +{ + Name *np; + + if(strlen(longname) < NAMELEN-1 && strpbrk(longname, " ")==nil) + return longname; + + for(np = names; np != nil; np = np->next) + if(strcmp(longname, np->longname) == 0) + return np->shortname; + if(!writeflag) + return longname; + np = newname(longname, !readonly); + return np->shortname; +} + +/* + * look up a short name, if it doesn't exist, return the + * longname. + */ +char* +short2long(char *shortname) +{ + Name *np; + + for(np = names; np != nil; np = np->next) + if(strcmp(shortname, np->shortname) == 0) + return np->longname; + return shortname; +} |