]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/lnfs.c
9bootfat: rename open() to fileinit and make it static as its really a internal funct...
[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         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 *
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 *
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 *
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
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*
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
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
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*
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*
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 }