1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <auth.h>
5 #include <fcall.h>
6 #include <String.h>
7 #include <libsec.h>
8
9 enum
10 {
11 OPERM = 0x3, /* mask of all permission types in open mode */
12 Maxsize = 512*1024*1024,
13 Maxfdata = 8192,
14 NAMELEN = 28,
15 };
16
17 typedef struct Fid Fid;
18 struct Fid
19 {
20 short busy;
21 int fid;
22 Fid *next;
23 char *user;
24 String *path; /* complete path */
25
26 int fd; /* set on open or create */
27 Qid qid; /* set on open or create */
28 int attach; /* this is an attach fd */
29
30 ulong diroff; /* directory offset */
31 Dir *dir; /* directory entries */
32 int ndir; /* number of directory entries */
33 };
34
35 Fid *fids;
36 int mfd[2];
37 char *user;
38 uchar mdata[IOHDRSZ+Maxfdata];
39 uchar rdata[Maxfdata]; /* buffer for data in reply */
40 uchar statbuf[STATMAX];
41 Fcall thdr;
42 Fcall rhdr;
43 int messagesize = sizeof mdata;
44 int readonly;
45 char *srvname;
46 int debug;
47
48 Fid * newfid(int);
49 void io(void);
50 void *erealloc(void*, ulong);
51 void *emalloc(ulong);
52 char *estrdup(char*);
53 void usage(void);
54 void fidqid(Fid*, Qid*);
55 char* short2long(char*);
56 char* long2short(char*, int);
57 void readnames(void);
58 void post(char*, int);
59
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*);
65
66 char *(*fcalls[])(Fid*) = {
67 [Tversion] rversion,
68 [Tflush] rflush,
69 [Tauth] rauth,
70 [Tattach] rattach,
71 [Twalk] rwalk,
72 [Topen] ropen,
73 [Tcreate] rcreate,
74 [Tread] rread,
75 [Twrite] rwrite,
76 [Tclunk] rclunk,
77 [Tremove] rremove,
78 [Tstat] rstat,
79 [Twstat] rwstat,
80 };
81
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";
93 char Eversion[] = "unknown 9P version";
94
95 void
usage(void)96 usage(void)
97 {
98 fprint(2, "usage: %s [-r] [-s srvname] mountpoint\n", argv0);
99 exits("usage");
100 }
101
102 void
notifyf(void * a,char * s)103 notifyf(void *a, char *s)
104 {
105 USED(a);
106 if(strncmp(s, "interrupt", 9) == 0)
107 noted(NCONT);
108 noted(NDFLT);
109 }
110
111 void
main(int argc,char * argv[])112 main(int argc, char *argv[])
113 {
114 char *defmnt;
115 int p[2];
116 Dir *d;
117
118 ARGBEGIN{
119 case 'r':
120 readonly = 1;
121 break;
122 case 'd':
123 debug = 1;
124 break;
125 case 's':
126 srvname = ARGF();
127 if(srvname == nil)
128 usage();
129 break;
130 default:
131 usage();
132 }ARGEND
133
134 if(argc < 1)
135 usage();
136 defmnt = argv[0];
137 d = dirstat(defmnt);
138 if(d == nil || !(d->qid.type & QTDIR))
139 sysfatal("mountpoint must be an accessible directory");
140 free(d);
141
142 if(pipe(p) < 0)
143 sysfatal("pipe failed");
144 mfd[0] = p[0];
145 mfd[1] = p[0];
146
147 user = getuser();
148 notify(notifyf);
149
150 if(srvname != nil)
151 post(srvname, p[1]);
152
153 if(debug)
154 fmtinstall('F', fcallfmt);
155 switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
156 case -1:
157 sysfatal("fork: %r");
158 case 0:
159 close(p[1]);
160 chdir(defmnt);
161 io();
162 break;
163 default:
164 close(p[0]); /* don't deadlock if child fails */
165 if(mount(p[1], -1, defmnt, MREPL|MCREATE, "") < 0)
166 sysfatal("mount failed: %r");
167 }
168 exits(0);
169 }
170
171 void
post(char * srvname,int pfd)172 post(char *srvname, int pfd)
173 {
174 char name[128];
175 int fd;
176
177 snprint(name, sizeof name, "#s/%s", srvname);
178 fd = create(name, OWRITE, 0666);
179 if(fd < 0)
180 sysfatal("create of %s failed: %r", srvname);
181 sprint(name, "%d", pfd);
182 if(write(fd, name, strlen(name)) < 0)
183 sysfatal("writing %s: %r", srvname);
184 }
185 char*
rversion(Fid *)186 rversion(Fid*)
187 {
188 Fid *f;
189
190 for(f = fids; f; f = f->next)
191 if(f->busy)
192 rclunk(f);
193 if(thdr.msize > sizeof mdata)
194 rhdr.msize = sizeof mdata;
195 else
196 rhdr.msize = thdr.msize;
197 messagesize = rhdr.msize;
198 if(strncmp(thdr.version, "9P2000", 6) != 0)
199 return Eversion;
200 rhdr.version = "9P2000";
201 return nil;
202 }
203
204 char*
rauth(Fid *)205 rauth(Fid*)
206 {
207 return Enoauth;
208 }
209
210 char*
rflush(Fid * f)211 rflush(Fid *f)
212 {
213 USED(f);
214 return nil;
215 }
216
217 char*
rattach(Fid * f)218 rattach(Fid *f)
219 {
220 /* no authentication! */
221 f->busy = 1;
222 if(thdr.uname[0])
223 f->user = estrdup(thdr.uname);
224 else
225 f->user = "none";
226 if(strcmp(user, "none") == 0)
227 user = f->user;
228 if(f->path)
229 s_free(f->path);
230 f->path = s_copy(".");
231 fidqid(f, &rhdr.qid);
232 f->attach = 1;
233 return nil;
234 }
235
236 char*
clone(Fid * f,Fid ** nf)237 clone(Fid *f, Fid **nf)
238 {
239 if(f->fd >= 0)
240 return Eisopen;
241 *nf = newfid(thdr.newfid);
242 (*nf)->busy = 1;
243 if((*nf)->path)
244 s_free((*nf)->path);
245 (*nf)->path = s_clone(f->path);
246 (*nf)->fd = -1;
247 (*nf)->user = f->user;
248 (*nf)->attach = 0;
249 return nil;
250 }
251
252 char*
rwalk(Fid * f)253 rwalk(Fid *f)
254 {
255 char *name;
256 Fid *nf;
257 char *err;
258 int i;
259 String *npath;
260 Dir *d;
261 char *cp;
262 Qid qid;
263
264 err = nil;
265 nf = nil;
266 rhdr.nwqid = 0;
267 if(rhdr.newfid != rhdr.fid){
268 err = clone(f, &nf);
269 if(err)
270 return err;
271 f = nf; /* walk the new fid */
272 }
273 readnames();
274 npath = s_clone(f->path);
275 if(thdr.nwname > 0){
276 for(i=0; i<thdr.nwname && i<MAXWELEM; i++){
277 name = long2short(thdr.wname[i], 0);
278 if(strcmp(name, ".") == 0){
279 ;
280 } else if(strcmp(name, "..") == 0){
281 cp = strrchr(s_to_c(npath), '/');
282 if(cp != nil){
283 *cp = 0;
284 npath->ptr = cp;
285 }
286 } else {
287 s_append(npath, "/");
288 s_append(npath, name);
289 }
290 d = dirstat(s_to_c(npath));
291 if(d == nil)
292 break;
293 rhdr.nwqid++;
294 qid = d->qid;
295 rhdr.wqid[i] = qid;
296 free(d);
297 }
298 if(i==0 && err == nil)
299 err = Enotexist;
300 }
301
302 /* if there was an error and we cloned, get rid of the new fid */
303 if(nf != nil && (err!=nil || rhdr.nwqid<thdr.nwname)){
304 f->busy = 0;
305 s_free(npath);
306 }
307
308 /* update the fid after a successful walk */
309 if(rhdr.nwqid == thdr.nwname){
310 s_free(f->path);
311 f->path = npath;
312 }
313 return err;
314 }
315
316 static char*
passerror(void)317 passerror(void)
318 {
319 static char err[256];
320
321 rerrstr(err, sizeof err);
322 return err;
323 }
324
325 char*
ropen(Fid * f)326 ropen(Fid *f)
327 {
328 if(readonly && (thdr.mode & 3))
329 return Eperm;
330 if(f->fd >= 0)
331 return Eisopen;
332 f->fd = open(s_to_c(f->path), thdr.mode);
333 if(f->fd < 0)
334 return passerror();
335 fidqid(f, &rhdr.qid);
336 f->qid = rhdr.qid;
337 rhdr.iounit = messagesize-IOHDRSZ;
338 return nil;
339 }
340
341 char*
rcreate(Fid * f)342 rcreate(Fid *f)
343 {
344 char *name;
345
346 if(readonly)
347 return Eperm;
348 readnames();
349 name = long2short(thdr.name, 1);
350 if(f->fd >= 0)
351 return Eisopen;
352 s_append(f->path, "/");
353 s_append(f->path, name);
354 f->fd = create(s_to_c(f->path), thdr.mode, thdr.perm);
355 if(f->fd < 0)
356 return passerror();
357 fidqid(f, &rhdr.qid);
358 f->qid = rhdr.qid;
359 rhdr.iounit = messagesize-IOHDRSZ;
360 return nil;
361 }
362
363 char*
rreaddir(Fid * f)364 rreaddir(Fid *f)
365 {
366 int i;
367 int n;
368
369 /* reread the directory */
370 if(thdr.offset == 0){
371 if(f->dir)
372 free(f->dir);
373 f->dir = nil;
374 if(f->diroff != 0)
375 seek(f->fd, 0, 0);
376 f->ndir = dirreadall(f->fd, &f->dir);
377 f->diroff = 0;
378 if(f->ndir < 0)
379 return passerror();
380 readnames();
381 for(i = 0; i < f->ndir; i++)
382 f->dir[i].name = short2long(f->dir[i].name);
383 }
384
385 /* copy in as many directory entries as possible */
386 for(n = 0; f->diroff < f->ndir; n += i){
387 i = convD2M(&f->dir[f->diroff], rdata+n, thdr.count - n);
388 if(i <= BIT16SZ)
389 break;
390 f->diroff++;
391 }
392 rhdr.data = (char*)rdata;
393 rhdr.count = n;
394 return nil;
395 }
396
397 char*
rread(Fid * f)398 rread(Fid *f)
399 {
400 long n;
401
402 if(f->fd < 0)
403 return Enotexist;
404 if(thdr.count > messagesize-IOHDRSZ)
405 thdr.count = messagesize-IOHDRSZ;
406 if(f->qid.type & QTDIR)
407 return rreaddir(f);
408 n = pread(f->fd, rdata, thdr.count, thdr.offset);
409 if(n < 0)
410 return passerror();
411 rhdr.data = (char*)rdata;
412 rhdr.count = n;
413 return nil;
414 }
415
416 char*
rwrite(Fid * f)417 rwrite(Fid *f)
418 {
419 long n;
420
421 if(readonly || (f->qid.type & QTDIR))
422 return Eperm;
423 if(f->fd < 0)
424 return Enotexist;
425 if(thdr.count > messagesize-IOHDRSZ) /* shouldn't happen, anyway */
426 thdr.count = messagesize-IOHDRSZ;
427 n = pwrite(f->fd, thdr.data, thdr.count, thdr.offset);
428 if(n < 0)
429 return passerror();
430 rhdr.count = n;
431 return nil;
432 }
433
434 char*
rclunk(Fid * f)435 rclunk(Fid *f)
436 {
437 f->busy = 0;
438 close(f->fd);
439 f->fd = -1;
440 f->path = s_reset(f->path);
441 if(f->attach){
442 free(f->user);
443 f->user = nil;
444 }
445 f->attach = 0;
446 if(f->dir != nil){
447 free(f->dir);
448 f->dir = nil;
449 }
450 f->diroff = f->ndir = 0;
451 return nil;
452 }
453
454 char*
rremove(Fid * f)455 rremove(Fid *f)
456 {
457 if(remove(s_to_c(f->path)) < 0)
458 return passerror();
459 return nil;
460 }
461
462 char*
rstat(Fid * f)463 rstat(Fid *f)
464 {
465 int n;
466 Dir *d;
467
468 d = dirstat(s_to_c(f->path));
469 if(d == nil)
470 return passerror();
471 d->name = short2long(d->name);
472 n = convD2M(d, statbuf, sizeof statbuf);
473 free(d);
474 if(n <= BIT16SZ)
475 return passerror();
476 rhdr.nstat = n;
477 rhdr.stat = statbuf;
478 return nil;
479 }
480
481 char*
rwstat(Fid * f)482 rwstat(Fid *f)
483 {
484 int n;
485 Dir d;
486
487 if(readonly)
488 return Eperm;
489 convM2D(thdr.stat, thdr.nstat, &d, (char*)rdata);
490 d.name = long2short(d.name, 1);
491 n = dirwstat(s_to_c(f->path), &d);
492 if(n < 0)
493 return passerror();
494 return nil;
495 }
496
497 Fid *
newfid(int fid)498 newfid(int fid)
499 {
500 Fid *f, *ff;
501
502 ff = 0;
503 for(f = fids; f; f = f->next)
504 if(f->fid == fid)
505 return f;
506 else if(!ff && !f->busy)
507 ff = f;
508 if(ff){
509 ff->fid = fid;
510 return ff;
511 }
512 f = emalloc(sizeof *f);
513 f->path = s_reset(f->path);
514 f->fd = -1;
515 f->fid = fid;
516 f->next = fids;
517 fids = f;
518 return f;
519 }
520
521 void
io(void)522 io(void)
523 {
524 char *err;
525 int n, pid;
526
527 pid = getpid();
528
529 for(;;){
530 /*
531 * reading from a pipe or a network device
532 * will give an error after a few eof reads.
533 * however, we cannot tell the difference
534 * between a zero-length read and an interrupt
535 * on the processes writing to us,
536 * so we wait for the error.
537 */
538 n = read9pmsg(mfd[0], mdata, messagesize);
539 if(n < 0)
540 sysfatal("mount read");
541 if(n == 0)
542 continue;
543 if(convM2S(mdata, n, &thdr) == 0)
544 continue;
545
546 if(debug)
547 fprint(2, "%s %d:<-%F\n", argv0, pid, &thdr);
548
549 if(!fcalls[thdr.type])
550 err = "bad fcall type";
551 else
552 err = (*fcalls[thdr.type])(newfid(thdr.fid));
553 if(err){
554 rhdr.type = Rerror;
555 rhdr.ename = err;
556 }else{
557 rhdr.type = thdr.type + 1;
558 rhdr.fid = thdr.fid;
559 }
560 rhdr.tag = thdr.tag;
561 if(debug)
562 fprint(2, "%s %d:->%F\n", argv0, pid, &rhdr);/**/
563 n = convS2M(&rhdr, mdata, messagesize);
564 if(n == 0)
565 sysfatal("convS2M error on write");
566 if(write(mfd[1], mdata, n) != n)
567 sysfatal("mount write");
568 }
569 }
570
571 void *
emalloc(ulong n)572 emalloc(ulong n)
573 {
574 void *p;
575
576 p = malloc(n);
577 if(!p)
578 sysfatal("out of memory");
579 memset(p, 0, n);
580 return p;
581 }
582
583 void *
erealloc(void * p,ulong n)584 erealloc(void *p, ulong n)
585 {
586 p = realloc(p, n);
587 if(!p)
588 sysfatal("out of memory");
589 return p;
590 }
591
592 char *
estrdup(char * q)593 estrdup(char *q)
594 {
595 char *p;
596 int n;
597
598 n = strlen(q)+1;
599 p = malloc(n);
600 if(!p)
601 sysfatal("out of memory");
602 memmove(p, q, n);
603 return p;
604 }
605
606 void
fidqid(Fid * f,Qid * q)607 fidqid(Fid *f, Qid *q)
608 {
609 Dir *d;
610
611 d = dirstat(s_to_c(f->path));
612 if(d == nil)
613 *q = (Qid){0, 0, QTFILE};
614 else {
615 *q = d->qid;
616 free(d);
617 }
618 }
619
620 /*
621 * table of name translations
622 *
623 * the file ./.longnames contains all the known long names.
624 * the short name is the first NAMELEN-1 bytes of the base64
625 * encoding of the MD5 hash of the longname.
626 */
627
628 typedef struct Name Name;
629 struct Name
630 {
631 Name *next;
632 char shortname[NAMELEN];
633 char *longname;
634 };
635
636 Dir *dbstat; /* last stat of the name file */
637 char *namefile = "./.longnames";
638 char *namebuf;
639 Name *names;
640
641 Name*
newname(char * longname,int writeflag)642 newname(char *longname, int writeflag)
643 {
644 Name *np;
645 int n;
646 uchar digest[MD5dlen];
647 int fd;
648
649 /* chain in new name */
650 n = strlen(longname);
651 np = emalloc(sizeof(*np)+n+1);
652 np->longname = (char*)&np[1];
653 strcpy(np->longname, longname);
654 md5((uchar*)longname, n, digest, nil);
655 enc32(np->shortname, sizeof(np->shortname), digest, MD5dlen);
656 np->next = names;
657 names = np;
658
659 /* don't change namefile if we're read only */
660 if(!writeflag)
661 return np;
662
663 /* add to namefile */
664 fd = open(namefile, OWRITE);
665 if(fd >= 0){
666 seek(fd, 0, 2);
667 fprint(fd, "%s\n", longname);
668 close(fd);
669 }
670 return np;
671 }
672
673 void
freenames(void)674 freenames(void)
675 {
676 Name *np, *next;
677
678 for(np = names; np != nil; np = next){
679 next = np->next;
680 free(np);
681 }
682 names = nil;
683 }
684
685 /*
686 * reread the file if the qid.path has changed.
687 *
688 * read any new entries if length has changed.
689 */
690 void
readnames(void)691 readnames(void)
692 {
693 Dir *d;
694 int fd;
695 vlong offset;
696 Biobuf *b;
697 char *p;
698
699 d = dirstat(namefile);
700 if(d == nil){
701 if(readonly)
702 return;
703
704 /* create file if it doesn't exist */
705 fd = create(namefile, OREAD, DMAPPEND|0666);
706 if(fd < 0)
707 return;
708 if(dbstat != nil)
709 free(dbstat);
710 dbstat = nil;
711 close(fd);
712 return;
713 }
714
715 /* up to date? */
716 offset = 0;
717 if(dbstat != nil){
718 if(d->qid.path == dbstat->qid.path){
719 if(d->length <= dbstat->length){
720 free(d);
721 return;
722 }
723 offset = dbstat->length;
724 } else {
725 freenames();
726 }
727 free(dbstat);
728 dbstat = nil;
729 }
730
731 /* read file */
732 b = Bopen(namefile, OREAD);
733 if(b == nil){
734 free(d);
735 return;
736 }
737 Bseek(b, offset, 0);
738 while((p = Brdline(b, '\n')) != nil){
739 p[Blinelen(b)-1] = 0;
740 newname(p, 0);
741 }
742 Bterm(b);
743 dbstat = d;
744 }
745
746 /*
747 * look up a long name, if it doesn't exist in the
748 * file, add an entry to the file if the writeflag is
749 * non-zero. Return a pointer to the short name.
750 */
751 char*
long2short(char * longname,int writeflag)752 long2short(char *longname, int writeflag)
753 {
754 Name *np;
755
756 if(strlen(longname) < NAMELEN-1 && strpbrk(longname, " ")==nil)
757 return longname;
758
759 for(np = names; np != nil; np = np->next)
760 if(strcmp(longname, np->longname) == 0)
761 return np->shortname;
762 if(!writeflag)
763 return longname;
764 np = newname(longname, !readonly);
765 return np->shortname;
766 }
767
768 /*
769 * look up a short name, if it doesn't exist, return the
770 * longname.
771 */
772 char*
short2long(char * shortname)773 short2long(char *shortname)
774 {
775 Name *np;
776
777 for(np = names; np != nil; np = np->next)
778 if(strcmp(shortname, np->shortname) == 0)
779 return np->longname;
780 return shortname;
781 }
782