fs.c (14835B)
1 /* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail> 2 * See LICENSE file for license details. 3 */ 4 #include "dat.h" 5 #include <ctype.h> 6 #include <stdarg.h> 7 #include <time.h> 8 #include "fns.h" 9 10 typedef union IxpFileIdU IxpFileIdU; 11 union IxpFileIdU { 12 Bar* bar; 13 Bar** bar_p; 14 CTuple* col; 15 Client* client; 16 Ruleset* rule; 17 View* view; 18 char* buf; 19 void* ref; 20 }; 21 22 #include <ixp_srvutil.h> 23 24 static IxpPending events; 25 static IxpPending pdebug[NDebugOpt]; 26 27 /* Constants */ 28 enum { /* Dirs */ 29 FsDBars, 30 FsDClient, 31 FsDClients, 32 FsDDebug, 33 FsDTag, 34 FsDTags, 35 FsRoot, 36 /* Files */ 37 FsFBar, 38 FsFCctl, 39 FsFClabel, 40 FsFColRules, 41 FsFCtags, 42 FsFDebug, 43 FsFEvent, 44 FsFKeys, 45 FsFRctl, 46 FsFRules, 47 FsFTctl, 48 FsFTindex, 49 FsFprops, 50 }; 51 52 /* Error messages */ 53 static char 54 Enoperm[] = "permission denied", 55 Enofile[] = "file not found", 56 Ebadvalue[] = "bad value", 57 Einterrupted[] = "interrupted"; 58 59 /* Macros */ 60 #define QID(t, i) (((vlong)((t)&0xFF)<<32)|((i)&0xFFFFFFFF)) 61 62 /* Global Vars */ 63 /***************/ 64 Ixp9Srv p9srv = { 65 .open= fs_open, 66 .walk= fs_walk, 67 .read= fs_read, 68 .stat= fs_stat, 69 .write= fs_write, 70 .clunk= fs_clunk, 71 .flush= fs_flush, 72 .attach=fs_attach, 73 .create=fs_create, 74 .remove=fs_remove, 75 .freefid=fs_freefid 76 }; 77 78 /* ad-hoc file tree. Empty names ("") indicate dynamic entries to be filled 79 * in by lookup_file 80 */ 81 static IxpDirtab 82 dirtab_root[]= {{".", QTDIR, FsRoot, 0500|DMDIR }, 83 {"rbar", QTDIR, FsDBars, 0700|DMDIR }, 84 {"lbar", QTDIR, FsDBars, 0700|DMDIR }, 85 {"debug", QTDIR, FsDDebug, 0500|DMDIR, FLHide }, 86 {"client", QTDIR, FsDClients, 0500|DMDIR }, 87 {"tag", QTDIR, FsDTags, 0500|DMDIR }, 88 {"ctl", QTAPPEND, FsFRctl, 0600|DMAPPEND }, 89 {"colrules", QTFILE, FsFColRules, 0600 }, 90 {"event", QTFILE, FsFEvent, 0600 }, 91 {"keys", QTFILE, FsFKeys, 0600 }, 92 {"rules", QTFILE, FsFRules, 0600 }, 93 {nil}}, 94 dirtab_clients[]={{".", QTDIR, FsDClients, 0500|DMDIR }, 95 {"", QTDIR, FsDClient, 0500|DMDIR }, 96 {nil}}, 97 dirtab_client[]= {{".", QTDIR, FsDClient, 0500|DMDIR }, 98 {"ctl", QTAPPEND, FsFCctl, 0600|DMAPPEND }, 99 {"label", QTFILE, FsFClabel, 0600 }, 100 {"tags", QTFILE, FsFCtags, 0600 }, 101 {"props", QTFILE, FsFprops, 0400 }, 102 {nil}}, 103 dirtab_debug[]= {{".", QTDIR, FsDDebug, 0500|DMDIR, FLHide }, 104 {"", QTFILE, FsFDebug, 0400 }, 105 {nil}}, 106 dirtab_bars[]= {{".", QTDIR, FsDBars, 0700|DMDIR }, 107 {"", QTFILE, FsFBar, 0600 }, 108 {nil}}, 109 dirtab_tags[]= {{".", QTDIR, FsDTags, 0500|DMDIR }, 110 {"", QTDIR, FsDTag, 0500|DMDIR }, 111 {nil}}, 112 dirtab_tag[]= {{".", QTDIR, FsDTag, 0500|DMDIR }, 113 {"ctl", QTAPPEND, FsFTctl, 0600|DMAPPEND }, 114 {"index", QTFILE, FsFTindex, 0400 }, 115 {nil}}; 116 static IxpDirtab* dirtab[] = { 117 [FsRoot] = dirtab_root, 118 [FsDBars] = dirtab_bars, 119 [FsDClients] = dirtab_clients, 120 [FsDClient] = dirtab_client, 121 [FsDDebug] = dirtab_debug, 122 [FsDTags] = dirtab_tags, 123 [FsDTag] = dirtab_tag, 124 }; 125 typedef char* (*MsgFunc)(void*, IxpMsg*); 126 typedef char* (*BufFunc)(void*); 127 128 typedef struct ActionTab ActionTab; 129 static struct ActionTab { 130 MsgFunc msg; 131 BufFunc read; 132 size_t buffer; 133 size_t size; 134 int max; 135 } actiontab[] = { 136 [FsFBar] = { .msg = (MsgFunc)message_bar, .read = (BufFunc)readctl_bar }, 137 [FsFCctl] = { .msg = (MsgFunc)message_client, .read = (BufFunc)readctl_client }, 138 [FsFRctl] = { .msg = (MsgFunc)message_root, .read = (BufFunc)readctl_root }, 139 [FsFTctl] = { .msg = (MsgFunc)message_view, .read = (BufFunc)readctl_view }, 140 [FsFTindex] = { .msg = (MsgFunc)0, .read = (BufFunc)view_index }, 141 [FsFColRules] = { .buffer = offsetof(Ruleset, string), .size = offsetof(Ruleset, size) }, 142 [FsFKeys] = { .buffer = offsetof(Defs, keys), .size = offsetof(Defs, keyssz) }, 143 [FsFRules] = { .buffer = offsetof(Ruleset, string), .size = offsetof(Ruleset, size) }, 144 [FsFClabel] = { .buffer = offsetof(Client, name), .max = sizeof ((Client*)0)->name }, 145 [FsFCtags] = { .buffer = offsetof(Client, tags), .max = sizeof ((Client*)0)->tags }, 146 [FsFprops] = { .buffer = offsetof(Client, props), .max = sizeof ((Client*)0)->props }, 147 }; 148 149 void 150 event(const char *format, ...) { 151 va_list ap; 152 153 va_start(ap, format); 154 vsnprint(buffer, sizeof buffer, format, ap); 155 va_end(ap); 156 157 ixp_pending_write(&events, buffer, strlen(buffer)); 158 } 159 160 static int dflags; 161 162 bool 163 setdebug(int flag) { 164 dflags = flag; 165 return true; 166 } 167 168 void 169 vdebug(int flag, const char *fmt, va_list ap) { 170 char *s; 171 172 if(flag == 0) 173 flag = dflags; 174 175 if(!((debugflag|debugfile) & flag)) 176 return; 177 178 s = vsmprint(fmt, ap); 179 dwrite(flag, s, strlen(s), false); 180 free(s); 181 } 182 183 void 184 debug(int flag, const char *fmt, ...) { 185 va_list ap; 186 187 va_start(ap, fmt); 188 vdebug(flag, fmt, ap); 189 va_end(ap); 190 } 191 192 void 193 dwrite(int flag, void *buf, int n, bool always) { 194 int i; 195 196 if(flag == 0) 197 flag = dflags; 198 199 if(always || debugflag&flag) 200 write(2, buf, n); 201 202 if(debugfile&flag) 203 for(i=0; i < nelem(pdebug); i++) 204 if(flag & (1<<i)) 205 ixp_pending_write(pdebug+i, buf, n); 206 } 207 208 static uint fs_size(IxpFileId*); 209 210 static void 211 dostat(IxpStat *s, IxpFileId *f) { 212 s->type = 0; 213 s->dev = 0; 214 s->qid.path = QID(f->tab.type, f->id); 215 s->qid.version = 0; 216 s->qid.type = f->tab.qtype; 217 s->mode = f->tab.perm; 218 s->atime = time(nil); 219 s->mtime = s->atime; 220 s->length = fs_size(f);; 221 s->name = f->tab.name; 222 s->uid = user; 223 s->gid = user; 224 s->muid = user; 225 } 226 227 /* 228 * All lookups and directory organization should be performed through 229 * lookup_file, mostly through the dirtab[] tree. 230 */ 231 static IxpFileId* 232 lookup_file(IxpFileId *parent, char *name) 233 { 234 IxpFileId *ret, *file, **last; 235 IxpDirtab *dir; 236 Client *c; 237 View *v; 238 Bar *b; 239 uint id; 240 int i; 241 242 243 if(!(parent->tab.perm & DMDIR)) 244 return nil; 245 dir = dirtab[parent->tab.type]; 246 last = &ret; 247 ret = nil; 248 for(; dir->name; dir++) { 249 # define push_file(nam, id_, vol) \ 250 file = ixp_srv_getfile(); \ 251 file->id = id_; \ 252 file->volatil = vol; \ 253 *last = file; \ 254 last = &file->next; \ 255 file->tab = *dir; \ 256 file->tab.name = estrdup(nam) 257 /* Dynamic dirs */ 258 if(dir->name[0] == '\0') { 259 switch(parent->tab.type) { 260 case FsDClients: 261 if(!name || !strcmp(name, "sel")) { 262 if((c = selclient())) { 263 push_file("sel", c->w.xid, true); 264 file->p.client = c; 265 file->index = c->w.xid; 266 } 267 if(name) 268 goto LastItem; 269 } 270 SET(id); 271 if(name) { 272 id = (uint)strtol(name, &name, 16); 273 if(*name) 274 goto NextItem; 275 } 276 for(c=client; c; c=c->next) { 277 if(!name || c->w.xid == id) { 278 push_file(sxprint("%#C", c), c->w.xid, true); 279 file->p.client = c; 280 file->index = c->w.xid; 281 assert(file->tab.name); 282 if(name) 283 goto LastItem; 284 } 285 } 286 break; 287 case FsDDebug: 288 for(i=0; i < nelem(pdebug); i++) 289 if(!name || !strcmp(name, debugtab[i])) { 290 push_file(debugtab[i], i, false); 291 if(name) 292 goto LastItem; 293 } 294 break; 295 case FsDTags: 296 if(!name || !strcmp(name, "sel")) { 297 if(selview) { 298 push_file("sel", selview->id, true); 299 file->p.view = selview; 300 } 301 if(name) 302 goto LastItem; 303 } 304 for(v=view; v; v=v->next) { 305 if(!name || !strcmp(name, v->name)) { 306 push_file(v->name, v->id, true); 307 file->p.view = v; 308 if(name) 309 goto LastItem; 310 } 311 } 312 break; 313 case FsDBars: 314 for(b=*parent->p.bar_p; b; b=b->next) { 315 if(!name || !strcmp(name, b->name)) { 316 push_file(b->name, b->id, true); 317 file->p.bar = b; 318 if(name) 319 goto LastItem; 320 } 321 } 322 break; 323 } 324 }else /* Static dirs */ 325 if(!name && !(dir->flags & FLHide) || name && !strcmp(name, dir->name)) { 326 push_file(file->tab.name, 0, false); 327 file->p.ref = parent->p.ref; 328 file->index = parent->index; 329 /* Special considerations: */ 330 switch(file->tab.type) { 331 case FsDBars: 332 if(!strcmp(file->tab.name, "lbar")) 333 file->p.bar_p = &screen[0].bar[BLeft]; 334 else 335 file->p.bar_p = &screen[0].bar[BRight]; 336 file->id = (int)(uintptr_t)file->p.bar_p; 337 break; 338 case FsFColRules: 339 file->p.rule = &def.colrules; 340 break; 341 case FsFKeys: 342 file->p.ref = &def; 343 break; 344 case FsFRules: 345 file->p.rule = &def.rules; 346 break; 347 } 348 if(name) 349 goto LastItem; 350 } 351 NextItem: 352 continue; 353 # undef push_file 354 } 355 LastItem: 356 *last = nil; 357 return ret; 358 } 359 360 /* Service Functions */ 361 void 362 fs_attach(Ixp9Req *r) { 363 IxpFileId *f; 364 365 f = ixp_srv_getfile(); 366 f->tab = dirtab[FsRoot][0]; 367 f->tab.name = estrdup("/"); 368 r->fid->aux = f; 369 r->fid->qid.type = f->tab.qtype; 370 r->fid->qid.path = QID(f->tab.type, 0); 371 r->ofcall.rattach.qid = r->fid->qid; 372 ixp_respond(r, nil); 373 } 374 375 void 376 fs_walk(Ixp9Req *r) { 377 378 ixp_srv_walkandclone(r, lookup_file); 379 } 380 381 static uint 382 fs_size(IxpFileId *f) { 383 ActionTab *t; 384 385 t = &actiontab[f->tab.type]; 386 if(f->tab.type < nelem(actiontab)) 387 if(t->size) 388 return structmember(f->p.ref, int, t->size); 389 else if(t->buffer && t->max) 390 return strlen(structptr(f->p.ref, char, t->buffer)); 391 else if(t->buffer) 392 return strlen(structmember(f->p.ref, char*, t->buffer)); 393 else if(t->read) 394 return strlen(t->read(f->p.ref)); 395 return 0; 396 } 397 398 void 399 fs_stat(Ixp9Req *r) { 400 IxpMsg m; 401 IxpStat s; 402 int size; 403 char *buf; 404 IxpFileId *f; 405 406 f = r->fid->aux; 407 408 if(!ixp_srv_verifyfile(f, lookup_file)) { 409 ixp_respond(r, Enofile); 410 return; 411 } 412 413 dostat(&s, f); 414 size = ixp_sizeof_stat(&s); 415 r->ofcall.rstat.nstat = size; 416 buf = emallocz(size); 417 418 m = ixp_message(buf, size, MsgPack); 419 ixp_pstat(&m, &s); 420 421 r->ofcall.rstat.stat = (uchar*)m.data; 422 ixp_respond(r, nil); 423 } 424 425 void 426 fs_read(Ixp9Req *r) { 427 char *buf; 428 IxpFileId *f; 429 ActionTab *t; 430 int n, found; 431 432 f = r->fid->aux; 433 found = 0; 434 435 if(!ixp_srv_verifyfile(f, lookup_file)) { 436 ixp_respond(r, Enofile); 437 return; 438 } 439 440 if(f->tab.perm & DMDIR && f->tab.perm & 0400) { 441 ixp_srv_readdir(r, lookup_file, dostat); 442 return; 443 } 444 else{ 445 if(f->pending) { 446 ixp_pending_respond(r); 447 return; 448 } 449 t = &actiontab[f->tab.type]; 450 if(f->tab.type < nelem(actiontab)) { 451 if(t->read) 452 buf = t->read(f->p.ref); 453 else if(t->buffer && t->max) 454 buf = structptr(f->p.ref, char, t->buffer); 455 else if(t->buffer) 456 buf = structmember(f->p.ref, char*, t->buffer); 457 else 458 goto done; 459 n = t->size ? structmember(f->p.ref, int, t->size) : strlen(buf); 460 ixp_srv_readbuf(r, buf, n); 461 ixp_respond(r, nil); 462 found++; 463 } 464 done: 465 switch(f->tab.type) { 466 default: 467 if(found) 468 return; 469 } 470 } 471 /* This should not be called if the file is not open for reading. */ 472 die("Read called on an unreadable file"); 473 } 474 475 void 476 fs_write(Ixp9Req *r) { 477 IxpFileId *f; 478 ActionTab *t; 479 char *errstr; 480 int found; 481 482 found = 0; 483 errstr = nil; 484 if(r->ifcall.io.count == 0) { 485 ixp_respond(r, nil); 486 return; 487 } 488 f = r->fid->aux; 489 490 if(!ixp_srv_verifyfile(f, lookup_file)) { 491 ixp_respond(r, Enofile); 492 return; 493 } 494 495 switch(f->tab.type) { 496 case FsFCtags: 497 r->ofcall.io.count = r->ifcall.io.count; 498 ixp_srv_data2cstring(r); 499 client_applytags(f->p.client, r->ifcall.io.data); 500 ixp_respond(r, nil); 501 return; 502 } 503 504 if(waserror()) { 505 ixp_respond(r, ixp_errbuf()); 506 return; 507 } 508 509 t = &actiontab[f->tab.type]; 510 if(f->tab.type < nelem(actiontab)) { 511 if(t->msg) { 512 errstr = ixp_srv_writectl(r, t->msg); 513 r->ofcall.io.count = r->ifcall.io.count; 514 } 515 else if(t->buffer && t->max) 516 ixp_srv_writebuf(r, (char*[]){ structptr(f->p.ref, char, t->buffer) }, 517 t->size ? structptr(f->p.ref, uint, t->size) 518 : (uint[]){ strlen(structptr(f->p.ref, char, t->buffer)) }, 519 t->max); 520 else if(t->buffer) 521 ixp_srv_writebuf(r, structptr(f->p.ref, char*, t->buffer), 522 t->size ? structptr(f->p.ref, uint, t->size) : nil, 523 t->max); 524 else 525 goto done; 526 ixp_respond(r, errstr); 527 found++; 528 } 529 done: 530 switch(f->tab.type) { 531 case FsFClabel: 532 frame_draw(f->p.client->sel); 533 update_class(f->p.client); 534 break; 535 case FsFCtags: 536 client_applytags(f->p.client, f->p.client->tags); 537 break; 538 case FsFEvent: 539 if(r->ifcall.io.data[r->ifcall.io.count-1] == '\n') 540 event("%.*s", (int)r->ifcall.io.count, r->ifcall.io.data); 541 else 542 event("%.*s\n", (int)r->ifcall.io.count, r->ifcall.io.data); 543 r->ofcall.io.count = r->ifcall.io.count; 544 ixp_respond(r, nil); 545 break; 546 default: 547 /* This should not be called if the file is not open for writing. */ 548 if(!found) 549 die("Write called on an unwritable file"); 550 } 551 poperror(); 552 return; 553 } 554 555 void 556 fs_open(Ixp9Req *r) { 557 IxpFileId *f; 558 559 f = r->fid->aux; 560 561 if(!ixp_srv_verifyfile(f, lookup_file)) { 562 ixp_respond(r, Enofile); 563 return; 564 } 565 566 switch(f->tab.type) { 567 case FsFEvent: 568 ixp_pending_pushfid(&events, r->fid); 569 break; 570 case FsFDebug: 571 ixp_pending_pushfid(pdebug+f->id, r->fid); 572 debugfile |= 1<<f->id; 573 break; 574 } 575 576 if((r->ifcall.topen.mode&3) == OEXEC 577 || (r->ifcall.topen.mode&3) != OREAD && !(f->tab.perm & 0200) 578 || (r->ifcall.topen.mode&3) != OWRITE && !(f->tab.perm & 0400) 579 || (r->ifcall.topen.mode & ~(3|OAPPEND|OTRUNC))) 580 ixp_respond(r, Enoperm); 581 else 582 ixp_respond(r, nil); 583 } 584 585 void 586 fs_create(Ixp9Req *r) { 587 IxpFileId *f; 588 589 f = r->fid->aux; 590 591 switch(f->tab.type) { 592 default: 593 ixp_respond(r, Enoperm); 594 return; 595 case FsDBars: 596 if(!strlen(r->ifcall.tcreate.name)) { 597 ixp_respond(r, Ebadvalue); 598 return; 599 } 600 bar_create(f->p.bar_p, r->ifcall.tcreate.name); 601 f = lookup_file(f, r->ifcall.tcreate.name); 602 if(!f) { 603 ixp_respond(r, Enofile); 604 return; 605 } 606 r->ofcall.ropen.qid.type = f->tab.qtype; 607 r->ofcall.ropen.qid.path = QID(f->tab.type, f->id); 608 f->next = r->fid->aux; 609 r->fid->aux = f; 610 ixp_respond(r, nil); 611 break; 612 } 613 } 614 615 void 616 fs_remove(Ixp9Req *r) { 617 IxpFileId *f; 618 WMScreen *s; 619 620 f = r->fid->aux; 621 if(!ixp_srv_verifyfile(f, lookup_file)) { 622 ixp_respond(r, Enofile); 623 return; 624 } 625 626 switch(f->tab.type) { 627 default: 628 ixp_respond(r, Enoperm); 629 return; 630 case FsFBar: 631 s = f->p.bar->screen; 632 bar_destroy(f->next->p.bar_p, f->p.bar); 633 bar_draw(s); 634 break; 635 case FsDClient: 636 client_kill(f->p.client, true); 637 break; 638 } 639 ixp_respond(r, nil); 640 } 641 642 void 643 fs_clunk(Ixp9Req *r) { 644 IxpFileId *f; 645 646 f = r->fid->aux; 647 if(!ixp_srv_verifyfile(f, lookup_file)) { 648 ixp_respond(r, nil); 649 return; 650 } 651 652 if(f->pending) { 653 /* Should probably be in freefid */ 654 if(ixp_pending_clunk(r)) { 655 if(f->tab.type == FsFDebug) 656 debugfile &= ~(1<<f->id); 657 } 658 return; 659 } 660 661 switch(f->tab.type) { 662 case FsFColRules: 663 case FsFRules: 664 update_rules(&f->p.rule->rule, f->p.rule->string); 665 break; 666 case FsFKeys: 667 update_keys(); 668 break; 669 } 670 ixp_respond(r, nil); 671 } 672 673 void 674 fs_flush(Ixp9Req *r) { 675 Ixp9Req *or; 676 IxpFileId *f; 677 678 or = r->oldreq; 679 f = or->fid->aux; 680 if(f->pending) 681 ixp_pending_flush(r); 682 /* else die() ? */ 683 ixp_respond(r->oldreq, Einterrupted); 684 ixp_respond(r, nil); 685 } 686 687 void 688 fs_freefid(IxpFid *f) { 689 IxpFileId *id, *tid; 690 691 tid = f->aux; 692 while((id = tid)) { 693 tid = id->next; 694 ixp_srv_freefile(id); 695 } 696 } 697