srv_util.c (14375B)
1 /* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail> 2 * See LICENSE file for license details. 3 */ 4 #include <assert.h> 5 #include <ctype.h> 6 #include <stdarg.h> 7 #include <stdbool.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <time.h> 11 #include <unistd.h> 12 #include "ixp_local.h" 13 14 typedef void* IxpFileIdU; 15 16 static char 17 Enofile[] = "file not found"; 18 19 #include "ixp_srvutil.h" 20 21 struct IxpQueue { 22 IxpQueue* link; 23 char* dat; 24 long len; 25 }; 26 27 #define QID(t, i) (((int64_t)((t)&0xFF)<<32)|((i)&0xFFFFFFFF)) 28 29 static IxpFileId* free_fileid; 30 31 /** 32 * Function: ixp_srv_getfile 33 * Type: IxpFileId 34 * 35 * Obtain an empty, reference counted IxpFileId struct. 36 * 37 * See also: 38 * F<ixp_srv_clonefiles>, F<ixp_srv_freefile> 39 */ 40 IxpFileId* 41 ixp_srv_getfile(void) { 42 IxpFileId *file; 43 int i; 44 45 if(!free_fileid) { 46 i = 15; 47 file = emallocz(i * sizeof *file); 48 for(; i; i--) { 49 file->next = free_fileid; 50 free_fileid = file++; 51 } 52 } 53 file = free_fileid; 54 free_fileid = file->next; 55 file->p = nil; 56 file->volatil = 0; 57 file->nref = 1; 58 file->next = nil; 59 file->pending = false; 60 return file; 61 } 62 63 /** 64 * Function: ixp_srv_freefile 65 * 66 * Decrease the reference count of the given IxpFileId, 67 * and push it onto the free list when it reaches 0; 68 * 69 * See also: 70 * F<ixp_srv_getfile> 71 */ 72 void 73 ixp_srv_freefile(IxpFileId *fileid) { 74 if(--fileid->nref) 75 return; 76 free(fileid->tab.name); 77 fileid->next = free_fileid; 78 free_fileid = fileid; 79 } 80 81 /** 82 * Function: ixp_srv_clonefiles 83 * 84 * Increase the reference count of every IxpFileId linked 85 * to P<fileid>. 86 * 87 * See also: 88 * F<ixp_srv_getfile> 89 */ 90 IxpFileId* 91 ixp_srv_clonefiles(IxpFileId *fileid) { 92 IxpFileId *r; 93 94 r = ixp_srv_getfile(); 95 memcpy(r, fileid, sizeof *r); 96 r->tab.name = estrdup(r->tab.name); 97 r->nref = 1; 98 for(fileid=fileid->next; fileid; fileid=fileid->next) 99 assert(fileid->nref++); 100 return r; 101 } 102 103 /** 104 * Function: ixp_srv_readbuf 105 * Function: ixp_srv_writebuf 106 * 107 * Utility functions for handling TRead and TWrite requests for 108 * files backed by in-memory buffers. For both functions, P<buf> 109 * points to a buffer and P<len> specifies the length of the 110 * buffer. In the case of ixp_srv_writebuf, these values add a 111 * level of pointer indirection, and updates the values if they 112 * change. 113 * 114 * If P<max> has a value other than 0, ixp_srv_writebuf will 115 * truncate any writes to that point in the buffer. Otherwise, 116 * P<*buf> is assumed to be malloc(3) allocated, and is 117 * reallocated to fit the new data as necessary. The buffer is 118 * is always left nul-terminated. 119 * 120 * Bugs: 121 * ixp_srv_writebuf always truncates its buffer to the end 122 * of the most recent write. 123 */ 124 125 void 126 ixp_srv_readbuf(Ixp9Req *req, char *buf, uint len) { 127 128 if(req->ifcall.io.offset >= len) 129 return; 130 131 len -= req->ifcall.io.offset; 132 if(len > req->ifcall.io.count) 133 len = req->ifcall.io.count; 134 req->ofcall.io.data = emalloc(len); 135 memcpy(req->ofcall.io.data, buf + req->ifcall.io.offset, len); 136 req->ofcall.io.count = len; 137 } 138 139 void 140 ixp_srv_writebuf(Ixp9Req *req, char **buf, uint *len, uint max) { 141 IxpFileId *file; 142 char *p; 143 uint offset, count; 144 145 file = req->fid->aux; 146 147 offset = req->ifcall.io.offset; 148 if(file->tab.perm & DMAPPEND) 149 offset = *len; 150 151 if(offset > *len || req->ifcall.io.count == 0) { 152 req->ofcall.io.count = 0; 153 return; 154 } 155 156 count = req->ifcall.io.count; 157 if(max && (offset + count > max)) 158 count = max - offset; 159 160 *len = offset + count; 161 if(max == 0) 162 *buf = erealloc(*buf, *len + 1); 163 p = *buf; 164 165 memcpy(p+offset, req->ifcall.io.data, count); 166 req->ofcall.io.count = count; 167 p[offset+count] = '\0'; 168 } 169 170 /** 171 * Function: ixp_srv_data2cstring 172 * 173 * Ensure that the data member of P<req> is null terminated, 174 * removing any new line from its end. 175 * 176 * See also: 177 * S<Ixp9Req> 178 */ 179 void 180 ixp_srv_data2cstring(Ixp9Req *req) { 181 char *p, *q; 182 uint i; 183 184 i = req->ifcall.io.count; 185 p = req->ifcall.io.data; 186 if(i && p[i - 1] == '\n') 187 i--; 188 q = memchr(p, '\0', i); 189 if(q) 190 i = q - p; 191 192 p = erealloc(req->ifcall.io.data, i+1); 193 p[i] = '\0'; 194 req->ifcall.io.data = p; 195 } 196 197 /** 198 * Function: ixp_srv_writectl 199 * 200 * This utility function is meant to simplify the writing of 201 * pseudo files to which single-lined commands are written. 202 * In order to use this function, the P<aux> member of 203 * P<req>->fid must be nul or an S<IxpFileId>. Each line of the 204 * written data is stripped of its trailing newline, 205 * nul-terminated, and stored in an S<IxpMsg>. For each line 206 * thus prepared, P<fn> is called with the IxpMsg pointer and 207 * the the P<p> member of the IxpFileId. 208 */ 209 char* 210 ixp_srv_writectl(Ixp9Req *req, char* (*fn)(void*, IxpMsg*)) { 211 char *err, *s, *p, c; 212 IxpFileId *file; 213 IxpMsg msg; 214 215 file = req->fid->aux; 216 217 ixp_srv_data2cstring(req); 218 s = req->ifcall.io.data; 219 220 err = nil; 221 c = *s; 222 while(c != '\0') { 223 while(*s == '\n') 224 s++; 225 p = s; 226 while(*p != '\0' && *p != '\n') 227 p++; 228 c = *p; 229 *p = '\0'; 230 231 msg = ixp_message(s, p-s, 0); 232 s = fn(file->p, &msg); 233 if(s) 234 err = s; 235 s = p + 1; 236 } 237 return err; 238 } 239 240 /** 241 * Function: ixp_pending_write 242 * Function: ixp_pending_print 243 * Function: ixp_pending_vprint 244 * Function: ixp_pending_pushfid 245 * Function: ixp_pending_clunk 246 * Function: ixp_pending_flush 247 * Function: ixp_pending_respond 248 * Type: IxpPending 249 * 250 * These functions aid in writing virtual files used for 251 * broadcasting events or writing data when it becomes 252 * available. When a file to be used with these functions is 253 * opened, ixp_pending_pushfid should be called with its 254 * S<IxpFid> as an argument. This sets the IxpFid's P<pending> 255 * member to true. Thereafter, for each file with its 256 * P<pending> member set, ixp_pending_respond should be called 257 * for each TRead request, ixp_pending_clunk for each TClunk 258 * request, and ixp_pending_flush for each TFlush request. 259 * 260 * ixp_pending_write queues the data in P<dat> of length P<ndat> 261 * to be written to each currently pending fid in P<pending>. If 262 * there is a read request pending for a given fid, the data is 263 * written immediately. Otherwise, it is written the next time 264 * ixp_pending_respond is called. Likewise, if there is data 265 * queued when ixp_pending_respond is called, it is written 266 * immediately, otherwise the request is queued. 267 * 268 * ixp_pending_print and ixp_pending_vprint call ixp_pending_write 269 * after formatting their arguments with V<ixp_vsmprint>. 270 * 271 * The IxpPending data structure is opaque and should be 272 * initialized zeroed before using these functions for the first 273 * time. 274 * 275 * Returns: 276 * ixp_pending_clunk returns true if P<pending> has any 277 * more pending IxpFids. 278 */ 279 280 void 281 ixp_pending_respond(Ixp9Req *req) { 282 IxpFileId *file; 283 IxpPendingLink *p; 284 IxpRequestLink *req_link; 285 IxpQueue *queue; 286 287 file = req->fid->aux; 288 assert(file->pending); 289 p = file->p; 290 if(p->queue) { 291 queue = p->queue; 292 p->queue = queue->link; 293 req->ofcall.io.data = queue->dat; 294 req->ofcall.io.count = queue->len; 295 if(req->aux) { 296 req_link = req->aux; 297 req_link->next->prev = req_link->prev; 298 req_link->prev->next = req_link->next; 299 free(req_link); 300 } 301 ixp_respond(req, nil); 302 free(queue); 303 }else { 304 req_link = emallocz(sizeof *req_link); 305 req_link->req = req; 306 req_link->next = &p->pending->req; 307 req_link->prev = req_link->next->prev; 308 req_link->next->prev = req_link; 309 req_link->prev->next = req_link; 310 req->aux = req_link; 311 } 312 } 313 314 void 315 ixp_pending_write(IxpPending *pending, const char *dat, long ndat) { 316 IxpRequestLink req_link; 317 IxpQueue **qp, *queue; 318 IxpPendingLink *pp; 319 IxpRequestLink *rp; 320 321 if(ndat == 0) 322 return; 323 324 if(pending->req.next == nil) { 325 pending->req.next = &pending->req; 326 pending->req.prev = &pending->req; 327 pending->fids.prev = &pending->fids; 328 pending->fids.next = &pending->fids; 329 } 330 331 for(pp=pending->fids.next; pp != &pending->fids; pp=pp->next) { 332 for(qp=&pp->queue; *qp; qp=&qp[0]->link) 333 ; 334 queue = emallocz(sizeof *queue); 335 queue->dat = emalloc(ndat); 336 memcpy(queue->dat, dat, ndat); 337 queue->len = ndat; 338 *qp = queue; 339 } 340 341 req_link.next = &req_link; 342 req_link.prev = &req_link; 343 if(pending->req.next != &pending->req) { 344 req_link.next = pending->req.next; 345 req_link.prev = pending->req.prev; 346 pending->req.prev = &pending->req; 347 pending->req.next = &pending->req; 348 } 349 req_link.prev->next = &req_link; 350 req_link.next->prev = &req_link; 351 352 while((rp = req_link.next) != &req_link) 353 ixp_pending_respond(rp->req); 354 } 355 356 int 357 ixp_pending_vprint(IxpPending *pending, const char *fmt, va_list ap) { 358 char *dat; 359 int res; 360 361 dat = ixp_vsmprint(fmt, ap); 362 res = strlen(dat); 363 ixp_pending_write(pending, dat, res); 364 free(dat); 365 return res; 366 } 367 368 int 369 ixp_pending_print(IxpPending *pending, const char *fmt, ...) { 370 va_list ap; 371 int res; 372 373 va_start(ap, fmt); 374 res = ixp_pending_vprint(pending, fmt, ap); 375 va_end(ap); 376 return res; 377 } 378 379 void 380 ixp_pending_pushfid(IxpPending *pending, IxpFid *fid) { 381 IxpPendingLink *pend_link; 382 IxpFileId *file; 383 384 if(pending->req.next == nil) { 385 pending->req.next = &pending->req; 386 pending->req.prev = &pending->req; 387 pending->fids.prev = &pending->fids; 388 pending->fids.next = &pending->fids; 389 } 390 391 file = fid->aux; 392 pend_link = emallocz(sizeof *pend_link); 393 pend_link->fid = fid; 394 pend_link->pending = pending; 395 pend_link->next = &pending->fids; 396 pend_link->prev = pend_link->next->prev; 397 pend_link->next->prev = pend_link; 398 pend_link->prev->next = pend_link; 399 file->pending = true; 400 file->p = pend_link; 401 } 402 403 static void 404 pending_flush(Ixp9Req *req) { 405 IxpFileId *file; 406 IxpRequestLink *req_link; 407 408 file = req->fid->aux; 409 if(file->pending) { 410 req_link = req->aux; 411 if(req_link) { 412 req_link->prev->next = req_link->next; 413 req_link->next->prev = req_link->prev; 414 free(req_link); 415 } 416 } 417 } 418 419 void 420 ixp_pending_flush(Ixp9Req *req) { 421 422 pending_flush(req->oldreq); 423 } 424 425 bool 426 ixp_pending_clunk(Ixp9Req *req) { 427 IxpPending *pending; 428 IxpPendingLink *pend_link; 429 IxpRequestLink *req_link; 430 Ixp9Req *r; 431 IxpFileId *file; 432 IxpQueue *queue; 433 bool more; 434 435 file = req->fid->aux; 436 pend_link = file->p; 437 438 pending = pend_link->pending; 439 for(req_link=pending->req.next; req_link != &pending->req;) { 440 r = req_link->req; 441 req_link = req_link->next; 442 if(r->fid == pend_link->fid) { 443 pending_flush(r); 444 ixp_respond(r, "interrupted"); 445 } 446 } 447 448 pend_link->prev->next = pend_link->next; 449 pend_link->next->prev = pend_link->prev; 450 451 while((queue = pend_link->queue)) { 452 pend_link->queue = queue->link; 453 free(queue->dat); 454 free(queue); 455 } 456 more = (pend_link->pending->fids.next == &pend_link->pending->fids); 457 free(pend_link); 458 ixp_respond(req, nil); 459 return more; 460 } 461 462 /** 463 * Function: ixp_srv_walkandclone 464 * Function: ixp_srv_readdir 465 * Function: ixp_srv_verifyfile 466 * Type: IxpLookupFn 467 * 468 * These convenience functions simplify the writing of basic and 469 * static file servers. They use a generic file lookup function 470 * to simplify the process of walking, cloning, and returning 471 * directory listings. Given the S<IxpFileId> of a directory and a 472 * filename name should return a new IxpFileId (allocated via 473 * F<ixp_srv_getfile>) for the matching directory entry, or null 474 * if there is no match. If the passed name is null, P<lookup> 475 * should return a linked list of IxpFileIds, one for each child 476 * directory entry. 477 * 478 * ixp_srv_walkandclone handles the moderately complex process 479 * of walking from a directory entry and cloning fids, and calls 480 * F<ixp_respond>. It should be called in response to a TWalk 481 * request. 482 * 483 * ixp_srv_readdir should be called to handle read requests on 484 * directories. It prepares a stat for each child of the 485 * directory, taking into account the requested offset, and 486 * calls F<ixp_respond>. The P<dostat> parameter must be a 487 * function which fills the passed S<IxpStat> pointer based on 488 * the contents of the passed IxpFileId. 489 * 490 * ixp_srv_verifyfile returns whether a file still exists in the 491 * filesystem, and should be used by filesystems that invalidate 492 * files once they have been deleted. 493 * 494 * See also: 495 * S<IxpFileId>, S<ixp_getfile>, S<ixp_freefile> 496 */ 497 bool 498 ixp_srv_verifyfile(IxpFileId *file, IxpLookupFn lookup) { 499 IxpFileId *tfile; 500 int ret; 501 502 if(!file->next) 503 return true; 504 505 ret = false; 506 if(ixp_srv_verifyfile(file->next, lookup)) { 507 tfile = lookup(file->next, file->tab.name); 508 if(tfile) { 509 if(!tfile->volatil || tfile->p == file->p) 510 ret = true; 511 ixp_srv_freefile(tfile); 512 } 513 } 514 return ret; 515 } 516 517 void 518 ixp_srv_readdir(Ixp9Req *req, IxpLookupFn lookup, void (*dostat)(IxpStat*, IxpFileId*)) { 519 IxpMsg msg; 520 IxpFileId *file, *tfile; 521 IxpStat stat; 522 char *buf; 523 ulong size, n; 524 uint64_t offset; 525 526 file = req->fid->aux; 527 528 size = req->ifcall.io.count; 529 if(size > req->fid->iounit) 530 size = req->fid->iounit; 531 buf = emallocz(size); 532 msg = ixp_message(buf, size, MsgPack); 533 534 file = lookup(file, nil); 535 tfile = file; 536 /* Note: The first file is ".", so we skip it. */ 537 offset = 0; 538 for(file=file->next; file; file=file->next) { 539 dostat(&stat, file); 540 n = ixp_sizeof_stat(&stat); 541 if(offset >= req->ifcall.io.offset) { 542 if(size < n) 543 break; 544 ixp_pstat(&msg, &stat); 545 size -= n; 546 } 547 offset += n; 548 } 549 while((file = tfile)) { 550 tfile=tfile->next; 551 ixp_srv_freefile(file); 552 } 553 req->ofcall.io.count = msg.pos - msg.data; 554 req->ofcall.io.data = msg.data; 555 ixp_respond(req, nil); 556 } 557 558 void 559 ixp_srv_walkandclone(Ixp9Req *req, IxpLookupFn lookup) { 560 IxpFileId *file, *tfile; 561 int i; 562 563 file = ixp_srv_clonefiles(req->fid->aux); 564 for(i=0; i < req->ifcall.twalk.nwname; i++) { 565 if(!strcmp(req->ifcall.twalk.wname[i], "..")) { 566 if(file->next) { 567 tfile = file; 568 file = file->next; 569 ixp_srv_freefile(tfile); 570 } 571 }else{ 572 tfile = lookup(file, req->ifcall.twalk.wname[i]); 573 if(!tfile) 574 break; 575 assert(!tfile->next); 576 if(strcmp(req->ifcall.twalk.wname[i], ".")) { 577 tfile->next = file; 578 file = tfile; 579 } 580 } 581 req->ofcall.rwalk.wqid[i].type = file->tab.qtype; 582 req->ofcall.rwalk.wqid[i].path = QID(file->tab.type, file->id); 583 } 584 /* There should be a way to do this on freefid() */ 585 if(i < req->ifcall.twalk.nwname) { 586 while((tfile = file)) { 587 file=file->next; 588 ixp_srv_freefile(tfile); 589 } 590 ixp_respond(req, Enofile); 591 return; 592 } 593 /* Remove refs for req->fid if no new fid */ 594 if(req->ifcall.hdr.fid == req->ifcall.twalk.newfid) { 595 tfile = req->fid->aux; 596 req->fid->aux = file; 597 while((file = tfile)) { 598 tfile = tfile->next; 599 ixp_srv_freefile(file); 600 } 601 }else 602 req->newfid->aux = file; 603 req->ofcall.rwalk.nwqid = i; 604 ixp_respond(req, nil); 605 } 606