123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781 |
- #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;
- }
|