conforming to the XSI-compliant version of strerror_r in sp_suerror.
[spfs.git] / libspclient / fsys.c
blobd23912b1325fde0be5929d57e393fe64900b072b
1 /*
2 * Copyright (C) 2006 by Latchesar Ionkov <lucho@ionkov.net>
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * LATCHESAR IONKOV AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <sys/socket.h>
29 #include <assert.h>
30 #include "spfs.h"
31 #include "spclient.h"
32 #include "spcimpl.h"
34 typedef struct Spcrpc Spcrpc;
35 struct Spcrpc {
36 Spcfsys* fs;
37 Spfcall* tc;
38 Spfcall* rc;
39 char* ename;
40 int ecode;
43 int spc_chatty;
44 int spc_msize = 32768 + IOHDRSZ;
45 char *Econn = "connection closed";
46 static char *Emismatch = "response mismatch";
47 static char *Eflush = "request flushed";
49 static Spfcall *spc_fcall_alloc(u32 msize);
50 static void spc_notify(Spfd *spfd, void *aux);
52 Spcfsys *
53 spc_create_fsys(int fd, int msize)
55 Spcfsys *fs;
57 fs = sp_malloc(sizeof(*fs));
58 if (!fs)
59 return NULL;
61 fs->fd = fd;
62 fs->spfd = NULL;
63 fs->dotu = 0;
64 fs->msize = msize;
65 fs->root = NULL;
66 fs->afid = NULL;
67 fs->tagpool = NULL;
68 fs->fidpool = NULL;
69 fs->ifcall = NULL;
70 fs->pend_pos = 0;
71 fs->pend_first = NULL;
72 fs->pend_last = NULL;
73 fs->sent_reqs = NULL;
74 fs->ename = NULL;
75 fs->ecode = 0;
76 fs->in_notify = 0;
77 fs->destroyed = 0;
78 fs->laddr = NULL;
79 fs->raddr = NULL;
81 fs->spfd = spfd_add(fd, spc_notify, fs);
82 if (!fs->spfd)
83 goto error;
85 fs->tagpool = spc_create_pool(NOTAG);
86 if (!fs->tagpool)
87 goto error;
89 fs->fidpool = spc_create_pool(NOFID);
90 if (!fs->fidpool)
91 goto error;
93 return fs;
95 error:
96 spc_disconnect_fsys(fs);
97 return NULL;
100 void
101 spc_remount(Spcfsys *fs)
103 fs->spfd = spfd_add(fs->fd, spc_notify, fs);
106 void
107 spc_disconnect_fsys(Spcfsys *fs)
109 int ecode;
110 char *ename;
111 Spcreq *req, *req1;
113 sp_rerror(&fs->ename, &fs->ecode);
114 if (fs->ecode) {
115 fs->ename = strdup(fs->ename);
116 if (!fs->ename)
117 fs->ename = Enomem;
120 if (fs->root) {
121 spc_fid_free(fs->root);
122 fs->root = NULL;
125 if (fs->afid) {
126 spc_fid_free(fs->afid);
127 fs->afid = NULL;
130 if (fs->fd >= 0) {
131 shutdown(fs->fd, 2);
132 close(fs->fd);
133 fs->fd = -1;
136 if (fs->spfd) {
137 spfd_remove(fs->spfd);
138 fs->spfd = NULL;
141 sp_rerror(&ename, &ecode);
142 if (ecode) {
143 ename = strdup(ename);
144 if (!ename) {
145 ename = Enomem;
146 ecode = ENOMEM;
150 sp_werror(Econn, ECONNRESET);
151 req = fs->pend_first;
152 while (req != NULL) {
153 (*req->cb)(req->cba, NULL);
154 req1 = req->next;
155 free(req);
156 req = req1;
158 fs->pend_first = NULL;
160 req = fs->sent_reqs;
161 while (req != NULL) {
162 (*req->cb)(req->cba, NULL);
163 req1 = req->next;
164 free(req);
165 req = req1;
167 fs->sent_reqs = NULL;
169 sp_werror(ename, ecode);
170 if (ename != Enomem)
171 free(ename);
174 void
175 spc_destroy_fsys(Spcfsys *fs)
177 assert(fs->fd<0);
178 if (fs->tagpool) {
179 spc_destroy_pool(fs->tagpool);
180 fs->tagpool = NULL;
183 if (fs->fidpool) {
184 spc_destroy_pool(fs->fidpool);
185 fs->fidpool = NULL;
188 free(fs->ifcall);
189 if (fs->ename != Enomem)
190 free(fs->ename);
192 free(fs->raddr);
193 fs->raddr = NULL;
194 free(fs->laddr);
195 fs->laddr = NULL;
196 if (fs->in_notify)
197 fs->destroyed = 1;
198 else
199 free(fs);
202 char *
203 spc_get_local_address(Spcfsys *fs)
205 return fs->laddr;
208 char *
209 spc_get_remote_address(Spcfsys *fs)
211 return fs->raddr;
214 void
215 spc_request_flushed(Spcreq *r)
217 int ecode;
218 char *ename;
219 Spcreq *req, *preq;
220 Spcfsys *fs;
222 fs = r->fs;
223 for(preq=NULL, req = fs->sent_reqs; req != NULL; preq=req, req = req->next)
224 if (r == req)
225 break;
227 if (req) {
228 if (preq)
229 preq->next = req->next;
230 else
231 fs->sent_reqs = req->next;
233 sp_rerror(&ename, &ecode);
234 if (ename)
235 ename = strdup(ename);
237 sp_werror(Eflush, EIO);
238 (*req->cb)(req->cba, NULL);
239 sp_werror(ename, ecode);
240 free(ename);
243 /* if req->flushed is set, the request is not freed if response arrives */
244 spc_put_id(fs->tagpool, r->tag);
245 free(r);
248 void
249 spc_flush_cb(void *aux, Spfcall *rc)
251 free(rc);
252 spc_request_flushed((Spcreq *) aux);
255 static void
256 spc_flush_request(Spcreq *req)
258 Spfcall *fc;
259 Spcfsys *fs;
261 if (req->flushed)
262 return;
264 req->flushed = 1;
265 fs = req->fs;
266 fc = sp_create_tflush(req->tag);
267 if (sp_poll_looping())
268 spc_rpcnb(fs, fc, spc_flush_cb, req);
269 else {
270 spc_rpc(fs, fc, NULL);
271 spc_request_flushed(req);
275 void
276 spc_flush_requests(Spcfsys *fs, Spcfid *fid)
278 int ecode;
279 char *ename;
280 Spcreq *preq, *req, *req1;
282 if (fs->fd < 0)
283 return;
285 // check the unsent requests
286 sp_rerror(&ename, &ecode);
287 if (ename)
288 ename = strdup(ename);
290 sp_werror(Eflush, EIO);
291 preq = NULL;
292 req = fs->pend_first;
293 while (req != NULL) {
294 if (req->tc->fid == fid->fid) {
295 if (preq)
296 preq->next = req->next;
297 else
298 fs->pend_first = req->next;
300 if (req == fs->pend_last)
301 fs->pend_last = preq;
303 (*req->cb)(req->cba, NULL);
304 req1 = req->next;
305 free(req);
306 req = req1;
307 } else {
308 preq = req;
309 req = req->next;
313 // check the sent ones
314 req = fs->sent_reqs;
315 while (req != NULL) {
316 if (req->tc->fid == fid->fid && !req->flushed) {
317 spc_flush_request(req);
318 req = fs->sent_reqs;
319 } else {
320 req = req->next;
323 sp_werror(ename, ecode);
324 free(ename);
327 static void
328 spc_fd_read(Spcfsys *fs)
330 int n, size;
331 Spfcall *fc;
332 Spcreq *req, *preq;
334 if (!fs->ifcall) {
335 fs->ifcall = spc_fcall_alloc(fs->msize);
336 if (!fs->ifcall) {
337 spc_disconnect_fsys(fs);
338 return;
341 fs->ifcall->size = 0;
344 fc = fs->ifcall;
345 n = spfd_read(fs->spfd, fc->pkt + fc->size, fs->msize - fc->size);
346 if (n <= 0) {
347 if (n == 0)
348 spc_disconnect_fsys(fs);
349 return;
352 fc->size += n;
354 again:
355 if (fc->size < 4)
356 return;
358 n = fc->size;
359 size = fc->pkt[0] | (fc->pkt[1]<<8) | (fc->pkt[2]<<16) | (fc->pkt[3]<<24);
360 if (size > fs->msize) {
361 sp_werror("invalid fcall size greater than msize", EIO);
362 spc_disconnect_fsys(fs);
363 return;
366 if (n < size)
367 return;
369 if (!sp_deserialize(fc, fc->pkt, fs->dotu)) {
370 sp_werror("invalid fcall", EIO);
371 spc_disconnect_fsys(fs);
372 return;
375 if (spc_chatty) {
376 fprintf(stderr, "-->>> (%p) ", fs);
377 sp_printfcall(stderr, fc, fs->dotu);
378 fprintf(stderr, "\n");
381 fs->ifcall = spc_fcall_alloc(fs->msize);
382 if (!fs->ifcall) {
383 spc_disconnect_fsys(fs);
384 return;
387 if (n > size)
388 memmove(fs->ifcall->pkt, fc->pkt + size, n - size);
390 fs->ifcall->size = n - size;
392 for(preq = NULL, req = fs->sent_reqs; req != NULL; preq = req, req = req->next)
393 if (fc->tag == req->tag)
394 break;
396 if (!req) {
397 sp_werror("unexpected fcall", EIO);
398 free(fc);
399 return;
402 if (preq)
403 preq->next = req->next;
404 else
405 fs->sent_reqs = req->next;
407 if (fc->type!=Rerror && req->tc->type+1!=fc->type) {
408 sp_werror(Emismatch, EIO);
409 free(fc);
410 fc = NULL;
413 (*req->cb)(req->cba, fc);
414 if (fs->destroyed) {
415 free(req);
416 return;
419 if (!req->flushed) {
420 spc_put_id(fs->tagpool, req->tag);
421 free(req);
424 if (fs->ifcall->size) {
425 fc = fs->ifcall;
426 goto again;
430 static void
431 spc_fd_write(Spcfsys *fs)
433 int n;
434 Spcreq *req;
435 Spfcall *tc;
437 if (!fs->pend_first)
438 return;
440 req = fs->pend_first;
441 tc = req->tc;
443 if (spc_chatty && fs->pend_pos == 0) {
444 fprintf(stderr, "<<<-- (%p) ", fs);
445 sp_printfcall(stderr, tc, fs->dotu);
446 fprintf(stderr, "\n");
449 n = spfd_write(fs->spfd, tc->pkt + fs->pend_pos, tc->size - fs->pend_pos);
450 if (n <= 0) {
451 if (n == 0)
452 spc_disconnect_fsys(fs);
453 return;
456 fs->pend_pos += n;
457 if (tc->size == fs->pend_pos) {
458 fs->pend_pos = 0;
459 fs->pend_first = req->next;
460 if (req == fs->pend_last)
461 fs->pend_last = NULL;
463 req->next = fs->sent_reqs;
464 fs->sent_reqs = req;
468 static void
469 spc_notify(Spfd *spfd, void *aux)
471 int ecode, ec;
472 char *ename, *en;
473 Spcfsys *fs;
475 fs = aux;
476 fs->in_notify++;
477 sp_rerror(&ename, &ecode);
478 if (ename)
479 ename = strdup(ename);
481 sp_werror(NULL, 0);
482 if (spfd_can_read(spfd))
483 spc_fd_read(fs);
485 if (fs->destroyed) {
486 free(fs);
487 return;
490 if (!fs->spfd)
491 goto error;
493 if (spfd_can_write(spfd))
494 spc_fd_write(fs);
496 if (spfd_has_error(spfd))
497 spc_disconnect_fsys(fs);
499 error:
500 sp_rerror(&en, &ec);
501 if (ec) {
502 if (spc_chatty)
503 fprintf(stderr, "Error: %s: %d\n", en, ec);
504 sp_werror(NULL, 0);
507 if (ecode)
508 sp_werror(ename, ecode);
510 free(ename);
511 fs->in_notify--;
515 spc_rpcnb(Spcfsys *fs, Spfcall *tc, void (*cb)(void *, Spfcall *), void *cba)
517 Spcreq *req;
519 if (!fs->spfd) {
520 sp_werror("disconnected", ECONNRESET);
521 goto error;
524 if (fs->ename) {
525 sp_werror(fs->ename, fs->ecode);
526 goto error;
529 req = sp_malloc(sizeof(*req));
530 if (!req)
531 goto error;
533 if (tc->type != Tversion) {
534 tc->tag = spc_get_id(fs->tagpool);
535 if (tc->tag == NOTAG) {
536 free(req);
537 sp_werror("tag pool full", EIO);
538 goto error;
541 sp_set_tag(tc, tc->tag);
544 req->tag = tc->tag;
545 req->tc = tc;
546 req->rc = NULL;
547 req->cb = cb;
548 req->cba = cba;
549 req->fs = fs;
550 req->flushed = 0;
551 req->next = NULL;
553 if (fs->pend_last)
554 fs->pend_last->next = req;
555 else
556 fs->pend_first = req;
558 fs->pend_last = req;
559 if (!fs->pend_first->next && spfd_can_write(fs->spfd))
560 spc_fd_write(fs);
562 return 0;
564 error:
565 (*cb)(cba, NULL);
566 return -1;
569 static void
570 spc_rpc_cb(void *cba, Spfcall *rc)
572 int ecode;
573 char *ename;
574 Spcrpc *r;
576 r = cba;
577 r->rc = rc;
578 sp_rerror(&ename, &ecode);
579 if (ecode == 0) {
580 ename = r->fs->ename;
581 ecode = r->fs->ecode;
584 if (ecode) {
585 r->ecode = ecode;
586 r->ename = strdup(ename);
587 if (!r->ename) {
588 r->ename = Enomem;
589 r->ecode = ENOMEM;
595 spc_rpc(Spcfsys *fs, Spfcall *tc, Spfcall **rc)
597 char *ename;
598 Spcrpc r;
600 if (rc)
601 *rc = NULL;
603 r.fs = fs;
604 r.tc = tc;
605 r.rc = NULL;
606 r.ename = NULL;
607 r.ecode = 0;
609 spc_rpcnb(fs, tc, spc_rpc_cb, &r);
610 while (!r.ename && !r.rc)
611 sp_poll_once();
613 if (r.ename) {
614 sp_werror(r.ename, r.ecode);
615 goto error;
618 if (r.rc && r.rc->type == Rerror) {
619 ename = sp_strdup(&r.rc->ename);
620 if (ename)
621 sp_werror(ename, r.rc->ecode);
623 free(ename);
624 goto error;
627 free(r.ename);
628 if (rc)
629 *rc = r.rc;
630 else
631 free(r.rc);
633 return 0;
635 error:
636 if (r.ename != Enomem)
637 free(r.ename);
638 free(r.rc);
639 return -1;
642 static Spfcall *
643 spc_fcall_alloc(u32 msize)
645 Spfcall *fc;
647 fc = sp_malloc(sizeof(*fc) + msize);
648 if (!fc)
649 return NULL;
651 fc->pkt = (u8*) fc + sizeof(*fc);
652 fc->size = msize;
654 return fc;