libixp

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

client.c (13483B)


      1 /* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
      2  * See LICENSE file for license details.
      3  */
      4 #include <assert.h>
      5 #include <stdarg.h>
      6 #include <stdint.h>
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include <sys/socket.h>
     11 #include <sys/types.h>
     12 #include <unistd.h>
     13 #include "ixp_local.h"
     14 
     15 #define nelem(ary) (sizeof(ary) / sizeof(*ary))
     16 
     17 enum {
     18 	RootFid = 1,
     19 };
     20 
     21 static int
     22 min(int a, int b) {
     23 	if(a < b)
     24 		return a;
     25 	return b;
     26 }
     27 
     28 static IxpCFid*
     29 getfid(IxpClient *c) {
     30 	IxpCFid *f;
     31 
     32 	thread->lock(&c->lk);
     33 	f = c->freefid;
     34 	if(f != nil)
     35 		c->freefid = f->next;
     36 	else {
     37 		f = emallocz(sizeof *f);
     38 		f->client = c;
     39 		f->fid = ++c->lastfid;
     40 		thread->initmutex(&f->iolock);
     41 	}
     42 	f->next = nil;
     43 	f->open = 0;
     44 	thread->unlock(&c->lk);
     45 	return f;
     46 }
     47 
     48 static void
     49 putfid(IxpCFid *f) {
     50 	IxpClient *c;
     51 
     52 	c = f->client;
     53 	thread->lock(&c->lk);
     54 	if(f->fid == c->lastfid) {
     55 		c->lastfid--;
     56 		thread->mdestroy(&f->iolock);
     57 		free(f);
     58 	}else {
     59 		f->next = c->freefid;
     60 		c->freefid = f;
     61 	}
     62 	thread->unlock(&c->lk);
     63 }
     64 
     65 static int
     66 dofcall(IxpClient *c, IxpFcall *fcall) {
     67 	IxpFcall *ret;
     68 
     69 	ret = muxrpc(c, fcall);
     70 	if(ret == nil)
     71 		return 0;
     72 	if(ret->hdr.type == RError) {
     73 		werrstr("%s", ret->error.ename);
     74 		goto fail;
     75 	}
     76 	if(ret->hdr.type != (fcall->hdr.type^1)) {
     77 		werrstr("received mismatched fcall");
     78 		goto fail;
     79 	}
     80 	memcpy(fcall, ret, sizeof *fcall);
     81 	free(ret);
     82 	return 1;
     83 fail:
     84 	ixp_freefcall(fcall);
     85 	free(ret);
     86 	return 0;
     87 }
     88 
     89 /**
     90  * Function: ixp_unmount
     91  *
     92  * Unmounts the client P<client> and frees its data structures.
     93  *
     94  * See also:
     95  *	F<ixp_mount>
     96  */
     97 void
     98 ixp_unmount(IxpClient *client) {
     99 	IxpCFid *f;
    100 
    101 	shutdown(client->fd, SHUT_RDWR);
    102 	close(client->fd);
    103 
    104 	muxfree(client);
    105 
    106 	while((f = client->freefid)) {
    107 		client->freefid = f->next;
    108 		thread->mdestroy(&f->iolock);
    109 		free(f);
    110 	}
    111 	free(client->rmsg.data);
    112 	free(client->wmsg.data);
    113 	free(client);
    114 }
    115 
    116 static void
    117 allocmsg(IxpClient *c, int n) {
    118 	c->rmsg.size = n;
    119 	c->wmsg.size = n;
    120 	c->rmsg.data = erealloc(c->rmsg.data, n);
    121 	c->wmsg.data = erealloc(c->wmsg.data, n);
    122 }
    123 
    124 /**
    125  * Function: ixp_mount
    126  * Function: ixp_mountfd
    127  * Function: ixp_nsmount
    128  * Type: IxpClient
    129  *
    130  * Params:
    131  *	fd:      A file descriptor which is already connected
    132  *	         to a 9P server.
    133  *	address: An address (in Plan 9 resource fomat) at
    134  *	         which to connect to a 9P server.
    135  *	name:    The name of a socket in the process's canonical
    136  *	         namespace directory.
    137  *
    138  * Initiate a 9P connection with the server at P<address>,
    139  * connected to on P<fd>, or under the process's namespace
    140  * directory as P<name>.
    141  *
    142  * Returns:
    143  *	A pointer to a new 9P client.
    144  * See also:
    145  *	F<ixp_open>, F<ixp_create>, F<ixp_remove>, F<ixp_unmount>
    146  */
    147 
    148 IxpClient*
    149 ixp_mountfd(int fd) {
    150 	IxpClient *c;
    151 	IxpFcall fcall;
    152 
    153 	c = emallocz(sizeof *c);
    154 	c->fd = fd;
    155 
    156 	muxinit(c);
    157 
    158 	allocmsg(c, 256);
    159 	c->lastfid = RootFid;
    160 	/* Override tag matching on TVersion */
    161 	c->mintag = IXP_NOTAG;
    162 	c->maxtag = IXP_NOTAG+1;
    163 
    164 	fcall.hdr.type = TVersion;
    165 	fcall.version.msize = IXP_MAX_MSG;
    166 	fcall.version.version = IXP_VERSION;
    167 
    168 	if(dofcall(c, &fcall) == 0) {
    169 		ixp_unmount(c);
    170 		return nil;
    171 	}
    172 
    173 	if(strcmp(fcall.version.version, IXP_VERSION)
    174 	|| fcall.version.msize > IXP_MAX_MSG) {
    175 		werrstr("bad 9P version response");
    176 		ixp_unmount(c);
    177 		return nil;
    178 	}
    179 
    180 	c->mintag = 0;
    181 	c->maxtag = 255;
    182 	c->msize = fcall.version.msize;
    183 
    184 	allocmsg(c, fcall.version.msize);
    185 	ixp_freefcall(&fcall);
    186 
    187 	fcall.hdr.type = TAttach;
    188 	fcall.hdr.fid = RootFid;
    189 	fcall.tattach.afid = IXP_NOFID;
    190 	fcall.tattach.uname = getenv("USER");
    191 	fcall.tattach.aname = "";
    192 	if(dofcall(c, &fcall) == 0) {
    193 		ixp_unmount(c);
    194 		return nil;
    195 	}
    196 
    197 	return c;
    198 }
    199 
    200 IxpClient*
    201 ixp_mount(const char *address) {
    202 	int fd;
    203 
    204 	fd = ixp_dial(address);
    205 	if(fd < 0)
    206 		return nil;
    207 	return ixp_mountfd(fd);
    208 }
    209 
    210 IxpClient*
    211 ixp_nsmount(const char *name) {
    212 	char *address;
    213 	IxpClient *c;
    214 
    215 	address = ixp_namespace();
    216 	if(address)
    217 		address = ixp_smprint("unix!%s/%s", address, name);
    218 	if(address == nil)
    219 		return nil;
    220 	c = ixp_mount(address);
    221 	free(address);
    222 	return c;
    223 }
    224 
    225 static IxpCFid*
    226 walk(IxpClient *c, const char *path) {
    227 	IxpCFid *f;
    228 	char *p;
    229 	IxpFcall fcall;
    230 	int n;
    231 
    232 	p = estrdup(path);
    233 	n = tokenize(fcall.twalk.wname, nelem(fcall.twalk.wname), p, '/');
    234 	f = getfid(c);
    235 
    236 	fcall.hdr.type = TWalk;
    237 	fcall.hdr.fid = RootFid;
    238 	fcall.twalk.nwname = n;
    239 	fcall.twalk.newfid = f->fid;
    240 	if(dofcall(c, &fcall) == 0)
    241 		goto fail;
    242 	if(fcall.rwalk.nwqid < n) {
    243 		werrstr("File does not exist");
    244 		if(fcall.rwalk.nwqid == 0)
    245 			werrstr("Protocol botch");
    246 		goto fail;
    247 	}
    248 
    249 	f->qid = fcall.rwalk.wqid[n-1];
    250 
    251 	ixp_freefcall(&fcall);
    252 	free(p);
    253 	return f;
    254 fail:
    255 	putfid(f);
    256 	free(p);
    257 	return nil;
    258 }
    259 
    260 static IxpCFid*
    261 walkdir(IxpClient *c, char *path, const char **rest) {
    262 	char *p;
    263 
    264 	p = path + strlen(path) - 1;
    265 	assert(p >= path);
    266 	while(*p == '/')
    267 		*p-- = '\0';
    268 
    269 	while((p > path) && (*p != '/'))
    270 		p--;
    271 	if(*p != '/') {
    272 		werrstr("bad path");
    273 		return nil;
    274 	}
    275 
    276 	*p++ = '\0';
    277 	*rest = p;
    278 	return walk(c, path);
    279 }
    280 
    281 static int
    282 clunk(IxpCFid *f) {
    283 	IxpClient *c;
    284 	IxpFcall fcall;
    285 	int ret;
    286 
    287 	c = f->client;
    288 
    289 	fcall.hdr.type = TClunk;
    290 	fcall.hdr.fid = f->fid;
    291 	ret = dofcall(c, &fcall);
    292 	if(ret)
    293 		putfid(f);
    294 	ixp_freefcall(&fcall);
    295 	return ret;
    296 }
    297 
    298 /**
    299  * Function: ixp_remove
    300  *
    301  * Params:
    302  *	path: The path of the file to remove.
    303  *
    304  * Removes a file or directory from the remote server.
    305  *
    306  * Returns:
    307  *	ixp_remove returns 0 on failure, 1 on success.
    308  * See also:
    309  *	F<ixp_mount>
    310  */
    311 
    312 int
    313 ixp_remove(IxpClient *c, const char *path) {
    314 	IxpFcall fcall;
    315 	IxpCFid *f;
    316 	int ret;
    317 
    318 	if((f = walk(c, path)) == nil)
    319 		return 0;
    320 
    321 	fcall.hdr.type = TRemove;
    322 	fcall.hdr.fid = f->fid;;
    323 	ret = dofcall(c, &fcall);
    324 	ixp_freefcall(&fcall);
    325 	putfid(f);
    326 
    327 	return ret;
    328 }
    329 
    330 static void
    331 initfid(IxpCFid *f, IxpFcall *fcall) {
    332 	f->open = 1;
    333 	f->offset = 0;
    334 	f->iounit = fcall->ropen.iounit;
    335 	if(f->iounit == 0 || fcall->ropen.iounit > f->client->msize-24)
    336 		f->iounit =  f->client->msize-24;
    337 	f->qid = fcall->ropen.qid;
    338 }
    339 
    340 /**
    341  * Function: ixp_open
    342  * Function: ixp_create
    343  * Type: IxpCFid
    344  * Type: IxpOMode
    345  *
    346  * Params:
    347  *	path: The path of the file to open or create.
    348  *	perm: The permissions with which to create the new
    349  *	      file. These will be ANDed with those of the
    350  *	      parent directory by the server.
    351  *	mode: The file's open mode.
    352  *
    353  * ixp_open and ixp_create each open a file at P<path>.
    354  * P<mode> must include OREAD, OWRITE, or ORDWR, and may
    355  * include any of the modes specified in T<IxpOMode>.
    356  * ixp_create, additionally, creates a file at P<path> if it
    357  * doesn't already exist.
    358  *
    359  * Returns:
    360  *	A pointer on which to operate on the newly
    361  *      opened file.
    362  *
    363  * See also:
    364  *	F<ixp_mount>, F<ixp_read>, F<ixp_write>, F<ixp_print>,
    365  *	F<ixp_fstat>, F<ixp_close>
    366  */
    367 
    368 IxpCFid*
    369 ixp_create(IxpClient *c, const char *path, uint perm, uint8_t mode) {
    370 	IxpFcall fcall;
    371 	IxpCFid *f;
    372 	char *tpath;;
    373 
    374 	tpath = estrdup(path);
    375 
    376 	f = walkdir(c, tpath, &path);
    377 	if(f == nil)
    378 		goto done;
    379 
    380 	fcall.hdr.type = TCreate;
    381 	fcall.hdr.fid = f->fid;
    382 	fcall.tcreate.name = (char*)(uintptr_t)path;
    383 	fcall.tcreate.perm = perm;
    384 	fcall.tcreate.mode = mode;
    385 
    386 	if(dofcall(c, &fcall) == 0) {
    387 		clunk(f);
    388 		f = nil;
    389 		goto done;
    390 	}
    391 
    392 	initfid(f, &fcall);
    393 	f->mode = mode;
    394 
    395 	ixp_freefcall(&fcall);
    396 
    397 done:
    398 	free(tpath);
    399 	return f;
    400 }
    401 
    402 IxpCFid*
    403 ixp_open(IxpClient *c, const char *path, uint8_t mode) {
    404 	IxpFcall fcall;
    405 	IxpCFid *f;
    406 
    407 	f = walk(c, path);
    408 	if(f == nil)
    409 		return nil;
    410 
    411 	fcall.hdr.type = TOpen;
    412 	fcall.hdr.fid = f->fid;
    413 	fcall.topen.mode = mode;
    414 
    415 	if(dofcall(c, &fcall) == 0) {
    416 		clunk(f);
    417 		return nil;
    418 	}
    419 
    420 	initfid(f, &fcall);
    421 	f->mode = mode;
    422 
    423 	ixp_freefcall(&fcall);
    424 	return f;
    425 }
    426 
    427 /**
    428  * Function: ixp_close
    429  *
    430  * Closes the file pointed to by P<f> and frees its
    431  * associated data structures;
    432  *
    433  * Returns:
    434  *	Returns 1 on success, and zero on failure.
    435  * See also:
    436  *	F<ixp_mount>, F<ixp_open>
    437  */
    438 
    439 int
    440 ixp_close(IxpCFid *f) {
    441 	return clunk(f);
    442 }
    443 
    444 static IxpStat*
    445 _stat(IxpClient *c, ulong fid) {
    446 	IxpMsg msg;
    447 	IxpFcall fcall;
    448 	IxpStat *stat;
    449 
    450 	fcall.hdr.type = TStat;
    451 	fcall.hdr.fid = fid;
    452 	if(dofcall(c, &fcall) == 0)
    453 		return nil;
    454 
    455 	msg = ixp_message((char*)fcall.rstat.stat, fcall.rstat.nstat, MsgUnpack);
    456 
    457 	stat = emalloc(sizeof *stat);
    458 	ixp_pstat(&msg, stat);
    459 	ixp_freefcall(&fcall);
    460 	if(msg.pos > msg.end) {
    461 		free(stat);
    462 		stat = nil;
    463 	}
    464 	return stat;
    465 }
    466 
    467 /**
    468  * Function: ixp_stat
    469  * Function: ixp_fstat
    470  * Type: IxpStat
    471  * Type: IxpQid
    472  * Type: IxpQType
    473  * Type: IxpDMode
    474  *
    475  * Params:
    476  *	path: The path of the file to stat.
    477  *	fid:  An open file descriptor to stat.
    478  *
    479  * Stats the file at P<path> or pointed to by P<fid>.
    480  *
    481  * Returns:
    482  *	Returns an IxpStat structure, which must be freed by
    483  *	the caller with free(3).
    484  * See also:
    485  *	F<ixp_mount>, F<ixp_open>
    486  */
    487 
    488 IxpStat*
    489 ixp_stat(IxpClient *c, const char *path) {
    490 	IxpStat *stat;
    491 	IxpCFid *f;
    492 
    493 	f = walk(c, path);
    494 	if(f == nil)
    495 		return nil;
    496 
    497 	stat = _stat(c, f->fid);
    498 	clunk(f);
    499 	return stat;
    500 }
    501 
    502 IxpStat*
    503 ixp_fstat(IxpCFid *fid) {
    504 	return _stat(fid->client, fid->fid);
    505 }
    506 
    507 static long
    508 _pread(IxpCFid *f, char *buf, long count, int64_t offset) {
    509 	IxpFcall fcall;
    510 	int n, len;
    511 
    512 	len = 0;
    513 	while(len < count) {
    514 		n = min(count-len, f->iounit);
    515 
    516 		fcall.hdr.type = TRead;
    517 		fcall.hdr.fid = f->fid;
    518 		fcall.tread.offset = offset;
    519 		fcall.tread.count = n;
    520 		if(dofcall(f->client, &fcall) == 0)
    521 			return -1;
    522 		if(fcall.rread.count > n)
    523 			return -1;
    524 
    525 		memcpy(buf+len, fcall.rread.data, fcall.rread.count);
    526 		offset += fcall.rread.count;
    527 		len += fcall.rread.count;
    528 
    529 		ixp_freefcall(&fcall);
    530 		if(fcall.rread.count < n)
    531 			break;
    532 	}
    533 	return len;
    534 }
    535 
    536 /**
    537  * Function: ixp_read
    538  * Function: ixp_pread
    539  *
    540  * Params:
    541  *	buf:    A buffer in which to store the read data.
    542  *	count:  The number of bytes to read.
    543  *	offset: The offset at which to begin reading.
    544  *
    545  * ixp_read and ixp_pread each read P<count> bytes of data
    546  * from the file pointed to by P<fid>, into P<buf>. ixp_read
    547  * begins reading at its stored offset, and increments it by
    548  * the number of bytes read. ixp_pread reads beginning at
    549  * P<offset> and does not alter P<fid>'s stored offset.
    550  *
    551  * Returns:
    552  *	These functions return the number of bytes read on
    553  *	success and -1 on failure.
    554  * See also:
    555  *	F<ixp_mount>, F<ixp_open>, F<ixp_write>
    556  */
    557 
    558 long
    559 ixp_read(IxpCFid *fid, void *buf, long count) {
    560 	int n;
    561 
    562 	thread->lock(&fid->iolock);
    563 	n = _pread(fid, buf, count, fid->offset);
    564 	if(n > 0)
    565 		fid->offset += n;
    566 	thread->unlock(&fid->iolock);
    567 	return n;
    568 }
    569 
    570 long
    571 ixp_pread(IxpCFid *fid, void *buf, long count, int64_t offset) {
    572 	int n;
    573 
    574 	thread->lock(&fid->iolock);
    575 	n = _pread(fid, buf, count, offset);
    576 	thread->unlock(&fid->iolock);
    577 	return n;
    578 }
    579 
    580 static long
    581 _pwrite(IxpCFid *f, const void *buf, long count, int64_t offset) {
    582 	IxpFcall fcall;
    583 	int n, len;
    584 
    585 	len = 0;
    586 	do {
    587 		n = min(count-len, f->iounit);
    588 		fcall.hdr.type = TWrite;
    589 		fcall.hdr.fid = f->fid;
    590 		fcall.twrite.offset = offset;
    591 		fcall.twrite.data = (char*)buf + len;
    592 		fcall.twrite.count = n;
    593 		if(dofcall(f->client, &fcall) == 0)
    594 			return -1;
    595 
    596 		offset += fcall.rwrite.count;
    597 		len += fcall.rwrite.count;
    598 
    599 		ixp_freefcall(&fcall);
    600 		if(fcall.rwrite.count < n)
    601 			break;
    602 	} while(len < count);
    603 	return len;
    604 }
    605 
    606 /**
    607  * Function: ixp_write
    608  * Function: ixp_pwrite
    609  *
    610  * Params:
    611  *	buf:    A buffer holding the contents to store.
    612  *	count:  The number of bytes to store.
    613  *	offset: The offset at which to write the data.
    614  *
    615  * ixp_write and ixp_pwrite each write P<count> bytes of
    616  * data stored in P<buf> to the file pointed to by C<fid>.
    617  * ixp_write writes its data at its stored offset, and
    618  * increments it by P<count>. ixp_pwrite writes its data a
    619  * P<offset> and does not alter C<fid>'s stored offset.
    620  *
    621  * Returns:
    622  *	These functions return the number of bytes actually
    623  *	written. Any value less than P<count> must be considered
    624  *	a failure.
    625  * See also:
    626  *	F<ixp_mount>, F<ixp_open>, F<ixp_read>
    627  */
    628 
    629 long
    630 ixp_write(IxpCFid *fid, const void *buf, long count) {
    631 	int n;
    632 
    633 	thread->lock(&fid->iolock);
    634 	n = _pwrite(fid, buf, count, fid->offset);
    635 	if(n > 0)
    636 		fid->offset += n;
    637 	thread->unlock(&fid->iolock);
    638 	return n;
    639 }
    640 
    641 long
    642 ixp_pwrite(IxpCFid *fid, const void *buf, long count, int64_t offset) {
    643 	int n;
    644 
    645 	thread->lock(&fid->iolock);
    646 	n = _pwrite(fid, buf, count, offset);
    647 	thread->unlock(&fid->iolock);
    648 	return n;
    649 }
    650 
    651 /**
    652  * Function: ixp_print
    653  * Function: ixp_vprint
    654  * Variable: ixp_vsmprint
    655  *
    656  * Params:
    657  *      fid:  An open IxpCFid to which to write the result.
    658  *	fmt:  The string with which to format the data.
    659  *	args: A va_list holding the arguments to the format
    660  *	      string.
    661  *	...:  The arguments to the format string.
    662  *
    663  * These functions act like the standard formatted IO
    664  * functions. They write the result of the formatting to the
    665  * file pointed to by C<fid>.
    666  *
    667  * V<ixp_vsmprint> may be set to a function which will
    668  * format its arguments and return a nul-terminated string
    669  * allocated by malloc(3). The default formats its arguments as
    670  * printf(3).
    671  *
    672  * Returns:
    673  *	These functions return the number of bytes written.
    674  *	There is currently no way to detect failure.
    675  * See also:
    676  *	F<ixp_mount>, F<ixp_open>, printf(3)
    677  */
    678 
    679 int
    680 ixp_vprint(IxpCFid *fid, const char *fmt, va_list args) {
    681 	char *buf;
    682 	int n;
    683 
    684 	buf = ixp_vsmprint(fmt, args);
    685 	if(buf == nil)
    686 		return -1;
    687 
    688 	n = ixp_write(fid, buf, strlen(buf));
    689 	free(buf);
    690 	return n;
    691 }
    692 
    693 int
    694 ixp_print(IxpCFid *fid, const char *fmt, ...) {
    695 	va_list ap;
    696 	int n;
    697 
    698 	va_start(ap, fmt);
    699 	n = ixp_vprint(fid, fmt, ap);
    700 	va_end(ap);
    701 
    702 	return n;
    703 }
    704