]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/tapefs/fs.c
kernel: keep segment locked for data2txt
[plan9front.git] / sys / src / cmd / tapefs / fs.c
1 #include <u.h>
2 #include <libc.h>
3 #include <authsrv.h>
4 #include <fcall.h>
5 #include "tapefs.h"
6
7 Fid     *fids;
8 Ram     *ram;
9 int     mfd[2];
10 char    *user;
11 uchar   mdata[Maxbuf+IOHDRSZ];
12 int     messagesize = Maxbuf+IOHDRSZ;
13 Fcall   rhdr;
14 Fcall   thdr;
15 ulong   path;
16 Idmap   *uidmap;
17 Idmap   *gidmap;
18 int     replete;
19 int     blocksize;              /* for 32v */
20 int     verbose;
21 int     newtap;         /* tap with time in sec */
22 int     blocksize;
23
24 Fid *   newfid(int);
25 int     ramstat(Ram*, uchar*, int);
26 void    io(void);
27 void    usage(void);
28 int     perm(int);
29
30 char    *rflush(Fid*), *rversion(Fid*), *rauth(Fid*),
31         *rattach(Fid*), *rwalk(Fid*),
32         *ropen(Fid*), *rcreate(Fid*),
33         *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
34         *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
35
36 char    *(*fcalls[])(Fid*) = {
37         [Tflush]        rflush,
38         [Tversion]              rversion,
39         [Tauth] rauth,
40         [Tattach]       rattach,
41         [Twalk]         rwalk,
42         [Topen]         ropen,
43         [Tcreate]       rcreate,
44         [Tread]         rread,
45         [Twrite]        rwrite,
46         [Tclunk]        rclunk,
47         [Tremove]       rremove,
48         [Tstat]         rstat,
49         [Twstat]        rwstat,
50 };
51
52 char    Eperm[] =       "permission denied";
53 char    Enotdir[] =     "not a directory";
54 char    Enoauth[] =     "tapefs: authentication not required";
55 char    Enotexist[] =   "file does not exist";
56 char    Einuse[] =      "file in use";
57 char    Eexist[] =      "file exists";
58 char    Enotowner[] =   "not owner";
59 char    Eisopen[] =     "file already open for I/O";
60 char    Excl[] =        "exclusive use file already open";
61 char    Ename[] =       "illegal name";
62
63 void
64 notifyf(void *a, char *s)
65 {
66         USED(a);
67         if(strncmp(s, "interrupt", 9) == 0)
68                 noted(NCONT);
69         noted(NDFLT);
70 }
71
72 void
73 main(int argc, char *argv[])
74 {
75         Ram *r;
76         char *defmnt;
77         int p[2];
78         char buf[TICKREQLEN];
79
80         fmtinstall('F', fcallfmt);
81
82         defmnt = "/n/tapefs";
83         ARGBEGIN{
84         case 'm':
85                 defmnt = EARGF(usage());
86                 break;
87         case 'p':                       /* password file */
88                 uidmap = getpass(EARGF(usage()));
89                 break;
90         case 'g':                       /* group file */
91                 gidmap = getpass(EARGF(usage()));
92                 break;
93         case 'v':
94                 verbose++;
95                 break;
96         case 'n':
97                 newtap++;
98                 break;
99         case 'b':
100                 blocksize = atoi(EARGF(usage()));
101                 break;
102         default:
103                 usage();
104         }ARGEND
105
106         if(argc==0)
107                 error("no file to mount");
108         user = getuser();
109         if(user == nil)
110                 user = "dmr";
111         ram = r = (Ram *)emalloc(sizeof(Ram));
112         r->busy = 1;
113         r->data = 0;
114         r->ndata = 0;
115         r->perm = DMDIR | 0775;
116         r->qid.path = 0;
117         r->qid.vers = 0;
118         r->qid.type = QTDIR;
119         r->parent = 0;
120         r->child = 0;
121         r->next = 0;
122         r->user = user;
123         r->group = user;
124         r->atime = time(0);
125         r->mtime = r->atime;
126         r->replete = 0;
127         r->name = estrdup(".");
128         populate(argv[0]);
129         r->replete |= replete;
130         if(pipe(p) < 0)
131                 error("pipe failed");
132         mfd[0] = mfd[1] = p[0];
133         notify(notifyf);
134
135         switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
136         case -1:
137                 error("fork");
138         case 0:
139                 close(p[1]);
140                 notify(notifyf);
141                 io();
142                 break;
143         default:
144                 close(p[0]);    /* don't deadlock if child fails */
145                 if(mount(p[1], -1, defmnt, MREPL|MCREATE, "") < 0) {
146                         sprint(buf, "mount on `%s' failed", defmnt);
147                         error(buf);
148                 }
149         }
150         exits(0);
151 }
152
153 char*
154 rversion(Fid *unused)
155 {
156         Fid *f;
157
158         USED(unused);
159
160         if(rhdr.msize < 256)
161                 return "version: message too small";
162         if(rhdr.msize > messagesize)
163                 rhdr.msize = messagesize;
164         else
165                 messagesize = rhdr.msize;
166         thdr.msize = messagesize;
167         if(strncmp(rhdr.version, "9P2000", 6) != 0)
168                 return "unrecognized 9P version";
169         thdr.version = "9P2000";
170
171         for(f = fids; f; f = f->next)
172                 if(f->busy)
173                         rclunk(f);
174         return 0;
175 }
176
177 char*
178 rauth(Fid *unused)
179 {
180         USED(unused);
181
182         return Enoauth;
183 }
184
185 char*
186 rflush(Fid *f)
187 {
188         USED(f);
189         return 0;
190 }
191
192 char*
193 rattach(Fid *f)
194 {
195         /* no authentication! */
196         f->busy = 1;
197         f->rclose = 0;
198         f->ram = ram;
199         thdr.qid = f->ram->qid;
200         if(rhdr.uname[0])
201                 f->user = strdup(rhdr.uname);
202         else
203                 f->user = "none";
204         return 0;
205 }
206
207 char*
208 rwalk(Fid *f)
209 {
210         Fid *nf;
211         Ram *r;
212         char *err;
213         char *name;
214         Ram *dir;
215         int i;
216
217         nf = nil;
218         if(f->ram->busy == 0)
219                 return Enotexist;
220         if(f->open)
221                 return Eisopen;
222         if(rhdr.newfid != rhdr.fid){
223                 nf = newfid(rhdr.newfid);
224                 nf->busy = 1;
225                 nf->open = 0;
226                 nf->rclose = 0;
227                 nf->ram = f->ram;
228                 nf->user = f->user;     /* no ref count; the leakage is minor */
229                 f = nf;
230         }
231
232         thdr.nwqid = 0;
233         err = nil;
234         r = f->ram;
235
236         if(rhdr.nwname > 0){
237                 for(i=0; i<rhdr.nwname; i++){
238                         if((r->qid.type & QTDIR) == 0){
239                                 err = Enotdir;
240                                 break;
241                         }
242                         if(r->busy == 0){
243                                 err = Enotexist;
244                                 break;
245                         }
246                         r->atime = time(0);
247                         name = rhdr.wname[i];
248                         dir = r;
249                         if(!perm(Pexec)){
250                                 err = Eperm;
251                                 break;
252                         }
253                         if(strcmp(name, "..") == 0){
254                                 r = dir->parent;
255    Accept:
256                                 if(i == MAXWELEM){
257                                         err = "name too long";
258                                         break;
259                                 }
260                                 thdr.wqid[thdr.nwqid++] = r->qid;
261                                 continue;
262                         }
263                         if(!dir->replete)
264                                 popdir(dir);
265                         for(r=dir->child; r; r=r->next)
266                                 if(r->busy && cistrcmp(name, r->name)==0)
267                                         goto Accept;
268                         break;  /* file not found */
269                 }
270
271                 if(i==0 && err == nil)
272                         err = Enotexist;
273         }
274
275         if(err!=nil || thdr.nwqid<rhdr.nwname){
276                 if(nf){
277                         nf->busy = 0;
278                         nf->open = 0;
279                         nf->ram = 0;
280                 }
281         }else if(thdr.nwqid  == rhdr.nwname)
282                 f->ram = r;
283
284         return err;
285
286 }
287
288 char *
289 ropen(Fid *f)
290 {
291         Ram *r;
292         int mode, trunc;
293
294         if(f->open)
295                 return Eisopen;
296         r = f->ram;
297         if(r->busy == 0)
298                 return Enotexist;
299         if(r->perm & DMEXCL)
300                 if(r->open)
301                         return Excl;
302         mode = rhdr.mode;
303         if(r->qid.type & QTDIR){
304                 if(mode != OREAD)
305                         return Eperm;
306                 thdr.qid = r->qid;
307                 return 0;
308         }
309         if(mode & ORCLOSE)
310                 return Eperm;
311         trunc = mode & OTRUNC;
312         mode &= OPERM;
313         if(mode==OWRITE || mode==ORDWR || trunc)
314                 if(!perm(Pwrite))
315                         return Eperm;
316         if(mode==OREAD || mode==ORDWR)
317                 if(!perm(Pread))
318                         return Eperm;
319         if(mode==OEXEC)
320                 if(!perm(Pexec))
321                         return Eperm;
322         if(trunc && (r->perm&DMAPPEND)==0){
323                 r->ndata = 0;
324                 dotrunc(r);
325                 r->qid.vers++;
326         }
327         thdr.qid = r->qid;
328         thdr.iounit = messagesize-IOHDRSZ;
329         f->open = 1;
330         r->open++;
331         return 0;
332 }
333
334 char *
335 rcreate(Fid *f)
336 {
337         USED(f);
338
339         return Eperm;
340 }
341
342 char*
343 rread(Fid *f)
344 {
345         int i, len;
346         Ram *r;
347         char *buf;
348         uvlong off, end;
349         int n, cnt;
350
351         if(f->ram->busy == 0)
352                 return Enotexist;
353         n = 0;
354         thdr.count = 0;
355         off = rhdr.offset;
356         end = rhdr.offset + rhdr.count;
357         cnt = rhdr.count;
358         if(cnt > messagesize-IOHDRSZ)
359                 cnt = messagesize-IOHDRSZ;
360         buf = thdr.data;
361         if(f->ram->qid.type & QTDIR){
362                 if(!f->ram->replete)
363                         popdir(f->ram);
364                 for(i=0,r=f->ram->child; r!=nil && i<end; r=r->next){
365                         if(!r->busy)
366                                 continue;
367                         len = ramstat(r, (uchar*)buf+n, cnt-n);
368                         if(len <= BIT16SZ)
369                                 break;
370                         if(i >= off)
371                                 n += len;
372                         i += len;
373                 }
374                 thdr.count = n;
375                 return 0;
376         }
377         r = f->ram;
378         if(off >= r->ndata)
379                 return 0;
380         r->atime = time(0);
381         n = cnt;
382         if(off+n > r->ndata)
383                 n = r->ndata - off;
384         thdr.data = doread(r, off, n);
385         thdr.count = n;
386         return 0;
387 }
388
389 char*
390 rwrite(Fid *f)
391 {
392         Ram *r;
393         ulong off;
394         int cnt;
395
396         r = f->ram;
397         if(dopermw(f->ram)==0)
398                 return Eperm;
399         if(r->busy == 0)
400                 return Enotexist;
401         off = rhdr.offset;
402         if(r->perm & DMAPPEND)
403                 off = r->ndata;
404         cnt = rhdr.count;
405         if(r->qid.type & QTDIR)
406                 return "file is a directory";
407         if(off > 100*1024*1024)         /* sanity check */
408                 return "write too big";
409         dowrite(r, rhdr.data, off, cnt);
410         r->qid.vers++;
411         r->mtime = time(0);
412         thdr.count = cnt;
413         return 0;
414 }
415
416 char *
417 rclunk(Fid *f)
418 {
419         if(f->open)
420                 f->ram->open--;
421         f->busy = 0;
422         f->open = 0;
423         f->ram = 0;
424         return 0;
425 }
426
427 char *
428 rremove(Fid *f)
429 {
430         USED(f);
431         return Eperm;
432 }
433
434 char *
435 rstat(Fid *f)
436 {
437         if(f->ram->busy == 0)
438                 return Enotexist;
439         thdr.nstat = ramstat(f->ram, thdr.stat, messagesize-IOHDRSZ);
440         return 0;
441 }
442
443 char *
444 rwstat(Fid *f)
445 {
446         if(f->ram->busy == 0)
447                 return Enotexist;
448         return Eperm;
449 }
450
451 int
452 ramstat(Ram *r, uchar *buf, int nbuf)
453 {
454         Dir dir;
455
456         dir.name = r->name;
457         dir.qid = r->qid;
458         dir.mode = r->perm;
459         dir.length = r->ndata;
460         dir.uid = r->user;
461         dir.gid = r->group;
462         dir.muid = r->user;
463         dir.atime = r->atime;
464         dir.mtime = r->mtime;
465         return convD2M(&dir, buf, nbuf);
466 }
467
468 Fid *
469 newfid(int fid)
470 {
471         Fid *f, *ff;
472
473         ff = 0;
474         for(f = fids; f; f = f->next)
475                 if(f->fid == fid)
476                         return f;
477                 else if(!ff && !f->busy)
478                         ff = f;
479         if(ff){
480                 ff->fid = fid;
481                 ff->open = 0;
482                 ff->busy = 1;
483         }
484         f = emalloc(sizeof *f);
485         f->ram = 0;
486         f->fid = fid;
487         f->busy = 1;
488         f->open = 0;
489         f->next = fids;
490         fids = f;
491         return f;
492 }
493
494 void
495 io(void)
496 {
497         char *err;
498         int n, nerr;
499         char buf[ERRMAX];
500
501         errstr(buf, sizeof buf);
502         for(nerr=0, buf[0]='\0'; nerr<100; nerr++){
503                 /*
504                  * reading from a pipe or a network device
505                  * will give an error after a few eof reads
506                  * however, we cannot tell the difference
507                  * between a zero-length read and an interrupt
508                  * on the processes writing to us,
509                  * so we wait for the error
510                  */
511                 n = read9pmsg(mfd[0], mdata, sizeof mdata);
512                 if(n==0)
513                         continue;
514                 if(n < 0){
515                         if(buf[0]=='\0')
516                                 errstr(buf, sizeof buf);
517                         continue;
518                 }
519                 nerr = 0;
520                 buf[0] = '\0';
521                 if(convM2S(mdata, n, &rhdr) != n)
522                         error("convert error in convM2S");
523
524                 if(verbose)
525                         fprint(2, "tapefs: <=%F\n", &rhdr);/**/
526
527                 thdr.data = (char*)mdata + IOHDRSZ;
528                 thdr.stat = mdata + IOHDRSZ;
529                 if(!fcalls[rhdr.type])
530                         err = "bad fcall type";
531                 else
532                         err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
533                 if(err){
534                         thdr.type = Rerror;
535                         thdr.ename = err;
536                 }else{
537                         thdr.type = rhdr.type + 1;
538                         thdr.fid = rhdr.fid;
539                 }
540                 thdr.tag = rhdr.tag;
541                 n = convS2M(&thdr, mdata, messagesize);
542                 if(n <= 0)
543                         error("convert error in convS2M");
544                 if(verbose)
545                         fprint(2, "tapefs: =>%F\n", &thdr);/**/
546                 if(write(mfd[1], mdata, n) != n)
547                         error("mount write");
548         }
549         if(buf[0]=='\0' || strstr(buf, "hungup"))
550                 exits("");
551         fprint(2, "%s: mount read: %s\n", argv0, buf);
552         exits(buf);
553 }
554
555 int
556 perm(int p)
557 {
558         if(p==Pwrite)
559                 return 0;
560         return 1;
561 }
562
563 void
564 error(char *s)
565 {
566         fprint(2, "%s: %s: ", argv0, s);
567         perror("");
568         exits(s);
569 }
570
571 char*
572 estrdup(char *s)
573 {
574         char *t;
575
576         t = emalloc(strlen(s)+1);
577         strcpy(t, s);
578         return t;
579 }
580
581 void *
582 emalloc(ulong n)
583 {
584         void *p;
585         p = mallocz(n, 1);
586         if(!p)
587                 error("out of memory");
588         return p;
589 }
590
591 void *
592 erealloc(void *p, ulong n)
593 {
594         p = realloc(p, n);
595         if(!p)
596                 error("out of memory");
597         return p;
598 }
599
600 void
601 usage(void)
602 {
603         fprint(2, "usage: %s [-s] [-m mountpoint]\n", argv0);
604         exits("usage");
605 }