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