]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/lnfs.c
upas/fs: fix more locking bugs, remove debugging clutter, remove planb mbox code
[plan9front.git] / sys / src / cmd / lnfs.c
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
96 usage(void)
97 {
98         fprint(2, "usage: %s [-r] [-s srvname] mountpoint\n", argv0);
99         exits("usage");
100 }
101
102 void
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
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
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*
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*
205 rauth(Fid*)
206 {
207         return Enoauth;
208 }
209
210 char*
211 rflush(Fid *f)
212 {
213         USED(f);
214         return nil;
215 }
216
217 char*
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*
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*
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*
317 passerror(void)
318 {
319         static char err[256];
320
321         rerrstr(err, sizeof err);
322         return err;
323 }
324
325 char*
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*
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*
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*
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*
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*
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*
455 rremove(Fid *f)
456 {
457         if(remove(s_to_c(f->path)) < 0)
458                 return passerror();
459         return nil;
460 }
461
462 char*
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*
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 *
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
522 io(void)
523 {
524         char *err;
525         int n, pid;
526
527         pid = getpid();
528
529         while((n = read9pmsg(mfd[0], mdata, messagesize)) != 0){
530                 if(n < 0)
531                         sysfatal("mount read: %r");
532                 if(convM2S(mdata, n, &thdr) != n)
533                         sysfatal("convM2S format error: %r");
534
535                 if(debug)
536                         fprint(2, "%s %d:<-%F\n", argv0, pid, &thdr);
537
538                 if(!fcalls[thdr.type])
539                         err = "bad fcall type";
540                 else
541                         err = (*fcalls[thdr.type])(newfid(thdr.fid));
542                 if(err){
543                         rhdr.type = Rerror;
544                         rhdr.ename = err;
545                 }else{
546                         rhdr.type = thdr.type + 1;
547                         rhdr.fid = thdr.fid;
548                 }
549                 rhdr.tag = thdr.tag;
550                 if(debug)
551                         fprint(2, "%s %d:->%F\n", argv0, pid, &rhdr);/**/
552                 n = convS2M(&rhdr, mdata, messagesize);
553                 if(n == 0)
554                         sysfatal("convS2M error on write");
555                 if(write(mfd[1], mdata, n) != n)
556                         sysfatal("mount write");
557         }
558 }
559
560 void *
561 emalloc(ulong n)
562 {
563         void *p;
564
565         p = malloc(n);
566         if(!p)
567                 sysfatal("out of memory");
568         memset(p, 0, n);
569         return p;
570 }
571
572 void *
573 erealloc(void *p, ulong n)
574 {
575         p = realloc(p, n);
576         if(!p)
577                 sysfatal("out of memory");
578         return p;
579 }
580
581 char *
582 estrdup(char *q)
583 {
584         char *p;
585         int n;
586
587         n = strlen(q)+1;
588         p = malloc(n);
589         if(!p)
590                 sysfatal("out of memory");
591         memmove(p, q, n);
592         return p;
593 }
594
595 void
596 fidqid(Fid *f, Qid *q)
597 {
598         Dir *d;
599
600         d = dirstat(s_to_c(f->path));
601         if(d == nil)
602                 *q = (Qid){0, 0, QTFILE};
603         else {
604                 *q = d->qid;
605                 free(d);
606         }
607 }
608
609 /*
610  *  table of name translations
611  *
612  *  the file ./.longnames contains all the known long names.
613  *  the short name is the first NAMELEN-1 bytes of the base64
614  *  encoding of the MD5 hash of the longname.
615  */
616
617 typedef struct Name Name;
618 struct Name
619 {
620         Name    *next;
621         char    shortname[NAMELEN];
622         char    *longname;
623 };
624
625 Dir *dbstat;    /* last stat of the name file */
626 char *namefile = "./.longnames";
627 char *namebuf;
628 Name *names;
629
630 Name*
631 newname(char *longname, int writeflag)
632 {
633         Name *np;
634         int n;
635         uchar digest[MD5dlen];
636         int fd;
637
638         /* chain in new name */
639         n = strlen(longname);
640         np = emalloc(sizeof(*np)+n+1);
641         np->longname = (char*)&np[1];
642         strcpy(np->longname, longname);
643         md5((uchar*)longname, n, digest, nil);
644         enc32(np->shortname, sizeof(np->shortname), digest, MD5dlen);
645         np->next = names;
646         names = np;
647
648         /* don't change namefile if we're read only */
649         if(!writeflag)
650                 return np;
651
652         /* add to namefile */
653         fd = open(namefile, OWRITE);
654         if(fd >= 0){
655                 seek(fd, 0, 2);
656                 fprint(fd, "%s\n", longname);
657                 close(fd);
658         }
659         return np;
660 }
661
662 void
663 freenames(void)
664 {
665         Name *np, *next;
666
667         for(np = names; np != nil; np = next){
668                 next = np->next;
669                 free(np);
670         }
671         names = nil;
672 }
673
674 /*
675  *  reread the file if the qid.path has changed.
676  *
677  *  read any new entries if length has changed.
678  */
679 void
680 readnames(void)
681 {
682         Dir *d;
683         int fd;
684         vlong offset;
685         Biobuf *b;
686         char *p;
687
688         d = dirstat(namefile);
689         if(d == nil){
690                 if(readonly)
691                         return;
692
693                 /* create file if it doesn't exist */
694                 fd = create(namefile, OREAD, DMAPPEND|0666);
695                 if(fd < 0)
696                         return;
697                 if(dbstat != nil)
698                         free(dbstat);
699                 dbstat = nil;
700                 close(fd);
701                 return;
702         }
703
704         /* up to date? */
705         offset = 0;
706         if(dbstat != nil){
707                 if(d->qid.path == dbstat->qid.path){
708                         if(d->length <= dbstat->length){
709                                 free(d);
710                                 return;
711                         }
712                         offset = dbstat->length;
713                 } else {
714                         freenames();
715                 }
716                 free(dbstat);
717                 dbstat = nil;
718         }
719
720         /* read file */
721         b = Bopen(namefile, OREAD);
722         if(b == nil){
723                 free(d);
724                 return;
725         }
726         Bseek(b, offset, 0);
727         while((p = Brdline(b, '\n')) != nil){
728                 p[Blinelen(b)-1] = 0;
729                 newname(p, 0);
730         }
731         Bterm(b);
732         dbstat = d;
733 }
734
735 /*
736  *  look up a long name,  if it doesn't exist in the
737  *  file, add an entry to the file if the writeflag is
738  *  non-zero.  Return a pointer to the short name.
739  */
740 char*
741 long2short(char *longname, int writeflag)
742 {
743         Name *np;
744
745         if(strlen(longname) < NAMELEN-1 && strpbrk(longname, " ")==nil)
746                 return longname;
747
748         for(np = names; np != nil; np = np->next)
749                 if(strcmp(longname, np->longname) == 0)
750                         return np->shortname;
751         if(!writeflag)
752                 return longname;
753         np = newname(longname, !readonly);
754         return np->shortname;
755 }
756
757 /*
758  *  look up a short name, if it doesn't exist, return the
759  *  longname.
760  */
761 char*
762 short2long(char *shortname)
763 {
764         Name *np;
765
766         for(np = names; np != nil; np = np->next)
767                 if(strcmp(shortname, np->shortname) == 0)
768                         return np->longname;
769         return shortname;
770 }