1 #include <u.h> 2 #include <libc.h> 3 #include <auth.h> 4 #include <fcall.h> 5 #include <thread.h> 6 #include <9p.h> 7 8 static char Ebadattach[] = "unknown specifier in attach"; 9 static char Ebadoffset[] = "bad offset"; 10 static char Ebadcount[] = "bad count"; 11 static char Ebotch[] = "9P protocol botch"; 12 static char Ecreatenondir[] = "create in non-directory"; 13 static char Edupfid[] = "duplicate fid"; 14 static char Eduptag[] = "duplicate tag"; 15 static char Eisdir[] = "is a directory"; 16 static char Enocreate[] = "create prohibited"; 17 static char Enomem[] = "out of memory"; 18 static char Enoremove[] = "remove prohibited"; 19 static char Enostat[] = "stat prohibited"; 20 static char Enotfound[] = "file not found"; 21 static char Enowrite[] = "write prohibited"; 22 static char Enowstat[] = "wstat prohibited"; 23 static char Eperm[] = "permission denied"; 24 static char Eunknownfid[] = "unknown fid"; 25 static char Ebaddir[] = "bad directory in wstat"; 26 static char Ewalknodir[] = "walk in non-directory"; 27 28 static void 29 setfcallerror(Fcall *f, char *err) 30 { 31 f->ename = err; 32 f->type = Rerror; 33 } 34 35 static void 36 changemsize(Srv *srv, int msize) 37 { 38 if(srv->rbuf && srv->wbuf && srv->msize == msize) 39 return; 40 qlock(&srv->rlock); 41 qlock(&srv->wlock); 42 srv->msize = msize; 43 free(srv->rbuf); 44 free(srv->wbuf); 45 srv->rbuf = emalloc9p(msize); 46 srv->wbuf = emalloc9p(msize); 47 qunlock(&srv->rlock); 48 qunlock(&srv->wlock); 49 } 50 51 static Req* 52 getreq(Srv *s) 53 { 54 long n; 55 uchar *buf; 56 Fcall f; 57 Req *r; 58 59 qlock(&s->rlock); 60 if((n = read9pmsg(s->infd, s->rbuf, s->msize)) <= 0){ 61 qunlock(&s->rlock); 62 return nil; 63 } 64 65 buf = emalloc9p(n); 66 memmove(buf, s->rbuf, n); 67 qunlock(&s->rlock); 68 69 if(convM2S(buf, n, &f) != n){ 70 free(buf); 71 return nil; 72 } 73 74 if((r=allocreq(s->rpool, f.tag)) == nil){ /* duplicate tag: cons up a fake Req */ 75 r = emalloc9p(sizeof *r); 76 incref(&r->ref); 77 r->tag = f.tag; 78 r->ifcall = f; 79 r->error = Eduptag; 80 r->buf = buf; 81 r->responded = 0; 82 r->type = 0; 83 r->srv = s; 84 r->pool = nil; 85 if(chatty9p) 86 fprint(2, "<-%d- %F: dup tag\n", s->infd, &f); 87 return r; 88 } 89 90 r->srv = s; 91 r->responded = 0; 92 r->buf = buf; 93 r->ifcall = f; 94 memset(&r->ofcall, 0, sizeof r->ofcall); 95 r->type = r->ifcall.type; 96 97 if(chatty9p) 98 if(r->error) 99 fprint(2, "<-%d- %F: %s\n", s->infd, &r->ifcall, r->error); 100 else 101 fprint(2, "<-%d- %F\n", s->infd, &r->ifcall); 102 103 return r; 104 } 105 106 static void 107 filewalk(Req *r) 108 { 109 int i; 110 File *f; 111 112 f = r->fid->file; 113 assert(f != nil); 114 115 incref(f); 116 for(i=0; i<r->ifcall.nwname; i++) 117 if(f = walkfile(f, r->ifcall.wname[i])) 118 r->ofcall.wqid[i] = f->qid; 119 else 120 break; 121 122 r->ofcall.nwqid = i; 123 if(f){ 124 r->newfid->file = f; 125 r->newfid->qid = r->newfid->file->qid; 126 } 127 respond(r, nil); 128 } 129 130 void 131 walkandclone(Req *r, char *(*walk1)(Fid*, char*, void*), char *(*clone)(Fid*, Fid*, void*), void *arg) 132 { 133 int i; 134 char *e; 135 136 if(r->fid == r->newfid && r->ifcall.nwname > 1){ 137 respond(r, "lib9p: unused documented feature not implemented"); 138 return; 139 } 140 141 if(r->fid != r->newfid){ 142 r->newfid->qid = r->fid->qid; 143 if(clone && (e = clone(r->fid, r->newfid, arg))){ 144 respond(r, e); 145 return; 146 } 147 } 148 149 e = nil; 150 for(i=0; i<r->ifcall.nwname; i++){ 151 if(e = walk1(r->newfid, r->ifcall.wname[i], arg)) 152 break; 153 r->ofcall.wqid[i] = r->newfid->qid; 154 } 155 156 r->ofcall.nwqid = i; 157 if(e && i==0) 158 respond(r, e); 159 else 160 respond(r, nil); 161 } 162 163 static void 164 sversion(Srv*, Req *r) 165 { 166 if(strncmp(r->ifcall.version, "9P", 2) != 0){ 167 r->ofcall.version = "unknown"; 168 respond(r, nil); 169 return; 170 } 171 172 r->ofcall.version = "9P2000"; 173 r->ofcall.msize = r->ifcall.msize; 174 respond(r, nil); 175 } 176 static void 177 rversion(Req *r, char *error) 178 { 179 assert(error == nil); 180 changemsize(r->srv, r->ofcall.msize); 181 } 182 183 static void 184 sauth(Srv *srv, Req *r) 185 { 186 char e[ERRMAX]; 187 188 if((r->afid = allocfid(srv->fpool, r->ifcall.afid)) == nil){ 189 respond(r, Edupfid); 190 return; 191 } 192 if(srv->auth) 193 srv->auth(r); 194 else{ 195 snprint(e, sizeof e, "%s: authentication not required", argv0); 196 respond(r, e); 197 } 198 } 199 static void 200 rauth(Req *r, char *error) 201 { 202 if(error && r->afid) 203 closefid(removefid(r->srv->fpool, r->afid->fid)); 204 } 205 206 static void 207 sattach(Srv *srv, Req *r) 208 { 209 if((r->fid = allocfid(srv->fpool, r->ifcall.fid)) == nil){ 210 respond(r, Edupfid); 211 return; 212 } 213 r->afid = nil; 214 if(r->ifcall.afid != NOFID && (r->afid = lookupfid(srv->fpool, r->ifcall.afid)) == nil){ 215 respond(r, Eunknownfid); 216 return; 217 } 218 r->fid->uid = estrdup9p(r->ifcall.uname); 219 if(srv->tree){ 220 r->fid->file = srv->tree->root; 221 /* BUG? incref(r->fid->file) ??? */ 222 r->ofcall.qid = r->fid->file->qid; 223 r->fid->qid = r->ofcall.qid; 224 } 225 if(srv->attach) 226 srv->attach(r); 227 else 228 respond(r, nil); 229 return; 230 } 231 static void 232 rattach(Req *r, char *error) 233 { 234 if(error && r->fid) 235 closefid(removefid(r->srv->fpool, r->fid->fid)); 236 } 237 238 static void 239 sflush(Srv *srv, Req *r) 240 { 241 r->oldreq = lookupreq(srv->rpool, r->ifcall.oldtag); 242 if(r->oldreq == nil || r->oldreq == r) 243 respond(r, nil); 244 else if(srv->flush) 245 srv->flush(r); 246 else 247 respond(r, nil); 248 } 249 static int 250 rflush(Req *r, char *error) 251 { 252 Req *or; 253 254 assert(error == nil); 255 or = r->oldreq; 256 if(or){ 257 qlock(&or->lk); 258 if(or->responded == 0){ 259 or->flush = erealloc9p(or->flush, (or->nflush+1)*sizeof(or->flush[0])); 260 or->flush[or->nflush++] = r; 261 qunlock(&or->lk); 262 return -1; /* delay response until or is responded */ 263 } 264 qunlock(&or->lk); 265 closereq(or); 266 } 267 r->oldreq = nil; 268 return 0; 269 } 270 271 static char* 272 oldwalk1(Fid *fid, char *name, void *arg) 273 { 274 char *e; 275 Qid qid; 276 Srv *srv; 277 278 srv = arg; 279 e = srv->walk1(fid, name, &qid); 280 if(e) 281 return e; 282 fid->qid = qid; 283 return nil; 284 } 285 286 static char* 287 oldclone(Fid *fid, Fid *newfid, void *arg) 288 { 289 Srv *srv; 290 291 srv = arg; 292 if(srv->clone == nil) 293 return nil; 294 return srv->clone(fid, newfid); 295 } 296 297 static void 298 swalk(Srv *srv, Req *r) 299 { 300 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){ 301 respond(r, Eunknownfid); 302 return; 303 } 304 if(r->fid->omode != -1){ 305 respond(r, "cannot clone open fid"); 306 return; 307 } 308 if(r->ifcall.nwname && !(r->fid->qid.type&QTDIR)){ 309 respond(r, Ewalknodir); 310 return; 311 } 312 if(r->ifcall.fid != r->ifcall.newfid){ 313 if((r->newfid = allocfid(srv->fpool, r->ifcall.newfid)) == nil){ 314 respond(r, Edupfid); 315 return; 316 } 317 r->newfid->uid = estrdup9p(r->fid->uid); 318 }else{ 319 incref(&r->fid->ref); 320 r->newfid = r->fid; 321 } 322 if(r->fid->file){ 323 filewalk(r); 324 }else if(srv->walk1) 325 walkandclone(r, oldwalk1, oldclone, srv); 326 else if(srv->walk) 327 srv->walk(r); 328 else 329 sysfatal("no walk function, no file trees"); 330 } 331 static void 332 rwalk(Req *r, char *error) 333 { 334 if(error || r->ofcall.nwqid < r->ifcall.nwname){ 335 if(r->ifcall.fid != r->ifcall.newfid && r->newfid) 336 closefid(removefid(r->srv->fpool, r->newfid->fid)); 337 if (r->ofcall.nwqid==0){ 338 if(error==nil && r->ifcall.nwname!=0) 339 r->error = Enotfound; 340 }else 341 r->error = nil; // No error on partial walks 342 }else{ 343 if(r->ofcall.nwqid == 0){ 344 /* Just a clone */ 345 r->newfid->qid = r->fid->qid; 346 }else{ 347 /* if file trees are in use, filewalk took care of the rest */ 348 r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1]; 349 } 350 } 351 } 352 353 static void 354 sopen(Srv *srv, Req *r) 355 { 356 int p; 357 358 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){ 359 respond(r, Eunknownfid); 360 return; 361 } 362 if(r->fid->omode != -1){ 363 respond(r, Ebotch); 364 return; 365 } 366 if((r->fid->qid.type&QTDIR) && (r->ifcall.mode&~ORCLOSE) != OREAD){ 367 respond(r, Eisdir); 368 return; 369 } 370 r->ofcall.qid = r->fid->qid; 371 switch(r->ifcall.mode&3){ 372 default: 373 assert(0); 374 case OREAD: 375 p = AREAD; 376 break; 377 case OWRITE: 378 p = AWRITE; 379 break; 380 case ORDWR: 381 p = AREAD|AWRITE; 382 break; 383 case OEXEC: 384 p = AEXEC; 385 break; 386 } 387 if(r->ifcall.mode&OTRUNC) 388 p |= AWRITE; 389 if((r->fid->qid.type&QTDIR) && p!=AREAD){ 390 respond(r, Eperm); 391 return; 392 } 393 if(r->fid->file){ 394 if(!hasperm(r->fid->file, r->fid->uid, p)){ 395 respond(r, Eperm); 396 return; 397 } 398 /* BUG RACE */ 399 if((r->ifcall.mode&ORCLOSE) 400 && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){ 401 respond(r, Eperm); 402 return; 403 } 404 r->ofcall.qid = r->fid->file->qid; 405 if((r->ofcall.qid.type&QTDIR) 406 && (r->fid->rdir = opendirfile(r->fid->file)) == nil){ 407 respond(r, "opendirfile failed"); 408 return; 409 } 410 } 411 if(srv->open) 412 srv->open(r); 413 else 414 respond(r, nil); 415 } 416 static void 417 ropen(Req *r, char *error) 418 { 419 char errbuf[ERRMAX]; 420 if(error) 421 return; 422 if(chatty9p){ 423 snprint(errbuf, sizeof errbuf, "fid mode is 0x%ux\n", r->ifcall.mode); 424 write(2, errbuf, strlen(errbuf)); 425 } 426 r->fid->omode = r->ifcall.mode; 427 r->fid->qid = r->ofcall.qid; 428 if(r->ofcall.qid.type&QTDIR) 429 r->fid->diroffset = 0; 430 } 431 432 static void 433 screate(Srv *srv, Req *r) 434 { 435 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil) 436 respond(r, Eunknownfid); 437 else if(r->fid->omode != -1) 438 respond(r, Ebotch); 439 else if(!(r->fid->qid.type&QTDIR)) 440 respond(r, Ecreatenondir); 441 else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE)) 442 respond(r, Eperm); 443 else if(srv->create) 444 srv->create(r); 445 else 446 respond(r, Enocreate); 447 } 448 static void 449 rcreate(Req *r, char *error) 450 { 451 if(error) 452 return; 453 r->fid->omode = r->ifcall.mode; 454 r->fid->qid = r->ofcall.qid; 455 } 456 457 static void 458 sread(Srv *srv, Req *r) 459 { 460 int o; 461 462 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){ 463 respond(r, Eunknownfid); 464 return; 465 } 466 if(r->ifcall.count < 0){ 467 respond(r, Ebotch); 468 return; 469 } 470 if(r->ifcall.offset < 0 471 || ((r->fid->qid.type&QTDIR) && r->ifcall.offset != 0 && r->ifcall.offset != r->fid->diroffset)){ 472 respond(r, Ebadoffset); 473 return; 474 } 475 476 if(r->ifcall.count > srv->msize - IOHDRSZ) 477 r->ifcall.count = srv->msize - IOHDRSZ; 478 r->rbuf = emalloc9p(r->ifcall.count); 479 r->ofcall.data = r->rbuf; 480 o = r->fid->omode & 3; 481 if(o != OREAD && o != ORDWR && o != OEXEC){ 482 respond(r, Ebotch); 483 return; 484 } 485 if((r->fid->qid.type&QTDIR) && r->fid->file){ 486 r->ofcall.count = readdirfile(r->fid->rdir, r->rbuf, r->ifcall.count); 487 respond(r, nil); 488 return; 489 } 490 if(srv->read) 491 srv->read(r); 492 else 493 respond(r, "no srv->read"); 494 } 495 static void 496 rread(Req *r, char *error) 497 { 498 if(error==nil && (r->fid->qid.type&QTDIR)) 499 r->fid->diroffset += r->ofcall.count; 500 } 501 502 static void 503 swrite(Srv *srv, Req *r) 504 { 505 int o; 506 char e[ERRMAX]; 507 508 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){ 509 respond(r, Eunknownfid); 510 return; 511 } 512 if(r->ifcall.count < 0){ 513 respond(r, Ebotch); 514 return; 515 } 516 if(r->ifcall.offset < 0){ 517 respond(r, Ebotch); 518 return; 519 } 520 if(r->ifcall.count > srv->msize - IOHDRSZ) 521 r->ifcall.count = srv->msize - IOHDRSZ; 522 o = r->fid->omode & 3; 523 if(o != OWRITE && o != ORDWR){ 524 snprint(e, sizeof e, "write on fid with open mode 0x%ux", r->fid->omode); 525 respond(r, e); 526 return; 527 } 528 if(srv->write) 529 srv->write(r); 530 else 531 respond(r, "no srv->write"); 532 } 533 static void 534 rwrite(Req *r, char *error) 535 { 536 if(error) 537 return; 538 if(r->fid->file) 539 r->fid->file->qid.vers++; 540 } 541 542 static void 543 sclunk(Srv *srv, Req *r) 544 { 545 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil) 546 respond(r, Eunknownfid); 547 else 548 respond(r, nil); 549 } 550 static void 551 rclunk(Req*, char*) 552 { 553 } 554 555 static void 556 sremove(Srv *srv, Req *r) 557 { 558 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil){ 559 respond(r, Eunknownfid); 560 return; 561 } 562 /* BUG RACE */ 563 if(r->fid->file && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){ 564 respond(r, Eperm); 565 return; 566 } 567 if(srv->remove) 568 srv->remove(r); 569 else 570 respond(r, r->fid->file ? nil : Enoremove); 571 } 572 static void 573 rremove(Req *r, char *error, char *errbuf) 574 { 575 if(error) 576 return; 577 if(r->fid->file){ 578 if(removefile(r->fid->file) < 0){ 579 snprint(errbuf, ERRMAX, "remove %s: %r", 580 r->fid->file->name); 581 r->error = errbuf; 582 } 583 r->fid->file = nil; 584 } 585 } 586 587 static void 588 sstat(Srv *srv, Req *r) 589 { 590 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){ 591 respond(r, Eunknownfid); 592 return; 593 } 594 if(r->fid->file){ 595 r->d = r->fid->file->Dir; 596 if(r->d.name) 597 r->d.name = estrdup9p(r->d.name); 598 if(r->d.uid) 599 r->d.uid = estrdup9p(r->d.uid); 600 if(r->d.gid) 601 r->d.gid = estrdup9p(r->d.gid); 602 if(r->d.muid) 603 r->d.muid = estrdup9p(r->d.muid); 604 } 605 if(srv->stat) 606 srv->stat(r); 607 else if(r->fid->file) 608 respond(r, nil); 609 else 610 respond(r, Enostat); 611 } 612 static void 613 rstat(Req *r, char *error) 614 { 615 int n; 616 uchar *statbuf; 617 uchar tmp[BIT16SZ]; 618 619 if(error) 620 return; 621 if(convD2M(&r->d, tmp, BIT16SZ) != BIT16SZ){ 622 r->error = "convD2M(_,_,BIT16SZ) did not return BIT16SZ"; 623 return; 624 } 625 n = GBIT16(tmp)+BIT16SZ; 626 statbuf = emalloc9p(n); 627 if(statbuf == nil){ 628 r->error = "out of memory"; 629 return; 630 } 631 r->ofcall.nstat = convD2M(&r->d, statbuf, n); 632 r->ofcall.stat = statbuf; /* freed in closereq */ 633 if(r->ofcall.nstat < 0){ 634 r->error = "convD2M fails"; 635 free(statbuf); 636 return; 637 } 638 } 639 640 static void 641 swstat(Srv *srv, Req *r) 642 { 643 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){ 644 respond(r, Eunknownfid); 645 return; 646 } 647 if(srv->wstat == nil){ 648 respond(r, Enowstat); 649 return; 650 } 651 if(convM2D(r->ifcall.stat, r->ifcall.nstat, &r->d, (char*)r->ifcall.stat) != r->ifcall.nstat){ 652 respond(r, Ebaddir); 653 return; 654 } 655 if((ushort)~r->d.type){ 656 respond(r, "wstat -- attempt to change type"); 657 return; 658 } 659 if((uint)~r->d.dev){ 660 respond(r, "wstat -- attempt to change dev"); 661 return; 662 } 663 if((uchar)~r->d.qid.type || (ulong)~r->d.qid.vers || (uvlong)~r->d.qid.path){ 664 respond(r, "wstat -- attempt to change qid"); 665 return; 666 } 667 if(r->d.muid && r->d.muid[0]){ 668 respond(r, "wstat -- attempt to change muid"); 669 return; 670 } 671 if((ulong)~r->d.mode && ((r->d.mode&DMDIR)>>24) != (r->fid->qid.type&QTDIR)){ 672 respond(r, "wstat -- attempt to change DMDIR bit"); 673 return; 674 } 675 srv->wstat(r); 676 } 677 static void 678 rwstat(Req*, char*) 679 { 680 } 681 682 void 683 srv(Srv *srv) 684 { 685 Req *r; 686 687 fmtinstall('D', dirfmt); 688 fmtinstall('F', fcallfmt); 689 690 if(srv->fpool == nil) 691 srv->fpool = allocfidpool(srv->destroyfid); 692 if(srv->rpool == nil) 693 srv->rpool = allocreqpool(srv->destroyreq); 694 if(srv->msize == 0) 695 srv->msize = 8192+IOHDRSZ; 696 697 changemsize(srv, srv->msize); 698 699 srv->fpool->srv = srv; 700 srv->rpool->srv = srv; 701 702 while(r = getreq(srv)){ 703 if(r->error){ 704 respond(r, r->error); 705 continue; 706 } 707 switch(r->ifcall.type){ 708 default: 709 respond(r, "unknown message"); 710 break; 711 case Tversion: sversion(srv, r); break; 712 case Tauth: sauth(srv, r); break; 713 case Tattach: sattach(srv, r); break; 714 case Tflush: sflush(srv, r); break; 715 case Twalk: swalk(srv, r); break; 716 case Topen: sopen(srv, r); break; 717 case Tcreate: screate(srv, r); break; 718 case Tread: sread(srv, r); break; 719 case Twrite: swrite(srv, r); break; 720 case Tclunk: sclunk(srv, r); break; 721 case Tremove: sremove(srv, r); break; 722 case Tstat: sstat(srv, r); break; 723 case Twstat: swstat(srv, r); break; 724 } 725 } 726 727 if(srv->end) 728 srv->end(srv); 729 } 730 731 void 732 respond(Req *r, char *error) 733 { 734 int i, m, n; 735 char errbuf[ERRMAX]; 736 Srv *srv; 737 738 srv = r->srv; 739 assert(srv != nil); 740 741 assert(r->responded == 0); 742 r->error = error; 743 744 switch(r->ifcall.type){ 745 default: 746 assert(0); 747 /* 748 * Flush is special. If the handler says so, we return 749 * without further processing. Respond will be called 750 * again once it is safe. 751 */ 752 case Tflush: 753 if(rflush(r, error)<0) 754 return; 755 break; 756 case Tversion: rversion(r, error); break; 757 case Tauth: rauth(r, error); break; 758 case Tattach: rattach(r, error); break; 759 case Twalk: rwalk(r, error); break; 760 case Topen: ropen(r, error); break; 761 case Tcreate: rcreate(r, error); break; 762 case Tread: rread(r, error); break; 763 case Twrite: rwrite(r, error); break; 764 case Tclunk: rclunk(r, error); break; 765 case Tremove: rremove(r, error, errbuf); break; 766 case Tstat: rstat(r, error); break; 767 case Twstat: rwstat(r, error); break; 768 } 769 770 r->ofcall.tag = r->ifcall.tag; 771 r->ofcall.type = r->ifcall.type+1; 772 if(r->error) 773 setfcallerror(&r->ofcall, r->error); 774 775 if(chatty9p) 776 fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall); 777 778 qlock(&srv->wlock); 779 n = convS2M(&r->ofcall, srv->wbuf, srv->msize); 780 if(n <= 0){ 781 fprint(2, "n = %d %F\n", n, &r->ofcall); 782 abort(); 783 } 784 assert(n > 2); 785 if(r->pool) /* not a fake */ 786 closereq(removereq(r->pool, r->ifcall.tag)); 787 m = write(srv->outfd, srv->wbuf, n); 788 if(m != n) 789 sysfatal("lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd); 790 qunlock(&srv->wlock); 791 792 qlock(&r->lk); /* no one will add flushes now */ 793 r->responded = 1; 794 qunlock(&r->lk); 795 796 for(i=0; i<r->nflush; i++) 797 respond(r->flush[i], nil); 798 free(r->flush); 799 800 if(r->pool) 801 closereq(r); 802 else 803 free(r); 804 } 805 806 int 807 postfd(char *name, int pfd) 808 { 809 int fd; 810 char buf[80]; 811 812 snprint(buf, sizeof buf, "/srv/%s", name); 813 if(chatty9p) 814 fprint(2, "postfd %s\n", buf); 815 fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600); 816 if(fd < 0){ 817 if(chatty9p) 818 fprint(2, "create fails: %r\n"); 819 return -1; 820 } 821 if(fprint(fd, "%d", pfd) < 0){ 822 if(chatty9p) 823 fprint(2, "write fails: %r\n"); 824 close(fd); 825 return -1; 826 } 827 if(chatty9p) 828 fprint(2, "postfd successful\n"); 829 return 0; 830 } 831 832