11 OPERM = 0x3, /* mask of all permission types in open mode */
12 Maxsize = 512*1024*1024,
17 typedef struct Fid Fid;
24 String *path; /* complete path */
26 int fd; /* set on open or create */
27 Qid qid; /* set on open or create */
28 int attach; /* this is an attach fd */
30 ulong diroff; /* directory offset */
31 Dir *dir; /* directory entries */
32 int ndir; /* number of directory entries */
38 uchar mdata[IOHDRSZ+Maxfdata];
39 uchar rdata[Maxfdata]; /* buffer for data in reply */
40 uchar statbuf[STATMAX];
43 int messagesize = sizeof mdata;
50 void *erealloc(void*, ulong);
54 void fidqid(Fid*, Qid*);
55 char* short2long(char*);
56 char* long2short(char*, int);
58 void post(char*, int);
60 char *rflush(Fid*), *rversion(Fid*), *rauth(Fid*),
61 *rattach(Fid*), *rwalk(Fid*),
62 *ropen(Fid*), *rcreate(Fid*),
63 *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
64 *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
66 char *(*fcalls[])(Fid*) = {
82 char Eperm[] = "permission denied";
83 char Enotdir[] = "not a directory";
84 char Enoauth[] = "lnfs: authentication not required";
85 char Enotexist[] = "file does not exist";
86 char Einuse[] = "file in use";
87 char Eexist[] = "file exists";
88 char Eisdir[] = "file is a directory";
89 char Enotowner[] = "not owner";
90 char Eisopen[] = "file already open for I/O";
91 char Excl[] = "exclusive use file already open";
92 char Ename[] = "illegal name";
97 fprint(2, "usage: %s [-r] [-s srvname] mountpoint\n", argv0);
102 notifyf(void *a, char *s)
105 if(strncmp(s, "interrupt", 9) == 0)
111 main(int argc, char *argv[])
137 if(d == nil || !(d->qid.type & QTDIR))
138 sysfatal("mountpoint must be an accessible directory");
142 sysfatal("pipe failed");
153 fmtinstall('F', fcallfmt);
154 switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
156 sysfatal("fork: %r");
163 close(p[0]); /* don't deadlock if child fails */
164 if(mount(p[1], -1, defmnt, MREPL|MCREATE, "") == -1)
165 sysfatal("mount failed: %r");
171 post(char *srvname, int pfd)
176 snprint(name, sizeof name, "#s/%s", srvname);
177 fd = create(name, OWRITE, 0666);
179 sysfatal("create of %s failed: %r", srvname);
180 sprint(name, "%d", pfd);
181 if(write(fd, name, strlen(name)) < 0)
182 sysfatal("writing %s: %r", srvname);
189 for(f = fids; f; f = f->next)
192 if(thdr.msize > sizeof mdata)
193 rhdr.msize = sizeof mdata;
195 rhdr.msize = thdr.msize;
196 messagesize = rhdr.msize;
197 rhdr.version = "9P2000";
198 if(strncmp(thdr.version, "9P", 2) != 0)
199 rhdr.version = "unknown";
219 /* no authentication! */
222 f->user = estrdup(thdr.uname);
225 if(strcmp(user, "none") == 0)
229 f->path = s_copy(".");
230 fidqid(f, &rhdr.qid);
236 clone(Fid *f, Fid **nf)
240 *nf = newfid(thdr.newfid);
244 (*nf)->path = s_clone(f->path);
246 (*nf)->user = f->user;
266 if(rhdr.newfid != rhdr.fid){
270 f = nf; /* walk the new fid */
273 npath = s_clone(f->path);
275 for(i=0; i<thdr.nwname && i<MAXWELEM; i++){
276 name = long2short(thdr.wname[i], 0);
277 if(strcmp(name, ".") == 0){
279 } else if(strcmp(name, "..") == 0){
280 cp = strrchr(s_to_c(npath), '/');
286 s_append(npath, "/");
287 s_append(npath, name);
289 d = dirstat(s_to_c(npath));
297 if(i==0 && err == nil)
301 /* if there was an error and we cloned, get rid of the new fid */
302 if(nf != nil && (err!=nil || rhdr.nwqid<thdr.nwname)){
307 /* update the fid after a successful walk */
308 if(rhdr.nwqid == thdr.nwname){
318 static char err[256];
320 rerrstr(err, sizeof err);
327 if(readonly && (thdr.mode & 3))
331 f->fd = open(s_to_c(f->path), thdr.mode);
334 fidqid(f, &rhdr.qid);
336 rhdr.iounit = messagesize-IOHDRSZ;
348 name = long2short(thdr.name, 1);
351 s_append(f->path, "/");
352 s_append(f->path, name);
353 f->fd = create(s_to_c(f->path), thdr.mode, thdr.perm);
356 fidqid(f, &rhdr.qid);
358 rhdr.iounit = messagesize-IOHDRSZ;
368 /* reread the directory */
369 if(thdr.offset == 0){
375 f->ndir = dirreadall(f->fd, &f->dir);
380 for(i = 0; i < f->ndir; i++)
381 f->dir[i].name = short2long(f->dir[i].name);
384 /* copy in as many directory entries as possible */
385 for(n = 0; f->diroff < f->ndir; n += i){
386 i = convD2M(&f->dir[f->diroff], rdata+n, thdr.count - n);
391 rhdr.data = (char*)rdata;
403 if(thdr.count > messagesize-IOHDRSZ)
404 thdr.count = messagesize-IOHDRSZ;
405 if(f->qid.type & QTDIR)
407 n = pread(f->fd, rdata, thdr.count, thdr.offset);
410 rhdr.data = (char*)rdata;
420 if(readonly || (f->qid.type & QTDIR))
424 if(thdr.count > messagesize-IOHDRSZ) /* shouldn't happen, anyway */
425 thdr.count = messagesize-IOHDRSZ;
426 n = pwrite(f->fd, thdr.data, thdr.count, thdr.offset);
439 f->path = s_reset(f->path);
449 f->diroff = f->ndir = 0;
456 if(remove(s_to_c(f->path)) < 0)
467 d = dirstat(s_to_c(f->path));
470 d->name = short2long(d->name);
471 n = convD2M(d, statbuf, sizeof statbuf);
488 convM2D(thdr.stat, thdr.nstat, &d, (char*)rdata);
489 d.name = long2short(d.name, 1);
490 n = dirwstat(s_to_c(f->path), &d);
502 for(f = fids; f; f = f->next)
505 else if(!ff && !f->busy)
511 f = emalloc(sizeof *f);
512 f->path = s_reset(f->path);
528 while((n = read9pmsg(mfd[0], mdata, messagesize)) != 0){
530 sysfatal("mount read: %r");
531 if(convM2S(mdata, n, &thdr) != n)
532 sysfatal("convM2S format error: %r");
535 fprint(2, "%s %d:<-%F\n", argv0, pid, &thdr);
537 if(!fcalls[thdr.type])
538 err = "bad fcall type";
540 err = (*fcalls[thdr.type])(newfid(thdr.fid));
545 rhdr.type = thdr.type + 1;
550 fprint(2, "%s %d:->%F\n", argv0, pid, &rhdr);/**/
551 n = convS2M(&rhdr, mdata, messagesize);
553 sysfatal("convS2M error on write");
554 if(write(mfd[1], mdata, n) != n)
555 sysfatal("mount write");
566 sysfatal("out of memory");
572 erealloc(void *p, ulong n)
576 sysfatal("out of memory");
589 sysfatal("out of memory");
595 fidqid(Fid *f, Qid *q)
599 d = dirstat(s_to_c(f->path));
601 *q = (Qid){0, 0, QTFILE};
609 * table of name translations
611 * the file ./.longnames contains all the known long names.
612 * the short name is the first NAMELEN-1 bytes of the base64
613 * encoding of the MD5 hash of the longname.
616 typedef struct Name Name;
620 char shortname[NAMELEN];
624 Dir *dbstat; /* last stat of the name file */
625 char *namefile = "./.longnames";
630 newname(char *longname, int writeflag)
634 uchar digest[MD5dlen];
637 /* chain in new name */
638 n = strlen(longname);
639 np = emalloc(sizeof(*np)+n+1);
640 np->longname = (char*)&np[1];
641 strcpy(np->longname, longname);
642 md5((uchar*)longname, n, digest, nil);
643 enc32(np->shortname, sizeof(np->shortname), digest, MD5dlen);
647 /* don't change namefile if we're read only */
651 /* add to namefile */
652 fd = open(namefile, OWRITE);
655 fprint(fd, "%s\n", longname);
666 for(np = names; np != nil; np = next){
674 * reread the file if the qid.path has changed.
676 * read any new entries if length has changed.
687 d = dirstat(namefile);
692 /* create file if it doesn't exist */
693 fd = create(namefile, OREAD, DMAPPEND|0666);
706 if(d->qid.path == dbstat->qid.path){
707 if(d->length <= dbstat->length){
711 offset = dbstat->length;
720 b = Bopen(namefile, OREAD);
726 while((p = Brdline(b, '\n')) != nil){
727 p[Blinelen(b)-1] = 0;
735 * look up a long name, if it doesn't exist in the
736 * file, add an entry to the file if the writeflag is
737 * non-zero. Return a pointer to the short name.
740 long2short(char *longname, int writeflag)
744 if(strlen(longname) < NAMELEN-1 && strpbrk(longname, " ")==nil)
747 for(np = names; np != nil; np = np->next)
748 if(strcmp(longname, np->longname) == 0)
749 return np->shortname;
752 np = newname(longname, !readonly);
753 return np->shortname;
757 * look up a short name, if it doesn't exist, return the
761 short2long(char *shortname)
765 for(np = names; np != nil; np = np->next)
766 if(strcmp(shortname, np->shortname) == 0)