libixp

git clone git://oldgit.suckless.org/libixp/
Log | Files | Refs | LICENSE

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