/* This file is in the public domain. */ #define QFS_VERBOSE /* * Filesystem backend implementation that mounts a libqfs * quasi-filesystem. * * This obeys permission bits to an extent; the world read and execute * bits on files and directories control the ability to read and * search them. */ #include #include #include #include #include #include #include #include #include #include #ifdef QFS_VERBOSE #include #include #include #define NSTRBUFS 8 #endif #include typedef unsigned long long int ULLI; typedef enum { FQNT_ERR = 50, FQNT_DIR, FQNT_FILE, FQNT_LINK, } FQNT; typedef struct fq_node FQ_NODE; typedef struct fq_mount FQ_MOUNT; typedef struct fq_stat FQ_STAT; typedef struct fq_stat_state FQ_STAT_STATE; typedef struct fq_readdir_state FQ_READDIR_STATE; typedef struct fq_lookup_state FQ_LOOKUP_STATE; typedef struct fq_read_state FQ_READ_STATE; typedef struct fq_readlink_state FQ_READLINK_STATE; struct fq_readlink_state { int done; QFS_ERR err; QFS_MSTR linkto; } ; struct fq_read_state { int done; QFS_ERR err; unsigned int len; unsigned char *data; } ; struct fq_lookup_state { int done; int err; QFS_MSTR ino; char type; QFS_FSIZE size; unsigned int mode; QFS_TIME mtime; } ; struct fq_readdir_state { FQ_MOUNT *m; int done; FQ_NODE *dir; QFS_ERR err; int nents; QFS_DIRENT **entv; int nstrs; char **strv; char *errv; } ; struct fq_stat { FQNT type; union { struct { // if FQNT_ERR int err; } e; struct { // if not FQNT_DIR unsigned int mode; } d; struct { // if not FQNT_FILE unsigned int mode; ULLI size; ULLI mtime; } f; struct { // if not FQNT_LINK ULLI size; } l; } u; } ; struct fq_stat_state { FQ_STAT s; int got; } ; struct fq_node { unsigned int num; ULLI serial; FSTREE_STRING path; // must have NUL unsigned int pathhash; QFS_MSTR qi; FQNT type; unsigned int mode; FSTREE_STAT stat; union { struct { // if FQNT_DIR int nents; FSTREE_DIRENT *ents; char *strs; int rdderr; } dir; // nothing if FQNT_FILE // nothing if FQNT_LINK } u; } ; struct fq_mount { QFS_C *q; int failed; int fd; unsigned int nextnode; AVL *nodes_by_num; AVL *nodes_by_path; AVL *nodes_by_qstr; AVL *nodes_free; unsigned int rootmode; } ; static ULLI nextnserial = 0; #define ZMSTR ((QFS_MSTR){.s=0,.l=0}) #define ZCSTR ((QFS_CSTR){.s=0,.l=0}) static const unsigned int crctable[] = { #include "crctable.h" }; #ifdef QFS_VERBOSE static void (*qfs_verbose)(const char *, int) = 0; static void verbosity_(const char *, ...) __attribute__((__format__(__printf__,1,2))); static void verbosity_(const char *fmt, ...) { va_list ap; char *s; int l; if (! qfs_verbose) return; va_start(ap,fmt); l = vasprintf(&s,fmt,ap); va_end(ap); (*qfs_verbose)(s,l); } #define verbosity if (qfs_verbose) verbosity_ void (*fstree_qfs_set_verbose(void (*fn)(const char *, int)))(const char *, int) { void (*prev)(const char *, int); prev = qfs_verbose; qfs_verbose = fn; return(prev); } #else #define verbosity(foo...) do ; while (0) #endif static void fatal(void) __attribute__((__noreturn__)); static void fatal(void) { abort(); } static int q_to_f_err(int e) { switch (e) { case EINVAL: return(FE_INVAL); break; case ENOENT: return(FE_NOENT); break; case ENOTDIR: return(FE_NOTDIR); break; case ENOMEM: return(FE_NOMEM); break; case ENOTPLAIN: return(FE_NOTPLAIN); break; case ELOOP: return(FE_LOOP); break; case EPERM: return(FE_PERM); break; } return(FE_INTERNAL); } static unsigned int hash_data(const void *data, int len) { unsigned int h; int i; h = 0xfeedface; for (i=0;i> 8) ^ crctable[(h^((const unsigned char *)data)[i])&0xff]; } return(h); } static int get_first_node(void *npv, void *nv) { *(FQ_NODE **)npv = nv; return(-1); } static FQ_NODE *new_fq_node(FQ_MOUNT *m) { FQ_NODE *n; if (avl_walk(m->nodes_free,&get_first_node,0,0,&n)) { if (avl_delete(m->nodes_free,n) != n) fatal(); verbosity("new_fq_node delete %d %p %llu from nodes_free for %p\n",n->num,(void *)n,n->serial,(void *)m); } else { n = malloc(sizeof(FQ_NODE)); if (! n) return(0); n->num = m->nextnode++; } n->serial = nextnserial++; verbosity("new_fq_node returning %d %p %llu for %p\n",n->num,(void *)n,n->serial,(void *)m); return(n); } static int qread(FQ_MOUNT *m) { char rbuf[8192]; int nr; struct pollfd pfd; int prv; if (m->failed) fatal(); while (1) { nr = read(m->fd,&rbuf[0],sizeof(rbuf)); if (nr < 0) { switch (errno) { case EINTR: continue; break; case EWOULDBLOCK: pfd.fd = m->fd; pfd.events = POLLIN | POLLRDNORM; prv = poll(&pfd,1,INFTIM); if (prv < 0) { switch (errno) { case EINTR: continue; break; } m->failed = 1; return(1); } continue; break; default: m->failed = 1; return(1); break; } } verbosity("qread got %d for %p\n",nr,(void *)m); if (nr == 0) { m->failed = 1; return(1); } qfs_c_receive(m->q,&rbuf[0],nr); return(0); } } static QFS_CSTR qs_mtoc(QFS_MSTR s) { return((QFS_CSTR){.s=s.s,.l=s.l}); } #ifdef QFS_VERBOSE static const char *qs_cstr(QFS_CSTR s) { static char *rstrs[NSTRBUFS] = { 0 }; static int hand = 0; ES e; int i; char *r; es_init(&e); es_append_1(&e,'<'); for (i=0;i'); es_append_1(&e,'\0'); i = es_len(&e); r = malloc(i); bcopy(es_buf(&e),r,i); free(rstrs[hand]); rstrs[hand] = r; hand = (hand ? hand : NSTRBUFS) - 1; return(r); } #endif #ifdef QFS_VERBOSE static const char *qs_mstr(QFS_MSTR s) { return(qs_cstr(qs_mtoc(s))); } #endif static QFS_MSTR dup_qcs(QFS_CSTR s) { char *n; if (s.l) { n = malloc(s.l); bcopy(s.s,n,s.l); return((QFS_MSTR){.s=n,.l=s.l}); } else { return(ZMSTR); } } /* * Grr. bcmp doesn't promise the signum of its nonzero returns; * strncmp is unusable because the QFS_STRs may contain NULs. So we * hand-roll our own and hope the performance penalty is tolerable. */ static int qstr_compare(const QFS_CSTR a, const QFS_CSTR b) { const unsigned char *ap; const unsigned char *bp; int i; if (a.l < b.l) return(-1); if (a.l > b.l) return(1); ap = (const void *)a.s; bp = (const void *)b.s; for (i=a.l;i>0;i--,ap++,bp++) { if (*ap < *bp) return(-1); if (*ap > *bp) return(1); } return(0); } static void setup_dir(FQ_NODE *n) { n->type = FQNT_DIR; n->u.dir.nents = -1; n->u.dir.ents = 0; n->u.dir.strs = 0; n->u.dir.rdderr = 0; } static void destroy_dir(FQ_NODE *n) { if (n->type != FQNT_DIR) fatal(); if (n->path.l >= 0) free(n->path.s); if (n->qi.l >= 0) free(n->qi.s); free(n); } static void fq_stat_cb(void *sv, QFS_ERR e, char type, unsigned int mode, QFS_FSIZE size, QFS_TIME mtime) { FQ_STAT_STATE *s; s = sv; s->got = 1; if (e) { verbosity("fq_stat_cb: state %p got err=%d\n",sv,e); s->s.type = FQNT_ERR; s->s.u.e.err = e; } else { verbosity("fq_stat_cb: state %p got type=%c mode=%03o size=%llu mtime=%llu\n",sv,type,mode,(ULLI)size,(ULLI)mtime); switch (type) { case 'd': s->s.type = FQNT_DIR; s->s.u.d.mode = mode; break; case 'f': s->s.type = FQNT_FILE; s->s.u.f.mode = mode; s->s.u.f.size = size; s->s.u.f.mtime = mtime; break; case 'l': s->s.type = FQNT_LINK; s->s.u.f.size = size; break; default: fatal(); break; } } } static int fstree_qfs_start(void *mv) { FQ_MOUNT *m; FQ_NODE *n; m = mv; if (m->failed) return(FE_INTERNAL); do <"nomem"> { n = new_fq_node(m); if (! n) break <"nomem">; n->path.l = -1; n->qi.l = -1; n->qi = ZMSTR; setup_dir(n); n->mode = m->rootmode; n->path = fstree_s_copy(".",1); if (n->path.l < 0) break <"nomem">; n->pathhash = hash_data(".",1); if (avl_insert(m->nodes_by_num,n)) fatal(); if (avl_insert(m->nodes_by_path,n)) fatal(); if (avl_insert(m->nodes_by_qstr,n)) fatal(); verbosity("fstree_qfs_start: mount %p, root node %p\n",(void *)m,(void *)n); return(FE_SUCCESS); } while (0); if (n) destroy_dir(n); return(FE_NOMEM); } static void fstree_qfs_done(void *mv) { (void)mv; fatal(); } static void fstree_qfs_release(void *mv, FSTREE_NODE n) { (void)mv; (void)n; } static FSTREE_STAT fstree_qfs_stat(void *mv, FSTREE_NODE nn) { FQ_NODE cn; FQ_MOUNT *m; FQ_NODE *n; FQ_STAT_STATE s; m = mv; if (m->failed) return((FSTREE_STAT){.type=FST_ERR,.u={.err={.err=FE_INTERNAL}}}); verbosity("fstree_qfs_stat: %u on %p\n",nn,mv); if (nn == FSTREE_QFS_ROOT) { verbosity("fstree_qfs_stat: root node\n"); return((FSTREE_STAT){.type=FST_DIR,.u={.dir={.mode=m->rootmode}}}); } cn.num = nn; n = avl_find(m->nodes_by_num,&cn); if (! n) fatal(); s.got = 0; verbosity("fstree_qfs_stat: stat query %s, state %p\n",qs_mstr(n->qi),(void *)&s); qfs_c_stat(m->q,qs_mtoc(n->qi),&fq_stat_cb,&s); while (! s.got) if (qread(m)) return((FSTREE_STAT){.type=FST_ERR,.u={.err={.err=FE_INTERNAL}}}); switch (s.s.type) { case FQNT_ERR: verbosity("fstree_qfs_stat: error %d -> %d\n",s.s.u.e.err,q_to_f_err(s.s.u.e.err)); return((FSTREE_STAT){.type=FST_ERR,.u={.err={.err=q_to_f_err(s.s.u.e.err)}}}); break; case FQNT_DIR: verbosity("fstree_qfs_stat: DIR mode=%03o\n",s.s.u.d.mode); return((FSTREE_STAT){.type=FST_DIR,.u={.dir={.mode=s.s.u.d.mode}}}); break; case FQNT_FILE: verbosity("fstree_qfs_stat: PLAIN mode=%03o size=%llu\n",s.s.u.d.mode,(ULLI)s.s.u.f.size); return((FSTREE_STAT){.type=FST_PLAIN,.u={.plain={.mode=s.s.u.f.mode,.size=s.s.u.f.size,.mtime=s.s.u.f.mtime}}}); break; case FQNT_LINK: verbosity("fstree_qfs_stat: LINK size=%llu\n",(ULLI)s.s.u.f.size); return((FSTREE_STAT){.type=FST_LINK,.u={.link={.size=s.s.u.l.size}}}); break; } fatal(); } static void fq_lookup_cb(void *sv, QFS_ERR err, QFS_CSTR ino, char type, QFS_FSIZE size, unsigned int mode, QFS_TIME mtime) { FQ_LOOKUP_STATE *s; s = sv; if (err) { verbosity("fq_lookup_cb: state %p got err=%d\n",sv,err); s->err = err; s->done = 1; return; } verbosity("fq_lookup_cb: state %p got ino=%s type=%c size=%llu mode=%03o mtime=%llu\n",sv,qs_cstr(ino),type,(ULLI)size,mode,mtime); s->ino = dup_qcs(ino); s->type = type; s->size = size; s->mode = mode; s->mtime = mtime; s->done = 1; } static FSTREE_NODESTAT fstree_qfs_lookup(void *mv, FSTREE_NODE dn, const char *name, int namelen) { FQ_NODE cn; FQ_MOUNT *m; FQ_NODE *d; FQ_LOOKUP_STATE s; FQ_NODE *n; FSTREE_STRING newpath; m = mv; if (m->failed) return((FSTREE_NODESTAT){.stat={.type=FST_ERR,.u={.err={.err=FE_INTERNAL}}}}); verbosity("fstree_qfs_lookup: %.*s in %u on %p\n",namelen,name,dn,mv); cn.num = dn; d = avl_find(m->nodes_by_num,&cn); if (! d) fatal(); if (d->type != FQNT_DIR) { verbosity("fstree_qfs_lookup: NOTDIR\n"); return((FSTREE_NODESTAT){.stat={.type=FST_ERR,.u={.err={.err=FE_NOTDIR}}}}); } if ((namelen == 1) && (name[0] == '.')) { verbosity("fstree_qfs_lookup: ., returning d\n"); return((FSTREE_NODESTAT){.node=dn,.stat=d->stat}); } if ((namelen == 2) && (name[0] == '.') && (name[1] == '.')) { int i; for (i=d->path.l-1;(i>=0)&&(d->path.s[i]!='/');i--) ; if (i < 0) { cn.num = 0; n = avl_find(m->nodes_by_num,&cn); if (! n) fatal(); verbosity("fstree_qfs_lookup: .. %.*s -> to subtree root\n",d->path.l,d->path.s); return((FSTREE_NODESTAT){.node=0,.stat=n->stat}); } cn.path.s = d->path.s; cn.path.l = i; cn.pathhash = hash_data(cn.path.s,i); n = avl_find(m->nodes_by_path,&cn); if (n) { verbosity("fstree_qfs_lookup: .. %.*s -> %.*s found\n",d->path.l,d->path.s,i,d->path.s); return((FSTREE_NODESTAT){.node=n->num,.stat=n->stat}); } verbosity("fstree_qfs_lookup: .. %.*s -> %.*s not found\n",d->path.l,d->path.s,i,d->path.s); newpath = fstree_s_copy(cn.path.s,cn.path.l); } else if ((d->path.l == 1) && (d->path.s[0] == '.')) { newpath = fstree_s_copy(name,namelen); } else { newpath.l = d->path.l + 1 + namelen; newpath.s = malloc(newpath.l+1); bcopy(d->path.s,newpath.s,d->path.l); newpath.s[d->path.l] = '/'; bcopy(name,newpath.s+d->path.l+1,namelen); newpath.s[newpath.l] = '\0'; } verbosity("fstree_qfs_lookup: new path %.*s\n",newpath.l,newpath.s); cn.path = newpath; cn.pathhash = hash_data(newpath.s,newpath.l); n = avl_find(m->nodes_by_path,&cn); if (n) { free(newpath.s); verbosity("fstree_qfs_lookup: found by path\n"); return((FSTREE_NODESTAT){.node=n->num,.stat=n->stat}); } s.done = 0; s.err = 0; verbosity("fstree_qfs_lookup: lookup query %.*s in %s, state %p\n",namelen,name,qs_mstr(d->qi),(void *)&s); qfs_c_lookup(m->q,qs_mtoc(d->qi),(QFS_CSTR){.s=name,.l=namelen},&fq_lookup_cb,&s); while (! s.done) { if (qread(m)) { free(newpath.s); return((FSTREE_NODESTAT){.stat={.type=FST_ERR,.u={.err={.err=FE_INTERNAL}}}}); } } if (s.err) { verbosity("fstree_qfs_lookup: error %d -> %d\n",s.err,q_to_f_err(s.err)); return((FSTREE_NODESTAT){.stat={.type=FST_ERR,.u={.err={.err=q_to_f_err(s.err)}}}}); } n = new_fq_node(m); n->path = newpath; n->pathhash = hash_data(n->path.s,n->path.l); n->qi = s.ino; s.ino.s = 0; switch (s.type) { case 'd': setup_dir(n); n->stat.type = FST_DIR; n->stat.u.dir.mode = s.mode; verbosity("fstree_qfs_lookup: new dir %d %.*s [%03o]\n",n->num,n->path.l,n->path.s,s.mode); break; case 'f': n->type = FQNT_FILE; n->stat.type = FST_PLAIN; n->stat.u.plain.mode = s.mode; n->stat.u.plain.size = s.size; n->stat.u.plain.mtime = s.mtime; verbosity("fstree_qfs_lookup: new file %d %.*s [%03o %llu %llu]\n",n->num,n->path.l,n->path.s,s.mode,(ULLI)s.size,(ULLI)s.mtime); break; case 'l': n->type = FQNT_LINK; n->stat.type = FST_LINK; n->stat.u.link.size = s.size; verbosity("fstree_qfs_lookup: new link %d %.*s [%llu]\n",n->num,n->path.l,n->path.s,(ULLI)s.size); break; default: fatal(); break; } n->mode = s.mode; if (avl_insert(m->nodes_by_num,n)) fatal(); if (avl_insert(m->nodes_by_path,n)) fatal(); if (avl_insert(m->nodes_by_qstr,n)) fatal(); return((FSTREE_NODESTAT){.node=n->num,.stat=n->stat}); } static void fq_readdir_cb(void *sv, QFS_ERR err, unsigned int nent, const QFS_DIRENT *ents) { FQ_READDIR_STATE *s; int i; int j; int tl; char *sb; const QFS_DIRENT *re; QFS_DIRENT *se; QFS_DIRENT **evp; s = sv; if (err) { verbosity("fq_readdir_cb: state %p got err=%d\n",sv,err); s->done = 1; s->err = err; return; } verbosity("fq_readdir_cb: state %p got nent %d\n",sv,nent); if (nent == 0) { s->done = 1; return; } tl = 0; for (i=0;instrs ++; s->strv = realloc(s->strv,s->nstrs*sizeof(char *)); s->strv[s->nstrs-1] = sb; i = s->nents + nent; s->entv = realloc(s->entv,i*sizeof(QFS_DIRENT *)); evp = s->entv + s->nents; s->nents += nent; re = ents; j = 0; for (i=0;iname.l > 0) && (re->name.s[0] == '.') && ( (re->name.l == 1) || ( (re->name.s[1] == '.') && (re->name.l == 2) ) ) ) continue; se = malloc(sizeof(QFS_DIRENT)); evp[j++] = se; *se = *re; se->name.s = sb; bcopy(re->name.s,sb,re->name.l); sb += re->name.l; se->ino.s = sb; bcopy(re->ino.s,sb,re->ino.l); sb += re->ino.l; } s->nents -= i - j; verbosity("fq_readdir_cb: state %p asking for more\n",(void *)s); qfs_c_readdir(s->m->q,qs_mtoc(s->dir->qi),qs_mtoc(ents[nent-1].cookie),2,&fq_readdir_cb,s); } static void convert_readdir_step1(FQ_READDIR_STATE *s) { FQ_NODE *d; int tl; int i; char *sp; QFS_DIRENT *qe; FSTREE_DIRENT *fe; d = s->dir; if (d->type != FQNT_DIR) fatal(); tl = 0; for (i=s->nents-1;i>=0;i--) tl += s->entv[i]->name.l + 1; sp = malloc(tl); d->u.dir.strs = sp; d->u.dir.nents = s->nents; d->u.dir.ents = malloc(d->u.dir.nents*sizeof(FSTREE_DIRENT)); for (i=s->nents-1;i>=0;i--) { qe = s->entv[i]; fe = d->u.dir.ents + i; fe->name = sp; bcopy(qe->name.s,sp,qe->name.l); sp += qe->name.l; *sp++ = '\0'; fe->namelen = qe->name.l; } if (sp != d->u.dir.strs + tl) fatal(); } static void convert_readdir_step2(FQ_READDIR_STATE *s) { FQ_NODE *d; int i; d = s->dir; if (d->type != FQNT_DIR) fatal(); for (i=s->nents-1;i>=0;i--) { if (s->errv[i]) { s->nents --; if (i <= s->nents) d->u.dir.ents[i] = d->u.dir.ents[s->nents]; } } } static void cleanup_readdir_state(FQ_READDIR_STATE *s) { int i; free(s->errv); for (i=s->nents-1;i>=0;i--) free(s->entv[i]); free(s->entv); for (i=s->nstrs-1;i>=0;i--) free(s->strv[i]); free(s->strv); } static int fstree_qfs_readdir(void *mv, FSTREE_NODE dn, long int *offp, FSTREE_DIRENT *ents, int nents, char *strbuf, int strsize) { FQ_NODE cn; FQ_MOUNT *m; FQ_NODE *d; FQ_READDIR_STATE rs; long int o; int ex; int sx; FSTREE_DIRENT *de; FSTREE_DIRENT *re; int i; FQ_STAT_STATE ss; if (!ents && !nents && !strbuf && !strsize) { verbosity("fstree_qfs_readdir: initial offset case\n"); *offp = 0; return(0); } m = mv; if (m->failed) return(-FE_INTERNAL); cn.num = dn; d = avl_find(m->nodes_by_num,&cn); if (! d) fatal(); verbosity("fstree_qfs_readdir: read %d/%d from %ld in %d on %p\n",nents,strsize,*offp,dn,mv); if (d->type != FQNT_DIR) { verbosity("fstree_qfs_readdir: ENOTDIR\n"); return(-FE_NOTDIR); } if (d->u.dir.nents < 0) { verbosity("fstree_qfs_readdir: loading entries\n"); if (d->u.dir.rdderr) return(d->u.dir.rdderr); rs.m = m; rs.done = 0; rs.dir = d; rs.err = 0; rs.nents = 0; rs.entv = 0; rs.nstrs = 0; rs.strv = 0; rs.errv = 0; verbosity("fstree_qfs_readdir: requesting\n"); qfs_c_readdir(m->q,qs_mtoc(d->qi),ZCSTR,1,&fq_readdir_cb,&rs); while (! rs.done) { if (qread(m)) { cleanup_readdir_state(&rs); return(FE_INTERNAL); } } if (rs.err) { d->u.dir.rdderr = q_to_f_err(rs.err); verbosity("fstree_qfs_readdir: request errored %d -> %d\n",rs.err,d->u.dir.rdderr); cleanup_readdir_state(&rs); return(d->u.dir.rdderr); } verbosity("fstree_qfs_readdir: processing directory\n"); convert_readdir_step1(&rs); rs.errv = malloc(rs.nents); bzero(rs.errv,rs.nents); for (i=0;iq,qs_mtoc(rs.entv[i]->ino),&fq_stat_cb,&ss); while (! ss.got) { if (qread(m)) { cleanup_readdir_state(&rs); return(FE_INTERNAL); } } switch (ss.s.type) { case FQNT_ERR: rs.errv[i] = 1; break; case FQNT_DIR: d->u.dir.ents[i].stat = (FSTREE_STAT){.type=FST_DIR,.u={.dir={.mode=ss.s.u.d.mode}}}; break; case FQNT_FILE: d->u.dir.ents[i].stat = (FSTREE_STAT){.type=FST_PLAIN,.u={.plain={.mode=ss.s.u.f.mode,.size=ss.s.u.f.size,.mtime=ss.s.u.f.mtime}}}; break; case FQNT_LINK: d->u.dir.ents[i].stat = (FSTREE_STAT){.type=FST_LINK,.u={.link={.size=ss.s.u.l.size}}}; break; } } convert_readdir_step2(&rs); cleanup_readdir_state(&rs); } verbosity("fstree_qfs_readdir: copying entries\n"); o = *offp; if ((o < 0) || (o >= d->u.dir.nents)) return(0); ex = 0; sx = 0; do <"overflow"> { while <"ents"> (1) { if (o >= d->u.dir.nents) break <"ents">; if (ex >= nents) break <"overflow">; de = d->u.dir.ents + o; if (sx+de->namelen+1 > strsize) break <"overflow">; re = ents + ex; re->name = strbuf + sx; bcopy(de->name,strbuf+sx,de->namelen+1); re->namelen = de->namelen; re->stat = de->stat; ex ++; sx += de->namelen + 1; o ++; } *offp = o; verbosity("fstree_qfs_readdir: returning (out of entries to copy)\n"); return(ex); } while (0); if (ex) { verbosity("fstree_qfs_readdir: returning (out of space: ex=%d sx=%d)\n",ex,sx); *offp = o; return(ex); } else { verbosity("fstree_qfs_readdir: returning (overflow with no entries)\n"); return(-FE_NOMEM); } } static void fq_read_cb(void *pv, QFS_ERR err, unsigned int len, const void *data) { FQ_READ_STATE *rs; rs = pv; if (err) { verbosity("fq_read_cb: state %p got err=%d\n",pv,err); rs->err = err; } else { verbosity("fq_read_cb: state %p got len=%u\n",pv,len); rs->err = 0; rs->len = len; rs->data = malloc(len); bcopy(data,rs->data,len); } rs->done = 1; } static int fstree_qfs_read(void *mv, FSTREE_NODE nn, FSTREE_OFFSET o, void *buf, int len) { FQ_MOUNT *m; FQ_NODE cn; FQ_NODE *n; FQ_READ_STATE rs; m = mv; if (m->failed) return(-FE_INTERNAL); cn.num = nn; n = avl_find(m->nodes_by_num,&cn); if (! n) fatal(); rs.done = 0; verbosity("fstree_qfs_read: reading %d at %llu from %u on %p: sending query, state %p\n",len,(ULLI)o,nn,mv,(void *)&rs); qfs_c_read(m->q,qs_mtoc(n->qi),o,(len>8192)?8192:len,&fq_read_cb,&rs); while (! rs.done) if (qread(m)) return(-FE_INTERNAL); if (rs.err) { verbosity("fstree_qfs_read: returning error %d\n",q_to_f_err(rs.err)); return(-q_to_f_err(rs.err)); } else { if (rs.len > len) fatal(); bcopy(rs.data,buf,rs.len); free(rs.data); verbosity("fstree_qfs_read: returning success %d\n",rs.len); return(rs.len); } } static void fq_readlink_cb(void *pv, QFS_ERR err, QFS_CSTR linkto) { FQ_READLINK_STATE *rls; rls = pv; if (err) { verbosity("fq_readlink_cb: state %p got err=%d\n",pv,err); rls->err = err; } else { verbosity("fq_readlink_cb: state %p got linkto=%.*s\n",pv,linkto.l,linkto.s); rls->err = 0; rls->linkto = dup_qcs(linkto); } rls->done = 1; } static int fstree_qfs_readlink(void *mv, FSTREE_NODE nn, void *buf, int len) { FQ_MOUNT *m; FQ_NODE cn; FQ_NODE *n; FQ_READLINK_STATE rls; int nb; m = mv; if (m->failed) return(-FE_INTERNAL); cn.num = nn; n = avl_find(m->nodes_by_num,&cn); if (! n) fatal(); rls.done = 0; verbosity("fstree_qfs_readlink: reading %u on %p: sending query, state %p\n",nn,mv,(void *)&rls); qfs_c_readlink(m->q,qs_mtoc(n->qi),&fq_readlink_cb,&rls); while (! rls.done) if (qread(m)) return(-FE_INTERNAL); if (rls.err) { verbosity("fstree_qfs_readlink: returning error %d -> %d\n",rls.err,q_to_f_err(rls.err)); return(-q_to_f_err(rls.err)); } else { nb = (rls.linkto.l < len) ? rls.linkto.l : len; bcopy(rls.linkto.s,buf,nb); free(rls.linkto.s); verbosity("fstree_qfs_readlink: stored %d, returning %d\n",nb,rls.linkto.l); return(rls.linkto.l); } } static int fstree_qfs_node_compare_num(void *av, void *bv) { FQ_NODE *a; FQ_NODE *b; a = av; b = bv; if (a->num < b->num) return(-1); if (a->num > b->num) return(1); return(0); } static int fstree_qfs_node_compare_path(void *av, void *bv) { FQ_NODE *a; FQ_NODE *b; a = av; b = bv; if (a->pathhash < b->pathhash) return(-1); if (a->pathhash > b->pathhash) return(1); if (a->path.l < b->path.l) return(-1); if (a->path.l > b->path.l) return(1); return(strncmp(a->path.s,b->path.s,a->path.l)); } static int fstree_qfs_node_compare_qstr(void *av, void *bv) { return(qstr_compare(qs_mtoc(((FQ_NODE *)av)->qi),qs_mtoc(((FQ_NODE *)bv)->qi))); } static const AVL_OPS fq_m_num_avl_ops = { .flags = AVL_F_NODUPS, .compare = &fstree_qfs_node_compare_num }; static const AVL_OPS fq_m_path_avl_ops = { .flags = AVL_F_NODUPS, .compare = &fstree_qfs_node_compare_path }; static const AVL_OPS fq_m_qstr_avl_ops = { .flags = AVL_F_NODUPS, .compare = &fstree_qfs_node_compare_qstr }; static void fstree_qfs_write(QFS_C *c, const void *data, int len) { FQ_MOUNT *m; const char *dp; int o; int w; struct pollfd pfd; int prv; m = qfs_c_app_priv(c); if (m->q != c) fatal(); if (m->failed) { verbosity("fstree_qfs_write already failed %p\n",(void *)m); return; } verbosity("fstree_qfs_write writing %d for %p\n",len,(void *)m); dp = data; o = 0; while (o < len) { w = write(m->fd,dp+o,len-o); if (w < 0) { switch (errno) { case EINTR: continue; break; case EWOULDBLOCK: pfd.fd = m->fd; pfd.events = POLLOUT | POLLWRNORM; prv = poll(&pfd,1,INFTIM); if (prv < 0) { switch (errno) { case EINTR: continue; break; } m->failed = 1; return; } continue; break; default: m->failed = 1; return; break; } } verbosity("fstree_qfs_write wrote %d\n",w); o += w; } } static QFS_C_OPS qfsops = { .write = &fstree_qfs_write }; const FSTREE_OPS fstree_qfs_ops = FSTREE_OPS_INIT(fstree_qfs); void *fstree_qfs_priv(const char *rendezvous, int *errp) { int pl; int l; struct sockaddr_un *s_un; QFS_C *qfs; FQ_MOUNT *m; int fd; int e; FQ_STAT_STATE s; verbosity("fstree_qfs_priv: setting up for %s\n",rendezvous); qfs = 0; m = 0; s_un = 0; fd = -1; do <"fail"> { pl = strlen(rendezvous); l = sizeof(*s_un) - sizeof(s_un->sun_path) + pl; s_un = malloc(l); s_un->sun_len = l; s_un->sun_family = AF_LOCAL; bcopy(rendezvous,&s_un->sun_path[0],pl); fd = socket(AF_LOCAL,SOCK_STREAM,0); if (fd < 0) { verbosity("fstree_qfs_priv: socket() failed [%s]\n",strerror(errno)); free(s_un); e = FE_INTERNAL; break <"fail">; } if (connect(fd,(void *)s_un,l) < 0) { verbosity("fstree_qfs_priv: connect() failed [%s]\n",strerror(errno)); e = FE_INTERNAL; break <"fail">; } free(s_un); fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0)|O_NONBLOCK); do <"nomem"> { m = malloc(sizeof(FQ_MOUNT)); if (! m) break <"nomem">; m->fd = fd; m->failed = 0; m->nodes_by_num = avl_new(&fq_m_num_avl_ops); if (! m->nodes_by_num) break <"nomem">; m->nodes_by_path = avl_new(&fq_m_path_avl_ops); if (! m->nodes_by_path) break <"nomem">; m->nodes_by_qstr = avl_new(&fq_m_qstr_avl_ops); if (! m->nodes_by_qstr) break <"nomem">; m->nodes_free = avl_new(&fq_m_num_avl_ops); if (! m->nodes_free) break <"nomem">; m->nextnode = 0; qfs = qfs_c_init(&qfsops,m); qfs_c_up(qfs); m->q = qfs; s.got = 0; verbosity("fstree_qfs_priv: issuing first stat query, state %p\n",(void *)&s); qfs_c_stat(m->q,ZCSTR,&fq_stat_cb,&s); while (! s.got) { if (qread(m)) { e = FE_INTERNAL; break <"fail">; } } if (s.s.type == FQNT_ERR) { e = q_to_f_err(s.s.u.e.err); verbosity("fstree_qfs_priv: stat errored %d -> %d\n",s.s.u.e.err,e); break <"fail">; } if (s.s.type != FQNT_DIR) { verbosity("fstree_qfs_priv: stat worked, NOTDIR\n"); e = FE_NOTDIR; break <"fail">; } m->rootmode = s.s.u.d.mode; verbosity("fstree_qfs_priv: returning, success\n"); return(m); } while (0); e = FE_NOMEM; } while (0); if (m) { if (m->nodes_by_num) avl_destroy(m->nodes_by_num,0,0); if (m->nodes_by_path) avl_destroy(m->nodes_by_path,0,0); if (m->nodes_by_qstr) avl_destroy(m->nodes_by_qstr,0,0); if (m->nodes_free) avl_destroy(m->nodes_free,0,0); free(m); } if (fd >= 0) close(fd); free(s_un); if (qfs) qfs_c_destroy(qfs); verbosity("fstree_qfs_priv: returning, failure %s\n",fstree_strerror(e)); if (errp) *errp = e; return(0); }