1 2 /* 3 * exportfs - Export a plan 9 name space across a network 4 */ 5 #include <u.h> 6 #include <libc.h> 7 #include <auth.h> 8 #include <fcall.h> 9 #define Extern 10 #include "exportfs.h" 11 12 #define QIDMODE (CHDIR|CHAPPEND|CHEXCL|CHMOUNT) 13 ulong newqid = ~QIDMODE; 14 15 void (*fcalls[])(Fsrpc*) = 16 { 17 [Tnop] Xnop, 18 [Tsession] Xsession, 19 [Tflush] Xflush, 20 [Tattach] Xattach, 21 [Tclone] Xclone, 22 [Twalk] Xwalk, 23 [Topen] slave, 24 [Tcreate] Xcreate, 25 [Tclunk] Xclunk, 26 [Tread] slave, 27 [Twrite] slave, 28 [Tremove] Xremove, 29 [Tstat] Xstat, 30 [Twstat] Xwstat, 31 [Tclwalk] Xclwalk, 32 }; 33 34 /* accounting and debugging counters */ 35 int filecnt; 36 int freecnt; 37 int qidcnt; 38 int qfreecnt; 39 int ncollision; 40 41 int fflag; 42 int netfd; 43 int filter(int); 44 45 void 46 usage(void) 47 { 48 fprint(2, "usage: %s [-as] [-c ctlfile]\n", argv0); 49 fatal("usage"); 50 } 51 52 void 53 main(int argc, char **argv) 54 { 55 char buf[128]; 56 Fsrpc *r; 57 int n; 58 char *dbfile, *srv; 59 char user[NAMELEN]; 60 61 dbfile = "/tmp/exportdb"; 62 srv = nil; 63 64 ARGBEGIN{ 65 case 'a': 66 // fprint(2, "srvauth\n"); 67 if(srvauth(0, user) < 0) 68 fatal("srvauth"); 69 // fprint(2, "newns\n"); 70 if(newns(user, 0) < 0) 71 fatal("newns"); 72 putenv("service", "exportfs"); 73 break; 74 75 case 'd': 76 dbg++; 77 break; 78 79 case 'f': 80 dbfile = EARGF(usage()); 81 break; 82 83 case 'F': 84 fflag++; 85 break; 86 87 case 'r': 88 srv = EARGF(usage()); 89 break; 90 91 case 's': 92 srv = "/"; 93 break; 94 95 default: 96 usage(); 97 }ARGEND 98 USED(argc, argv); 99 100 if(dbg) { 101 n = create(dbfile, OWRITE|OTRUNC, 0666); 102 dup(n, DFD); 103 close(n); 104 } 105 106 DEBUG(DFD, "exportfs: started\n"); 107 108 rfork(RFNOTEG); 109 110 Workq = mallocz(sizeof(Fsrpc)*Nr_workbufs, 1); 111 fhash = mallocz(sizeof(Fid*)*FHASHSIZE, 1); 112 113 if(Workq == 0 || fhash == 0) 114 fatal("no initial memory"); 115 116 117 fmtinstall('F', fcallconv); 118 119 /* 120 * Get tree to serve from network connection, 121 * check we can get there and ack the connection 122 */ 123 if(srv) { 124 chdir(srv); 125 DEBUG(DFD, "invoked as server for %s", srv); 126 } 127 else { 128 buf[0] = 0; 129 n = read(0, buf, sizeof(buf)); 130 if(n < 0) { 131 errstr(buf); 132 fprint(0, "read(0): %s", buf); 133 DEBUG(DFD, "read(0): %s", buf); 134 exits(buf); 135 } 136 buf[n] = 0; 137 if(chdir(buf) < 0) { 138 char ebuf[128]; 139 errstr(ebuf); 140 fprint(0, "chdir(%d:\"%s\"): %s", n, buf, ebuf); 141 DEBUG(DFD, "chdir(%d:\"%s\"): %s", n, buf, ebuf); 142 exits(ebuf); 143 } 144 } 145 146 DEBUG(DFD, "initing root\n"); 147 initroot(); 148 149 DEBUG(DFD, "exportfs: %s\n", buf); 150 151 if(srv == nil && write(0, "OK", 2) != 2) 152 fatal("open ack write"); 153 154 /* 155 * push the fcall line discipline 156 */ 157 netfd = 0; 158 if(fflag) 159 netfd = filter(netfd); 160 161 /* 162 * Start serving file requests from the network 163 */ 164 for(;;) { 165 r = getsbuf(); 166 if(r == 0) 167 fatal("Out of service buffers"); 168 169 do 170 n = read9p(netfd, r->buf, sizeof(r->buf)); 171 while(n == 0); 172 173 if(n < 0) 174 fatal("server read"); 175 176 if(convM2S(r->buf, &r->work, n) == 0) 177 fatal("format error"); 178 179 DEBUG(DFD, "%F\n", &r->work); 180 (fcalls[r->work.type])(r); 181 } 182 } 183 184 void 185 reply(Fcall *r, Fcall *t, char *err) 186 { 187 char data[MAXFDATA+MAXMSG]; 188 int n; 189 190 t->tag = r->tag; 191 t->fid = r->fid; 192 if(err) { 193 t->type = Rerror; 194 strncpy(t->ename, err, ERRLEN); 195 } 196 else 197 t->type = r->type + 1; 198 199 DEBUG(DFD, "\t%F\n", t); 200 201 n = convS2M(t, data); 202 if(write9p(netfd, data, n)!=n) 203 fatal("mount write"); 204 } 205 206 Fid * 207 getfid(int nr) 208 { 209 Fid *f; 210 211 for(f = fidhash(nr); f; f = f->next) 212 if(f->nr == nr) 213 return f; 214 215 return 0; 216 } 217 218 int 219 freefid(int nr) 220 { 221 Fid *f, **l; 222 char buf[128]; 223 224 l = &fidhash(nr); 225 for(f = *l; f; f = f->next) { 226 if(f->nr == nr) { 227 if(f->mpend) 228 f->mpend->busy = 0; 229 if(f->mid) { 230 sprint(buf, "/mnt/exportfs/%d", f->mid); 231 unmount(0, buf); 232 psmap[f->mid] = 0; 233 } 234 freefile(f->f); 235 f->f = nil; 236 *l = f->next; 237 f->next = fidfree; 238 fidfree = f; 239 return 1; 240 } 241 l = &f->next; 242 } 243 244 return 0; 245 } 246 247 Fid * 248 newfid(int nr) 249 { 250 Fid *new, **l; 251 int i; 252 253 l = &fidhash(nr); 254 for(new = *l; new; new = new->next) 255 if(new->nr == nr) 256 return 0; 257 258 if(fidfree == 0) { 259 fidfree = mallocz(sizeof(Fid) * Fidchunk, 1); 260 if(fidfree == 0) 261 fatal("out of memory"); 262 263 for(i = 0; i < Fidchunk-1; i++) 264 fidfree[i].next = &fidfree[i+1]; 265 266 fidfree[Fidchunk-1].next = 0; 267 } 268 269 new = fidfree; 270 fidfree = new->next; 271 272 memset(new, 0, sizeof(Fid)); 273 new->next = *l; 274 *l = new; 275 new->nr = nr; 276 new->fid = -1; 277 new->mpend = 0; 278 new->mid = 0; 279 280 return new; 281 } 282 283 Fsrpc * 284 getsbuf(void) 285 { 286 static int ap; 287 int look, rounds; 288 Fsrpc *wb; 289 290 for(rounds = 0; rounds < 10; rounds++) { 291 for(look = 0; look < Nr_workbufs; look++) { 292 if(++ap == Nr_workbufs) 293 ap = 0; 294 if(Workq[ap].busy == 0) 295 break; 296 } 297 298 if(look == Nr_workbufs){ 299 sleep(10 * rounds); 300 continue; 301 } 302 303 wb = &Workq[ap]; 304 wb->pid = 0; 305 wb->canint = 0; 306 wb->flushtag = NOTAG; 307 wb->busy = 1; 308 309 return wb; 310 } 311 fatal("No more work buffers"); 312 return nil; 313 } 314 315 void 316 freefile(File *f) 317 { 318 File *parent, *child; 319 320 Loop: 321 f->ref--; 322 if(f->ref > 0) 323 return; 324 freecnt++; 325 if(f->ref < 0) abort(); 326 DEBUG(DFD, "free %s\n", f->name); 327 /* delete from parent */ 328 parent = f->parent; 329 if(parent->child == f) 330 parent->child = f->childlist; 331 else{ 332 for(child=parent->child; child->childlist!=f; child=child->childlist) 333 if(child->childlist == nil) 334 fatal("bad child list"); 335 child->childlist = f->childlist; 336 } 337 freeqid(f->qidt); 338 free(f); 339 f = parent; 340 if(f != nil) 341 goto Loop; 342 } 343 344 File * 345 file(File *parent, char *name) 346 { 347 Dir dir; 348 char buf[128]; 349 File *f; 350 351 DEBUG(DFD, "\tfile: 0x%p %s name %s\n", parent, parent->name, name); 352 353 makepath(buf, parent, name); 354 if(dirstat(buf, &dir) < 0) 355 return nil; 356 357 for(f = parent->child; f; f = f->childlist) 358 if(strcmp(name, f->name) == 0) 359 break; 360 361 if(f == nil){ 362 f = mallocz(sizeof(File), 1); 363 if(f == 0) 364 fatal("no memory"); 365 strcpy(f->name, name); 366 367 f->parent = parent; 368 f->childlist = parent->child; 369 parent->child = f; 370 parent->ref++; 371 f->ref = 0; 372 filecnt++; 373 } 374 f->ref++; 375 f->qid.vers = dir.qid.vers; 376 f->qidt = uniqueqid(&dir); 377 f->qid.path = f->qidt->uniqpath; 378 379 f->inval = 0; 380 381 382 return f; 383 } 384 385 void 386 initroot(void) 387 { 388 Dir dir; 389 390 root = mallocz(sizeof(File), 1); 391 if(root == 0) 392 fatal("no memory"); 393 394 strcpy(root->name, "."); 395 if(dirstat(root->name, &dir) < 0) 396 fatal("root stat"); 397 398 root->ref = 1; 399 root->qid.vers = dir.qid.vers; 400 root->qidt = uniqueqid(&dir); 401 root->qid.path = root->qidt->uniqpath; 402 403 psmpt = mallocz(sizeof(File), 1); 404 if(psmpt == 0) 405 fatal("no memory"); 406 407 strcpy(psmpt->name, "/"); 408 if(dirstat(psmpt->name, &dir) < 0) 409 return; 410 411 psmpt->ref = 1; 412 psmpt->qid.vers = dir.qid.vers; 413 psmpt->qidt = uniqueqid(&dir); 414 psmpt->qid.path = psmpt->qidt->uniqpath; 415 416 psmpt = file(psmpt, "mnt"); 417 if(psmpt == 0) 418 return; 419 psmpt = file(psmpt, "exportfs"); 420 } 421 422 void 423 makepath(char *s, File *p, char *name) 424 { 425 int i; 426 char *c, *seg[256]; 427 428 seg[0] = name; 429 for(i = 1; i < 100 && p; i++, p = p->parent) 430 seg[i] = p->name; 431 432 while(i--) { 433 for(c = seg[i]; *c; c++) 434 *s++ = *c; 435 *s++ = '/'; 436 } 437 while(s[-1] == '/') 438 s--; 439 *s = '\0'; 440 } 441 442 int 443 qidhash(ulong path) 444 { 445 int h, n; 446 447 h = 0; 448 for(n=0; n<32; n+=Nqidbits){ 449 h ^= path; 450 path >>= Nqidbits; 451 } 452 return h & (Nqidtab-1); 453 } 454 455 void 456 freeqid(Qidtab *q) 457 { 458 ulong h; 459 Qidtab *l; 460 461 q->ref--; 462 if(q->ref > 0) 463 return; 464 qfreecnt++; 465 h = qidhash(q->path); 466 if(qidtab[h] == q) 467 qidtab[h] = q->next; 468 else{ 469 for(l=qidtab[h]; l->next!=q; l=l->next) 470 if(l->next == nil) 471 fatal("bad qid list"); 472 l->next = q->next; 473 } 474 free(q); 475 } 476 477 Qidtab* 478 qidlookup(Dir *d) 479 { 480 ulong h; 481 Qidtab *q; 482 483 h = qidhash(d->qid.path); 484 for(q=qidtab[h]; q!=nil; q=q->next) 485 if(q->type==d->type && q->dev==d->dev && q->path==d->qid.path) 486 return q; 487 return nil; 488 } 489 490 int 491 qidexists(ulong path) 492 { 493 int h; 494 Qidtab *q; 495 496 for(h=0; h<Nqidtab; h++) 497 for(q=qidtab[h]; q!=nil; q=q->next) 498 if(q->uniqpath == path) 499 return 1; 500 return 0; 501 } 502 503 Qidtab* 504 uniqueqid(Dir *d) 505 { 506 ulong h, path; 507 Qidtab *q; 508 509 q = qidlookup(d); 510 if(q != nil){ 511 q->ref++; 512 return q; 513 } 514 path = d->qid.path; 515 while(qidexists(path)){ 516 /* collision: find a new one */ 517 ncollision++; 518 DEBUG(DFD, "collision on %s\n", d->name); 519 path = newqid--; 520 if(newqid == 0) 521 newqid = ~QIDMODE; 522 path |= d->qid.path & (CHDIR|CHAPPEND|CHEXCL|CHMOUNT); 523 } 524 q = mallocz(sizeof(Qidtab), 1); 525 if(q == nil) 526 fatal("no memory for qid table"); 527 qidcnt++; 528 q->ref = 1; 529 q->type = d->type; 530 q->dev = d->dev; 531 q->path = d->qid.path; 532 q->uniqpath = path; 533 h = qidhash(d->qid.path); 534 q->next = qidtab[h]; 535 qidtab[h] = q; 536 return q; 537 } 538 539 void 540 fatal(char *s) 541 { 542 char buf[128]; 543 Proc *m; 544 545 sprint(buf, "exportfs: %r: %s", s); 546 547 /* Clear away the slave children */ 548 for(m = Proclist; m; m = m->next) 549 postnote(PNPROC, m->pid, "kill"); 550 551 DEBUG(DFD, "%s\n", buf); 552 exits(buf); 553 } 554 555 /* Network on fd1, mount driver on fd0 */ 556 int 557 filter(int fd) 558 { 559 int p[2]; 560 561 if(pipe(p) < 0) 562 fatal("pipe"); 563 564 switch(rfork(RFNOWAIT|RFPROC|RFFDG)) { 565 case -1: 566 fatal("rfork record module"); 567 case 0: 568 dup(fd, 1); 569 close(fd); 570 dup(p[0], 0); 571 close(p[0]); 572 close(p[1]); 573 execl("/bin/aux/fcall", "fcall", 0); 574 fatal("exec record module"); 575 default: 576 close(fd); 577 close(p[0]); 578 } 579 return p[1]; 580 } 581