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