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