added gentoo ebuilds
[libmixp.git] / libmixp / client.c
blobadda3d5ef2d522f18dc6f40f82114416799fccef
1 /* Copyright ©2007 Kris Maglione <fbsdaemon@gmail.com>
2 * See LICENSE file for license details.
3 */
4 #include <assert.h>
5 #include <stdarg.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/socket.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12 #include "mixp_local.h"
13 #include <9p-mixp/stat.h>
14 #include <9p-mixp/err.h>
15 #include <9p-mixp/convert.h>
17 #include "util.h"
19 #define nelem(ary) (sizeof(ary) / sizeof(*ary))
21 enum {
22 RootFid = 1,
25 static int
26 min(int a, int b) {
27 if(a < b)
28 return a;
29 return b;
32 static MIXP_CFID *
33 getfid(MIXP_CLIENT *c) {
34 MIXP_CFID *f;
36 mixp_thread->lock(&c->lk);
37 f = c->freefid;
38 if(f != NULL)
39 c->freefid = f->next;
40 else {
41 f = calloc(1,sizeof *f);
42 f->client = c;
43 f->fid = ++c->lastfid;
44 mixp_thread->initmutex(&f->iolock);
46 f->next = NULL;
47 f->open = 0;
48 mixp_thread->unlock(&c->lk);
49 return f;
52 static void
53 putfid(MIXP_CFID *f) {
54 MIXP_CLIENT *c;
56 c = f->client;
57 mixp_thread->lock(&c->lk);
58 if(f->fid == c->lastfid) {
59 c->lastfid--;
60 mixp_thread->mdestroy(&f->iolock);
61 MIXP_FREE(f);
62 }else {
63 f->next = c->freefid;
64 c->freefid = f;
66 mixp_thread->unlock(&c->lk);
69 static IxpFcall* do_fcall(MIXP_CLIENT* c, IxpFcall* fcall)
71 IxpFcall *ret;
72 __init_errstream();
73 ret = muxrpc(c, fcall);
74 if(ret == NULL)
76 // fprintf(mixp_error_stream,"MIXP: fcall without reply\n");
77 return NULL;
80 if(ret->type == P9_RError) {
81 mixp_werrstr("%s", ret->Rerror.ename);
82 fprintf(mixp_error_stream, "MIXP: fcall returned error %s\n", ret->Rerror.ename);
83 return NULL;
85 if(ret->type != (fcall->type^1)) {
86 mixp_werrstr("received mismatched fcall");
87 fprintf(mixp_error_stream, "MIXP: received mismatched fcall\n");
88 return NULL;
90 return ret;
93 static int
94 dofcall(MIXP_CLIENT *c, IxpFcall *fcall) {
95 IxpFcall *ret;
97 ret = muxrpc(c, fcall);
98 if(ret == NULL)
99 return 0;
100 if(ret->type == P9_RError) {
101 mixp_werrstr("%s", ret->Rerror.ename);
102 goto fail;
104 if(ret->type != (fcall->type^1)) {
105 mixp_werrstr("received mismatched fcall");
106 goto fail;
108 memcpy(fcall, ret, sizeof(IxpFcall));
109 MIXP_FREE(ret);
110 return 1;
111 fail:
112 mixp_fcall_free(fcall);
113 return 0;
116 void
117 mixp_unmount(MIXP_CLIENT *c) {
118 MIXP_CFID *f;
120 shutdown(c->fd, SHUT_RDWR);
121 close(c->fd);
123 muxfree(c);
125 while((f = c->freefid)) {
126 c->freefid = f->next;
127 mixp_thread->mdestroy(&f->iolock);
128 MIXP_FREE(f);
130 MIXP_FREE(c->rmsg.data);
131 MIXP_FREE(c->wmsg.data);
132 MIXP_FREE(c);
135 static void
136 allocmsg(MIXP_CLIENT *c, int n) {
137 c->rmsg.size = n;
138 c->wmsg.size = n;
139 c->rmsg.data = ixp_erealloc(c->rmsg.data, n);
140 c->wmsg.data = ixp_erealloc(c->wmsg.data, n);
143 MIXP_CLIENT *
144 mixp_mountfd(int fd) {
145 MIXP_CLIENT *c;
146 IxpFcall* fcall = (IxpFcall*)calloc(1,sizeof(IxpFcall));
147 IxpFcall* retfcall = NULL;
149 c = calloc(1,sizeof(*c));
150 c->fd = fd;
152 muxinit(c);
154 allocmsg(c, 256);
155 c->lastfid = RootFid;
156 /* Override tag matching on P9_TVersion */
157 c->mintag = IXP_NOTAG;
158 c->maxtag = IXP_NOTAG+1;
160 fcall->type = P9_TVersion;
161 fcall->Tversion.msize = IXP_MAX_MSG;
162 fcall->Tversion.version = IXP_VERSION;
164 if ((retfcall = do_fcall(c, fcall))==NULL)
166 mixp_unmount(c);
167 mixp_fcall_free(fcall);
168 return NULL;
171 if(strcmp(retfcall->Rversion.version, IXP_VERSION) ||
172 retfcall->Rversion.msize > IXP_MAX_MSG) {
173 mixp_werrstr("bad 9P version response");
174 mixp_unmount(c);
175 mixp_fcall_free(fcall);
176 mixp_fcall_free(retfcall);
177 return NULL;
180 c->mintag = 0;
181 c->maxtag = 255;
183 allocmsg(c, retfcall->Tversion.msize);
185 mixp_fcall_free(fcall);
186 mixp_fcall_free(retfcall);
188 fcall = (IxpFcall*)calloc(1,sizeof(IxpFcall));
190 fcall->type = P9_TAttach;
191 fcall->fid = RootFid;
192 fcall->Tattach.afid = IXP_NOFID;
193 fcall->Tattach.uname = getenv("USER");
194 fcall->Tattach.aname = "";
195 if((retfcall = do_fcall(c, fcall)) == NULL) {
196 mixp_unmount(c);
197 mixp_fcall_free(fcall);
198 return NULL;
201 mixp_fcall_free(fcall);
202 mixp_fcall_free(retfcall);
203 return c;
206 MIXP_CLIENT *
207 mixp_mount(const char *address) {
208 int fd;
210 fd = mixp_dial(address);
211 if(fd < 0)
212 return NULL;
213 return mixp_mountfd(fd);
216 MIXP_CLIENT * mixp_mount_addr(MIXP_SERVER_ADDRESS *addr)
218 int fd;
220 fd = mixp_dial_addr(addr);
221 if(fd < 0)
222 return NULL;
223 return mixp_mountfd(fd);
226 static MIXP_CFID *
227 walk(MIXP_CLIENT *c, const char *pathname) {
228 MIXP_CFID *f;
229 int n;
231 IxpFcall* fcall = (IxpFcall*)calloc(1,sizeof(IxpFcall));
232 IxpFcall* retfcall = NULL;
234 char* path = strdup(pathname);
235 n = ixp_tokenize(fcall->Twalk.wname, nelem(fcall->Twalk.wname), path, '/');
237 f = getfid(c);
239 fcall->type = P9_TWalk;
240 fcall->fid = RootFid;
241 fcall->Twalk.nwname = n;
242 fcall->Twalk.newfid = f->fid;
243 if((retfcall=do_fcall(c, fcall)) == NULL)
244 goto fail;
245 if(retfcall->Rwalk.nwqid < n)
246 goto fail;
248 f->qid = fcall->Rwalk.wqid[n-1];
250 goto out;
251 fail:
252 putfid(f);
253 f = NULL;
254 out:
255 mixp_fcall_free(fcall);
256 mixp_fcall_free(retfcall);
257 free(path);
258 return f;
261 static MIXP_CFID *
262 walkdir(MIXP_CLIENT *c, char *path, char **rest) {
263 char *p;
265 p = path + strlen(path) - 1;
266 assert(p >= path);
267 while(*p == '/')
268 *p-- = '\0';
270 while((p > path) && (*p != '/'))
271 p--;
272 if(*p != '/') {
273 mixp_werrstr("bad path");
274 return NULL;
277 *p++ = '\0';
278 *rest = p;
279 return walk(c, path);
282 static int
283 clunk(MIXP_CFID *f) {
284 MIXP_CLIENT *c;
285 IxpFcall* fcall = (IxpFcall*)calloc(1,sizeof(IxpFcall));
286 IxpFcall* retfcall = NULL;
288 c = f->client;
290 fcall->type = P9_TClunk;
291 fcall->fid = f->fid;
292 if ((retfcall=do_fcall(c,fcall))==NULL)
294 mixp_fcall_free(fcall);
295 return 0;
297 else
299 putfid(f);
300 mixp_fcall_free(retfcall);
301 mixp_fcall_free(fcall);
302 return 1;
307 mixp_remove(MIXP_CLIENT *c, const char *path)
309 MIXP_CFID *f;
310 if((f = walk(c, path)) == NULL)
311 return 0;
313 IxpFcall *fcall = calloc(1,sizeof(IxpFcall));
314 fcall->type = P9_TRemove;
315 fcall->fid = f->fid;
317 IxpFcall *retfcall = NULL;
318 if ((retfcall = do_fcall(c, fcall))==NULL)
320 mixp_fcall_free(fcall);
321 return 0;
323 else
325 putfid(f);
326 mixp_fcall_free(fcall);
327 mixp_fcall_free(retfcall);
328 return 1;
332 static void
333 initfid(MIXP_CFID *f, IxpFcall *fcall)
335 __init_errstream();
337 f->open = 1;
338 f->offset = 0;
339 f->iounit = min(fcall->Ropen.iounit, IXP_MAX_MSG-17);
340 f->qid = fcall->Ropen.qid;
342 if (!(f->iounit))
344 // fprintf(mixp_error_stream,"initfid() iounit missing. fixing to %d\n", DEFAULT_IOUNIT);
345 f->iounit=DEFAULT_IOUNIT;
348 if (f->iounit < 0)
350 // fprintf(mixp_error_stream,"initfid() iounit <0: %d .. fixing to %d\n", f->iounit, DEFAULT_IOUNIT);
351 f->iounit=DEFAULT_IOUNIT;
355 MIXP_CFID*
356 mixp_create(MIXP_CLIENT *c, const char *filename, unsigned int perm, unsigned char mode)
358 MIXP_CFID *f;
359 char *path = strdup(filename);
360 char *name = strdup(filename); // FIXME: correct ?!
362 if ((f = walkdir(c, path, &name))==NULL)
364 MIXP_FREE(path);
365 MIXP_FREE(name);
368 IxpFcall *fcall = (IxpFcall*)calloc(1,sizeof(IxpFcall));
369 fcall->type = P9_TCreate;
370 fcall->fid = f->fid;
371 fcall->Tcreate.name = name;
372 fcall->Tcreate.perm = perm;
373 fcall->Tcreate.mode = mode;
375 IxpFcall* retfcall;
376 if ((retfcall = do_fcall(c, fcall)) == NULL)
378 clunk(f);
379 f = NULL;
381 else
383 initfid(f, retfcall);
384 f->mode = mode;
385 mixp_fcall_free(retfcall);
387 return f;
390 MIXP_CFID*
391 mixp_open(MIXP_CLIENT *c, const char *name, unsigned char mode)
393 MIXP_CFID *f;
395 f = walk(c, name);
396 if(f == NULL)
397 return NULL;
399 IxpFcall* fcall = (IxpFcall*)calloc(1,sizeof(IxpFcall));
400 fcall->type = P9_TOpen;
401 fcall->fid = f->fid;
402 fcall->Topen.mode = mode;
404 IxpFcall* retfcall;
406 if ((retfcall=do_fcall(c, fcall)) == NULL)
408 clunk(f);
409 mixp_fcall_free(fcall);
410 return NULL;
413 initfid(f, retfcall);
414 f->mode = mode;
415 mixp_fcall_free(fcall);
416 mixp_fcall_free(retfcall);
417 return f;
420 int mixp_close(MIXP_CFID *f)
422 return clunk(f);
425 MIXP_STAT *
426 mixp_stat(MIXP_CLIENT *c, const char *path)
428 MIXP_MESSAGE msg;
429 MIXP_STAT *stat;
430 MIXP_CFID *f;
432 stat = NULL;
433 f = walk(c, path);
434 if(f == NULL)
435 return NULL;
437 IxpFcall* fcall = (IxpFcall*)calloc(1,sizeof(IxpFcall));
438 fcall->type = P9_TStat;
439 fcall->fid = f->fid;
441 IxpFcall* retfcall;
442 if((retfcall = do_fcall(c, fcall))==NULL)
443 goto done;
445 msg = mixp_message(retfcall->Rstat.stat, retfcall->Rstat.nstat, MsgUnpack);
447 stat = malloc(sizeof(MIXP_STAT));
448 mixp_pstat(&msg, stat);
449 mixp_fcall_free(fcall);
450 if(msg.pos > msg.end) {
451 MIXP_FREE(stat);
452 stat = 0;
455 done:
456 clunk(f);
457 mixp_fcall_free(fcall);
458 mixp_fcall_free(retfcall);
459 return stat;
462 static long _pread(MIXP_CFID *f, void *buf, size_t count, int64_t offset)
464 __init_errstream();
466 IxpFcall* fcall = NULL;
467 IxpFcall* retfcall = NULL;
468 int len = 0;
469 while(len < count)
471 mixp_fcall_free(fcall); fcall = NULL;
472 mixp_fcall_free(retfcall); retfcall = NULL;
474 fcall = (IxpFcall*)calloc(1,sizeof(IxpFcall));
475 int n = min(count-len, f->iounit);
477 fcall->type = P9_TRead;
478 fcall->fid = f->fid;
479 fcall->Tread.offset = offset;
480 fcall->Tread.count = n;
482 if ((retfcall = do_fcall(f->client, fcall)) == NULL)
484 fprintf(mixp_error_stream,"MIXP: _pread() fcall failed\n");
485 goto err;
488 if (retfcall->Rread.count > n)
490 fprintf(mixp_error_stream,"MIXP: _pread() received more (%d) than requested (%d)\n", retfcall->Rread.count, n);
491 goto err;
494 memcpy(buf+len, retfcall->Rread.data, retfcall->Rread.count);
495 offset += retfcall->Rread.count;
496 len += retfcall->Rread.count;
498 if(retfcall->Rread.count < n)
499 break;
501 mixp_fcall_free(fcall);
502 mixp_fcall_free(retfcall);
503 return len;
504 err:
505 mixp_fcall_free(fcall);
506 mixp_fcall_free(retfcall);
507 return -1;
510 long mixp_read(MIXP_CFID *f, void *buf, size_t count)
512 int n;
514 mixp_thread->lock(&f->iolock);
515 n = _pread(f, buf, count, f->offset);
516 if(n > 0)
517 f->offset += n;
518 mixp_thread->unlock(&f->iolock);
519 return n;
522 long mixp_pread(MIXP_CFID *f, void *buf, long count, int64_t offset)
524 int n;
526 mixp_thread->lock(&f->iolock);
527 n = _pread(f, buf, count, offset);
528 mixp_thread->unlock(&f->iolock);
529 return n;
532 static long _pwrite(MIXP_CFID *f, const void *buf, long count, int64_t offset)
534 int n, len;
535 len = 0;
538 IxpFcall* fcall = (IxpFcall*)calloc(1,sizeof(IxpFcall));
539 IxpFcall* retfcall = NULL;
541 n = min(count-len, f->iounit);
542 fcall->type = P9_TWrite;
543 fcall->fid = f->fid;
544 fcall->Twrite.offset = f->offset;
545 fcall->Twrite.data = (char*)(buf + len);
546 fcall->Twrite.count = n;
547 if ((retfcall = do_fcall(f->client, fcall)) == NULL)
549 mixp_fcall_free(fcall);
550 return -1;
553 f->offset += retfcall->Rwrite.count;
554 len += retfcall->Rwrite.count;
556 if(fcall->Rwrite.count < n)
558 mixp_fcall_free(fcall);
559 mixp_fcall_free(retfcall);
560 return len;
562 mixp_fcall_free(fcall);
563 mixp_fcall_free(retfcall);
564 } while(len < count);
565 return len;
568 long
569 mixp_write(MIXP_CFID *f, const void *buf, long count) {
570 int n;
572 mixp_thread->lock(&f->iolock);
573 n = _pwrite(f, buf, count, f->offset);
574 if(n > 0)
575 f->offset += n;
576 mixp_thread->unlock(&f->iolock);
577 return n;
580 long
581 mixp_pwrite(MIXP_CFID *f, const void *buf, long count, int64_t offset) {
582 int n;
584 mixp_thread->lock(&f->iolock);
585 n = _pwrite(f, buf, count, offset);
586 mixp_thread->unlock(&f->iolock);
587 return n;