fs.c (8928B)
1 #include "std.h" 2 #include "dat.h" 3 4 enum 5 { 6 Qroot, 7 Qfactotum, 8 Qrpc, 9 Qkeylist, 10 Qprotolist, 11 Qconfirm, 12 Qlog, 13 Qctl, 14 Qneedkey, 15 Qconv 16 }; 17 18 static int qtop; 19 20 Qid 21 mkqid(int type, int path) 22 { 23 Qid q; 24 25 q.type = type; 26 q.path = path; 27 q.vers = 0; 28 return q; 29 } 30 31 static struct 32 { 33 char *name; 34 int qidpath; 35 ulong perm; 36 } dirtab[] = { 37 /* positions of confirm and needkey known below */ 38 "confirm", Qconfirm, 0600|DMEXCL, 39 "needkey", Qneedkey, 0600|DMEXCL, 40 "ctl", Qctl, 0600, 41 "rpc", Qrpc, 0666, 42 "proto", Qprotolist, 0444, 43 "log", Qlog, 0600|DMEXCL, 44 "conv", Qconv, 0400 45 }; 46 47 static void 48 fillstat(Dir *dir, char *name, int type, int path, ulong perm) 49 { 50 dir->name = estrdup(name); 51 dir->uid = estrdup(owner); 52 dir->gid = estrdup(owner); 53 dir->mode = perm; 54 dir->length = 0; 55 dir->qid = mkqid(type, path); 56 dir->atime = time(0); 57 dir->mtime = time(0); 58 dir->muid = estrdup(""); 59 } 60 61 static int 62 rootdirgen(int n, Dir *dir, void *v) 63 { 64 USED(v); 65 66 if(n > 0) 67 return -1; 68 69 fillstat(dir, factname, QTDIR, Qfactotum, DMDIR|0555); 70 return 0; 71 } 72 73 static int 74 fsdirgen(int n, Dir *dir, void *v) 75 { 76 USED(v); 77 78 if(n >= nelem(dirtab)) 79 return -1; 80 fillstat(dir, dirtab[n].name, 0, dirtab[n].qidpath, dirtab[n].perm); 81 return 0; 82 } 83 84 static char* 85 fswalk1(Fid *fid, char *name, Qid *qid) 86 { 87 int i; 88 89 switch((int)fid->qid.path){ 90 default: 91 return "fswalk1: cannot happen"; 92 case Qroot: 93 if(strcmp(name, factname) == 0){ 94 *qid = mkqid(QTDIR, Qfactotum); 95 fid->qid = *qid; 96 return nil; 97 } 98 if(strcmp(name, "..") == 0){ 99 *qid = fid->qid; 100 return nil; 101 } 102 return "not found"; 103 case Qfactotum: 104 for(i=0; i<nelem(dirtab); i++) 105 if(strcmp(name, dirtab[i].name) == 0){ 106 *qid = mkqid(0, dirtab[i].qidpath); 107 fid->qid = *qid; 108 return nil; 109 } 110 if(strcmp(name, "..") == 0){ 111 *qid = mkqid(QTDIR, qtop); 112 fid->qid = *qid; 113 return nil; 114 } 115 return "not found"; 116 } 117 } 118 119 static void 120 fsstat(Req *r) 121 { 122 int i, path; 123 124 path = r->fid->qid.path; 125 switch(path){ 126 case Qroot: 127 fillstat(&r->d, "/", QTDIR, Qroot, 0555|DMDIR); 128 break; 129 case Qfactotum: 130 fillstat(&r->d, "factotum", QTDIR, Qfactotum, 0555|DMDIR); 131 break; 132 default: 133 for(i=0; i<nelem(dirtab); i++) 134 if(dirtab[i].qidpath == path){ 135 fillstat(&r->d, dirtab[i].name, 0, dirtab[i].qidpath, dirtab[i].perm); 136 goto Break2; 137 } 138 respond(r, "file not found"); 139 break; 140 } 141 Break2: 142 respond(r, nil); 143 } 144 145 static int 146 readlist(int off, int (*gen)(int, char*, uint), Req *r) 147 { 148 char *a, *ea; 149 int n; 150 151 a = r->ofcall.data; 152 ea = a+r->ifcall.count; 153 for(;;){ 154 n = (*gen)(off, a, ea-a); 155 if(n == 0){ 156 r->ofcall.count = a - (char*)r->ofcall.data; 157 return off; 158 } 159 a += n; 160 off++; 161 } 162 /* not reached */ 163 } 164 165 static int 166 keylist(int i, char *a, uint nn) 167 { 168 int n; 169 char buf[4096]; 170 Key *k; 171 172 if(i >= ring.nkey) 173 return 0; 174 175 k = ring.key[i]; 176 k->attr = sortattr(k->attr); 177 n = snprint(buf, sizeof buf, "key %A %N\n", k->attr, k->privattr); 178 if(n >= sizeof(buf)-5) 179 strcpy(buf+sizeof(buf)-5, "...\n"); 180 n = strlen(buf); 181 if(n > nn) 182 return 0; 183 memmove(a, buf, n); 184 return n; 185 } 186 187 static int 188 protolist(int i, char *a, uint n) 189 { 190 if(prototab[i] == nil) 191 return 0; 192 if(strlen(prototab[i]->name)+1 > n) 193 return 0; 194 n = strlen(prototab[i]->name)+1; 195 memmove(a, prototab[i]->name, n-1); 196 a[n-1] = '\n'; 197 return n; 198 } 199 200 /* BUG this is O(n^2) to fill in the list */ 201 static int 202 convlist(int i, char *a, uint nn) 203 { 204 Conv *c; 205 char buf[512]; 206 int n; 207 208 for(c=conv; c && i-- > 0; c=c->next) 209 ; 210 211 if(c == nil) 212 return 0; 213 214 if(c->state) 215 n = snprint(buf, sizeof buf, "conv state=%q %A\n", c->state, c->attr); 216 else 217 n = snprint(buf, sizeof buf, "conv state=closed err=%q\n", c->err); 218 219 if(n >= sizeof(buf)-5) 220 strcpy(buf+sizeof(buf)-5, "...\n"); 221 n = strlen(buf); 222 if(n > nn) 223 return 0; 224 memmove(a, buf, n); 225 return n; 226 } 227 228 static void 229 fskickreply(Conv *c) 230 { 231 Req *r; 232 233 if(c->hangup){ 234 if((r = c->req) != nil){ 235 c->req = nil; 236 respond(r, "hangup"); 237 } 238 return; 239 } 240 241 if(!c->req || !c->nreply) 242 return; 243 244 r = c->req; 245 r->ofcall.count = c->nreply; 246 r->ofcall.data = c->reply; 247 if(r->ofcall.count > r->ifcall.count) 248 r->ofcall.count = r->ifcall.count; 249 c->req = nil; 250 respond(r, nil); 251 c->nreply = 0; 252 } 253 254 /* 255 * Some of the file system work happens in the fs proc, but 256 * fsopen, fsread, fswrite, fsdestroyfid, and fsflush happen in 257 * the main proc so that they can access the various shared 258 * data structures without worrying about locking. 259 */ 260 static int inuse[nelem(dirtab)]; 261 int *confirminuse = &inuse[0]; 262 int *needkeyinuse = &inuse[1]; 263 int *loginuse = &inuse[5]; 264 static void 265 fsopen(Req *r) 266 { 267 int i, *inusep, perm; 268 static int need[4] = { 4, 2, 6, 1 }; 269 Conv *c; 270 271 inusep = nil; 272 perm = 5; /* directory */ 273 for(i=0; i<nelem(dirtab); i++) 274 if(dirtab[i].qidpath == r->fid->qid.path){ 275 if(dirtab[i].perm & DMEXCL) 276 inusep = &inuse[i]; 277 if(strcmp(r->fid->uid, owner) == 0) 278 perm = dirtab[i].perm>>6; 279 else 280 perm = dirtab[i].perm; 281 break; 282 } 283 284 if((r->ifcall.mode&~(OMASK|OTRUNC)) 285 || (need[r->ifcall.mode&3] & ~perm)){ 286 respond(r, "permission denied"); 287 return; 288 } 289 290 if(inusep){ 291 if(*inusep){ 292 respond(r, "file in use"); 293 return; 294 } 295 *inusep = 1; 296 } 297 298 if(r->fid->qid.path == Qrpc){ 299 if((c = convalloc(r->fid->uid)) == nil){ 300 char e[ERRMAX]; 301 302 rerrstr(e, sizeof e); 303 respond(r, e); 304 return; 305 } 306 c->kickreply = fskickreply; 307 r->fid->aux = c; 308 } 309 310 respond(r, nil); 311 } 312 313 static void 314 fsread(Req *r) 315 { 316 Conv *c; 317 318 switch((int)r->fid->qid.path){ 319 default: 320 respond(r, "fsread: cannot happen"); 321 break; 322 case Qroot: 323 dirread9p(r, rootdirgen, nil); 324 respond(r, nil); 325 break; 326 case Qfactotum: 327 dirread9p(r, fsdirgen, nil); 328 respond(r, nil); 329 break; 330 case Qrpc: 331 c = r->fid->aux; 332 if(c->rpc.op == RpcUnknown){ 333 respond(r, "no rpc pending"); 334 break; 335 } 336 if(c->req){ 337 respond(r, "read already pending"); 338 break; 339 } 340 c->req = r; 341 if(c->nreply) 342 (*c->kickreply)(c); 343 else 344 rpcexec(c); 345 break; 346 case Qconfirm: 347 confirmread(r); 348 break; 349 case Qlog: 350 logread(r); 351 break; 352 case Qctl: 353 r->fid->aux = (void*)(uintptr)readlist((uintptr)r->fid->aux, keylist, r); 354 respond(r, nil); 355 break; 356 case Qneedkey: 357 needkeyread(r); 358 break; 359 case Qprotolist: 360 r->fid->aux = (void*)(uintptr)readlist((uintptr)r->fid->aux, protolist, r); 361 respond(r, nil); 362 break; 363 case Qconv: 364 r->fid->aux = (void*)(uintptr)readlist((uintptr)r->fid->aux, convlist, r); 365 respond(r, nil); 366 break; 367 } 368 } 369 370 static void 371 fswrite(Req *r) 372 { 373 int ret; 374 char err[ERRMAX], *s; 375 int (*strfn)(char*); 376 char *name; 377 378 switch((int)r->fid->qid.path){ 379 default: 380 respond(r, "fswrite: cannot happen"); 381 break; 382 case Qrpc: 383 if(rpcwrite(r->fid->aux, r->ifcall.data, r->ifcall.count) < 0){ 384 rerrstr(err, sizeof err); 385 respond(r, err); 386 }else{ 387 r->ofcall.count = r->ifcall.count; 388 respond(r, nil); 389 } 390 break; 391 case Qneedkey: 392 name = "needkey"; 393 strfn = needkeywrite; 394 goto string; 395 case Qctl: 396 name = "ctl"; 397 strfn = ctlwrite; 398 goto string; 399 case Qconfirm: 400 name = "confirm"; 401 strfn = confirmwrite; 402 string: 403 s = emalloc(r->ifcall.count+1); 404 memmove(s, r->ifcall.data, r->ifcall.count); 405 s[r->ifcall.count] = '\0'; 406 ret = (*strfn)(s); 407 free(s); 408 if(ret < 0){ 409 rerrstr(err, sizeof err); 410 respond(r, err); 411 flog("write %s: %s", name, err); 412 }else{ 413 r->ofcall.count = r->ifcall.count; 414 respond(r, nil); 415 } 416 break; 417 } 418 } 419 420 static void 421 fsflush(Req *r) 422 { 423 confirmflush(r->oldreq); 424 logflush(r->oldreq); 425 respond(r, nil); 426 } 427 428 static void 429 fsdestroyfid(Fid *fid) 430 { 431 if(fid->qid.path == Qrpc && fid->aux){ 432 convhangup(fid->aux); 433 convclose(fid->aux); 434 } 435 } 436 437 static Channel *creq; 438 static Channel *cfid, *cfidr; 439 440 static void 441 fsreqthread(void *v) 442 { 443 Req *r; 444 445 USED(v); 446 447 while((r = recvp(creq)) != nil){ 448 switch(r->ifcall.type){ 449 default: 450 respond(r, "bug in fsreqthread"); 451 break; 452 case Topen: 453 fsopen(r); 454 break; 455 case Tread: 456 fsread(r); 457 break; 458 case Twrite: 459 fswrite(r); 460 break; 461 case Tflush: 462 fsflush(r); 463 break; 464 } 465 } 466 } 467 468 static void 469 fsclunkthread(void *v) 470 { 471 Fid *f; 472 473 USED(v); 474 475 while((f = recvp(cfid)) != nil){ 476 fsdestroyfid(f); 477 sendp(cfidr, 0); 478 } 479 } 480 481 static void 482 fsproc(void *v) 483 { 484 USED(v); 485 486 threadcreate(fsreqthread, nil, STACK); 487 threadcreate(fsclunkthread, nil, STACK); 488 threadexits(nil); 489 } 490 491 static void 492 fsattach(Req *r) 493 { 494 r->fid->qid = mkqid(QTDIR, qtop); 495 r->ofcall.qid = r->fid->qid; 496 respond(r, nil); 497 } 498 499 static void 500 fssend(Req *r) 501 { 502 sendp(creq, r); 503 } 504 505 static void 506 fssendclunk(Fid *f) 507 { 508 sendp(cfid, f); 509 recvp(cfidr); 510 } 511 512 void 513 fsstart(Srv *s) 514 { 515 USED(s); 516 517 if(extrafactotumdir) 518 qtop = Qroot; 519 else 520 qtop = Qfactotum; 521 creq = chancreate(sizeof(Req*), 0); 522 cfid = chancreate(sizeof(Fid*), 0); 523 cfidr = chancreate(sizeof(Fid*), 0); 524 proccreate(fsproc, nil, STACK); 525 } 526 527 Srv fs; 528 529 void 530 fsinit0(void) 531 { 532 fs.attach = fsattach; 533 fs.walk1 = fswalk1; 534 fs.open = fssend; 535 fs.read = fssend; 536 fs.write = fssend; 537 fs.stat = fsstat; 538 fs.flush = fssend; 539 fs.destroyfid = fssendclunk; 540 fs.start = fsstart; 541 }