]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/lnfs.c
rio: open /dev/snarf with OCEXEC flag when writing
[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
94 void
95 usage(void)
96 {
97         fprint(2, "usage: %s [-r] [-s srvname] mountpoint\n", argv0);
98         exits("usage");
99 }
100
101 void
102 notifyf(void *a, char *s)
103 {
104         USED(a);
105         if(strncmp(s, "interrupt", 9) == 0)
106                 noted(NCONT);
107         noted(NDFLT);
108 }
109
110 void
111 main(int argc, char *argv[])
112 {
113         char *defmnt;
114         int p[2];
115         Dir *d;
116
117         ARGBEGIN{
118         case 'r':
119                 readonly = 1;
120                 break;
121         case 'd':
122                 debug = 1;
123                 break;
124         case 's':
125                 srvname = ARGF();
126                 if(srvname == nil)
127                         usage();
128                 break;
129         default:
130                 usage();
131         }ARGEND
132
133         if(argc < 1)
134                 usage();
135         defmnt = argv[0];
136         d = dirstat(defmnt);
137         if(d == nil || !(d->qid.type & QTDIR))
138                 sysfatal("mountpoint must be an accessible directory");
139         free(d);
140
141         if(pipe(p) < 0)
142                 sysfatal("pipe failed");
143         mfd[0] = p[0];
144         mfd[1] = p[0];
145
146         user = getuser();
147         notify(notifyf);
148
149         if(srvname != nil)
150                 post(srvname, p[1]);
151
152         if(debug)
153                 fmtinstall('F', fcallfmt);
154         switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
155         case -1:
156                 sysfatal("fork: %r");
157         case 0:
158                 close(p[1]);
159                 chdir(defmnt);
160                 io();
161                 break;
162         default:
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");
166         }
167         exits(0);
168 }
169
170 void
171 post(char *srvname, int pfd)
172 {
173         char name[128];
174         int fd;
175
176         snprint(name, sizeof name, "#s/%s", srvname);
177         fd = create(name, OWRITE, 0666);
178         if(fd < 0)
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);
183 }
184 char*
185 rversion(Fid*)
186 {
187         Fid *f;
188
189         for(f = fids; f; f = f->next)
190                 if(f->busy)
191                         rclunk(f);
192         if(thdr.msize > sizeof mdata)
193                 rhdr.msize = sizeof mdata;
194         else
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";
200         return nil;
201 }
202
203 char*
204 rauth(Fid*)
205 {
206         return Enoauth;
207 }
208
209 char*
210 rflush(Fid *f)
211 {
212         USED(f);
213         return nil;
214 }
215
216 char*
217 rattach(Fid *f)
218 {
219         /* no authentication! */
220         f->busy = 1;
221         if(thdr.uname[0])
222                 f->user = estrdup(thdr.uname);
223         else
224                 f->user = "none";
225         if(strcmp(user, "none") == 0)
226                 user = f->user;
227         if(f->path)
228                 s_free(f->path);
229         f->path = s_copy(".");
230         fidqid(f, &rhdr.qid);
231         f->attach = 1;
232         return nil;
233 }
234
235 char*
236 clone(Fid *f, Fid **nf)
237 {
238         if(f->fd >= 0)
239                 return Eisopen;
240         *nf = newfid(thdr.newfid);
241         (*nf)->busy = 1;
242         if((*nf)->path)
243                 s_free((*nf)->path);
244         (*nf)->path = s_clone(f->path);
245         (*nf)->fd = -1;
246         (*nf)->user = f->user;
247         (*nf)->attach = 0;
248         return nil;
249 }
250
251 char*
252 rwalk(Fid *f)
253 {
254         char *name;
255         Fid *nf;
256         char *err;
257         int i;
258         String *npath;
259         Dir *d;
260         char *cp;
261         Qid qid;
262
263         err = nil;
264         nf = nil;
265         rhdr.nwqid = 0;
266         if(rhdr.newfid != rhdr.fid){
267                 err = clone(f, &nf);
268                 if(err)
269                         return err;
270                 f = nf; /* walk the new fid */
271         }
272         readnames();
273         npath = s_clone(f->path);
274         if(thdr.nwname > 0){
275                 for(i=0; i<thdr.nwname && i<MAXWELEM; i++){
276                         name = long2short(thdr.wname[i], 0);
277                         if(strcmp(name, ".") == 0){
278                                 ;
279                         } else if(strcmp(name, "..") == 0){
280                                 cp = strrchr(s_to_c(npath), '/');
281                                 if(cp != nil){
282                                         *cp = 0;
283                                         npath->ptr = cp;
284                                 }
285                         } else {
286                                 s_append(npath, "/");
287                                 s_append(npath, name);
288                         }
289                         d = dirstat(s_to_c(npath));
290                         if(d == nil)
291                                 break;
292                         rhdr.nwqid++;
293                         qid = d->qid;
294                         rhdr.wqid[i] = qid;
295                         free(d);
296                 }
297                 if(i==0 && err == nil)
298                         err = Enotexist;
299         }
300
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)){
303                 f->busy = 0;
304                 s_free(npath);
305         }
306
307         /* update the fid after a successful walk */
308         if(rhdr.nwqid == thdr.nwname){
309                 s_free(f->path);
310                 f->path = npath;
311         }
312         return err;
313 }
314
315 static char*
316 passerror(void)
317 {
318         static char err[256];
319
320         rerrstr(err, sizeof err);
321         return err;
322 }
323
324 char*
325 ropen(Fid *f)
326 {
327         if(readonly && (thdr.mode & 3))
328                 return Eperm;
329         if(f->fd >= 0)
330                 return Eisopen;
331         f->fd = open(s_to_c(f->path), thdr.mode);
332         if(f->fd < 0)
333                 return passerror();
334         fidqid(f, &rhdr.qid);
335         f->qid = rhdr.qid;
336         rhdr.iounit = messagesize-IOHDRSZ;
337         return nil;
338 }
339
340 char*
341 rcreate(Fid *f)
342 {
343         char *name;
344
345         if(readonly)
346                 return Eperm;
347         readnames();
348         name = long2short(thdr.name, 1);
349         if(f->fd >= 0)
350                 return Eisopen;
351         s_append(f->path, "/");
352         s_append(f->path, name);
353         f->fd = create(s_to_c(f->path), thdr.mode, thdr.perm);
354         if(f->fd < 0)
355                 return passerror();
356         fidqid(f, &rhdr.qid);
357         f->qid = rhdr.qid;
358         rhdr.iounit = messagesize-IOHDRSZ;
359         return nil;
360 }
361
362 char*
363 rreaddir(Fid *f)
364 {
365         int i;
366         int n;
367
368         /* reread the directory */
369         if(thdr.offset == 0){
370                 if(f->dir)
371                         free(f->dir);
372                 f->dir = nil;
373                 if(f->diroff != 0)
374                         seek(f->fd, 0, 0);
375                 f->ndir = dirreadall(f->fd, &f->dir);
376                 f->diroff = 0;
377                 if(f->ndir < 0)
378                         return passerror();
379                 readnames();
380                 for(i = 0; i < f->ndir; i++)
381                         f->dir[i].name = short2long(f->dir[i].name);
382         }
383
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);
387                 if(i <= BIT16SZ)
388                         break;
389                 f->diroff++;
390         }
391         rhdr.data = (char*)rdata;
392         rhdr.count = n;
393         return nil;
394 }
395
396 char*
397 rread(Fid *f)
398 {
399         long n;
400
401         if(f->fd < 0)
402                 return Enotexist;
403         if(thdr.count > messagesize-IOHDRSZ)
404                 thdr.count = messagesize-IOHDRSZ;
405         if(f->qid.type & QTDIR)
406                 return rreaddir(f);
407         n = pread(f->fd, rdata, thdr.count, thdr.offset);
408         if(n < 0)
409                 return passerror();
410         rhdr.data = (char*)rdata;
411         rhdr.count = n;
412         return nil;
413 }
414
415 char*
416 rwrite(Fid *f)
417 {
418         long n;
419
420         if(readonly || (f->qid.type & QTDIR))
421                 return Eperm;
422         if(f->fd < 0)
423                 return Enotexist;
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);
427         if(n < 0)
428                 return passerror();
429         rhdr.count = n;
430         return nil;
431 }
432
433 char*
434 rclunk(Fid *f)
435 {
436         f->busy = 0;
437         close(f->fd);
438         f->fd = -1;
439         f->path = s_reset(f->path);
440         if(f->attach){
441                 free(f->user);
442                 f->user = nil;
443         }
444         f->attach = 0;
445         if(f->dir != nil){
446                 free(f->dir);
447                 f->dir = nil;
448         }
449         f->diroff = f->ndir = 0;
450         return nil;
451 }
452
453 char*
454 rremove(Fid *f)
455 {
456         if(remove(s_to_c(f->path)) < 0)
457                 return passerror();
458         return nil;
459 }
460
461 char*
462 rstat(Fid *f)
463 {
464         int n;
465         Dir *d;
466
467         d = dirstat(s_to_c(f->path));
468         if(d == nil)
469                 return passerror();
470         d->name = short2long(d->name);
471         n = convD2M(d, statbuf, sizeof statbuf);
472         free(d);
473         if(n <= BIT16SZ)
474                 return passerror();
475         rhdr.nstat = n;
476         rhdr.stat = statbuf;
477         return nil;
478 }
479
480 char*
481 rwstat(Fid *f)
482 {
483         int n;
484         Dir d;
485
486         if(readonly)
487                 return Eperm;
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);
491         if(n < 0)
492                 return passerror();
493         return nil;
494 }
495
496 Fid *
497 newfid(int fid)
498 {
499         Fid *f, *ff;
500
501         ff = 0;
502         for(f = fids; f; f = f->next)
503                 if(f->fid == fid)
504                         return f;
505                 else if(!ff && !f->busy)
506                         ff = f;
507         if(ff){
508                 ff->fid = fid;
509                 return ff;
510         }
511         f = emalloc(sizeof *f);
512         f->path = s_reset(f->path);
513         f->fd = -1;
514         f->fid = fid;
515         f->next = fids;
516         fids = f;
517         return f;
518 }
519
520 void
521 io(void)
522 {
523         char *err;
524         int n, pid;
525
526         pid = getpid();
527
528         while((n = read9pmsg(mfd[0], mdata, messagesize)) != 0){
529                 if(n < 0)
530                         sysfatal("mount read: %r");
531                 if(convM2S(mdata, n, &thdr) != n)
532                         sysfatal("convM2S format error: %r");
533
534                 if(debug)
535                         fprint(2, "%s %d:<-%F\n", argv0, pid, &thdr);
536
537                 if(!fcalls[thdr.type])
538                         err = "bad fcall type";
539                 else
540                         err = (*fcalls[thdr.type])(newfid(thdr.fid));
541                 if(err){
542                         rhdr.type = Rerror;
543                         rhdr.ename = err;
544                 }else{
545                         rhdr.type = thdr.type + 1;
546                         rhdr.fid = thdr.fid;
547                 }
548                 rhdr.tag = thdr.tag;
549                 if(debug)
550                         fprint(2, "%s %d:->%F\n", argv0, pid, &rhdr);/**/
551                 n = convS2M(&rhdr, mdata, messagesize);
552                 if(n == 0)
553                         sysfatal("convS2M error on write");
554                 if(write(mfd[1], mdata, n) != n)
555                         sysfatal("mount write");
556         }
557 }
558
559 void *
560 emalloc(ulong n)
561 {
562         void *p;
563
564         p = malloc(n);
565         if(!p)
566                 sysfatal("out of memory");
567         memset(p, 0, n);
568         return p;
569 }
570
571 void *
572 erealloc(void *p, ulong n)
573 {
574         p = realloc(p, n);
575         if(!p)
576                 sysfatal("out of memory");
577         return p;
578 }
579
580 char *
581 estrdup(char *q)
582 {
583         char *p;
584         int n;
585
586         n = strlen(q)+1;
587         p = malloc(n);
588         if(!p)
589                 sysfatal("out of memory");
590         memmove(p, q, n);
591         return p;
592 }
593
594 void
595 fidqid(Fid *f, Qid *q)
596 {
597         Dir *d;
598
599         d = dirstat(s_to_c(f->path));
600         if(d == nil)
601                 *q = (Qid){0, 0, QTFILE};
602         else {
603                 *q = d->qid;
604                 free(d);
605         }
606 }
607
608 /*
609  *  table of name translations
610  *
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.
614  */
615
616 typedef struct Name Name;
617 struct Name
618 {
619         Name    *next;
620         char    shortname[NAMELEN];
621         char    *longname;
622 };
623
624 Dir *dbstat;    /* last stat of the name file */
625 char *namefile = "./.longnames";
626 char *namebuf;
627 Name *names;
628
629 Name*
630 newname(char *longname, int writeflag)
631 {
632         Name *np;
633         int n;
634         uchar digest[MD5dlen];
635         int fd;
636
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);
644         np->next = names;
645         names = np;
646
647         /* don't change namefile if we're read only */
648         if(!writeflag)
649                 return np;
650
651         /* add to namefile */
652         fd = open(namefile, OWRITE);
653         if(fd >= 0){
654                 seek(fd, 0, 2);
655                 fprint(fd, "%s\n", longname);
656                 close(fd);
657         }
658         return np;
659 }
660
661 void
662 freenames(void)
663 {
664         Name *np, *next;
665
666         for(np = names; np != nil; np = next){
667                 next = np->next;
668                 free(np);
669         }
670         names = nil;
671 }
672
673 /*
674  *  reread the file if the qid.path has changed.
675  *
676  *  read any new entries if length has changed.
677  */
678 void
679 readnames(void)
680 {
681         Dir *d;
682         int fd;
683         vlong offset;
684         Biobuf *b;
685         char *p;
686
687         d = dirstat(namefile);
688         if(d == nil){
689                 if(readonly)
690                         return;
691
692                 /* create file if it doesn't exist */
693                 fd = create(namefile, OREAD, DMAPPEND|0666);
694                 if(fd < 0)
695                         return;
696                 if(dbstat != nil)
697                         free(dbstat);
698                 dbstat = nil;
699                 close(fd);
700                 return;
701         }
702
703         /* up to date? */
704         offset = 0;
705         if(dbstat != nil){
706                 if(d->qid.path == dbstat->qid.path){
707                         if(d->length <= dbstat->length){
708                                 free(d);
709                                 return;
710                         }
711                         offset = dbstat->length;
712                 } else {
713                         freenames();
714                 }
715                 free(dbstat);
716                 dbstat = nil;
717         }
718
719         /* read file */
720         b = Bopen(namefile, OREAD);
721         if(b == nil){
722                 free(d);
723                 return;
724         }
725         Bseek(b, offset, 0);
726         while((p = Brdline(b, '\n')) != nil){
727                 p[Blinelen(b)-1] = 0;
728                 newname(p, 0);
729         }
730         Bterm(b);
731         dbstat = d;
732 }
733
734 /*
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.
738  */
739 char*
740 long2short(char *longname, int writeflag)
741 {
742         Name *np;
743
744         if(strlen(longname) < NAMELEN-1 && strpbrk(longname, " ")==nil)
745                 return longname;
746
747         for(np = names; np != nil; np = np->next)
748                 if(strcmp(longname, np->longname) == 0)
749                         return np->shortname;
750         if(!writeflag)
751                 return longname;
752         np = newname(longname, !readonly);
753         return np->shortname;
754 }
755
756 /*
757  *  look up a short name, if it doesn't exist, return the
758  *  longname.
759  */
760 char*
761 short2long(char *shortname)
762 {
763         Name *np;
764
765         for(np = names; np != nil; np = np->next)
766                 if(strcmp(shortname, np->shortname) == 0)
767                         return np->longname;
768         return shortname;
769 }