__private_extern__ int readdirs_9p(mount_9p *nmp, fid_9p fid, dir_9p **d, uint32_t *nd) { uint8_t *p, *newp; uint32_t n; int e, ts; TRACE(); *d = NULL; *nd = 0; ts = 0; p = NULL; for (;;) { newp = malloc_9p(ts+DIRMAX); if (newp == NULL) { e = ENOMEM; break; } bcopy(p, newp, ts); free_9p(p); p = newp; if ((e=read_9p(nmp, fid, p+ts, DIRMAX, ts, &n)) || n==0) break; ts += n; } if (!e) e = dirpackage(p, ts, d, nd, ISSET(nmp->flags, F_DOTU)); free_9p(p); return e; }
__private_extern__ int wstat_9p(mount_9p *nmp, fid_9p fid, dir_9p *dp) { Fcall tx, rx; void *p; uint32_t n; int e, dotu; TRACE(); dotu = ISSET(nmp->flags, F_DOTU); n = sizeD2M(dp, dotu); p = malloc_9p(n); if (p == NULL) return ENOMEM; if(convD2M(dp, p, n, dotu) != n){ free_9p(p); return EINVAL; } tx.type = Twstat; tx.fid = fid; tx.stat = p; tx.nstat = n; e = rpc_9p(nmp, &tx, &rx, NULL); free_9p(p); return e; }
__private_extern__ int walk_9p(mount_9p *nmp, fid_9p fid, char *name, int nname, fid_9p *fidp, qid_9p *qid) { Fcall tx, rx; char *s; int e; TRACE(); tx.type = Twalk; tx.fid = fid; tx.newfid = nextfid(nmp); tx.nwname = 0; s = NULL; if(name){ if ((e=strndup(name, nname, &s))) return e; tx.nwname = 1; tx.wname[0] = s; } e = rpc_9p(nmp, &tx, &rx, NULL); free_9p(s); if (e) return e; // Hardening if (name && rx.nwqid == 0) return ENOENT; *fidp = tx.newfid; if(rx.nwqid == 0) *qid = rx.qid; else *qid = rx.wqid[rx.nwqid-1]; return e; }
__private_extern__ int stat_9p(mount_9p *nmp, fid_9p fid, dir_9p **dpp) { Fcall tx, rx; Dir *dp; void *p; int e, n; TRACE(); p = NULL; dp = NULL; tx.type = Tstat; tx.fid = fid; if ((e=rpc_9p(nmp, &tx, &rx, &p))) return e; n = GBIT16((uint8_t*)p); dp = malloc_9p(sizeof(Dir) + BIT16SZ + n); if (dp == NULL) { e = ENOMEM; goto error; } if(convM2D(rx.stat, rx.nstat, dp, (char*)&dp[1], ISSET(nmp->flags, F_DOTU)) != rx.nstat) { DEBUG("convM2D"); e = EBADRPC; goto error; } error: free_9p(p); *dpp = dp; return e; }
static int nread_9p(node_9p *np, uio_t uio) { openfid_9p *op; uint32_t n, l, sz; char *p; int e; TRACE(); op = &np->openfid[OREAD]; if (op->fid == NOFID) op = &np->openfid[ORDWR]; if (op->fid == NOFID) return EBADF; sz = np->iounit; if (sz == 0) sz = np->nmp->msize-IOHDRSZ; p = malloc_9p(sz); if (p == NULL) return ENOMEM; e = 0; while (uio_resid(uio) > 0) { n = MIN(uio_resid(uio), sz); if ((e=read_9p(np->nmp, op->fid, p, n, uio_offset(uio), &l)) || l==0) break; if ((e=uiomove(p, l, uio))) break; } free_9p(p); return e; }
static int rdwr_9p(int type, mount_9p *nmp, fid_9p fid, void *buf, uint32_t count, uint64_t off, uint32_t *countp) { Fcall tx, rx; void *p; int e; count = MIN(count, nmp->msize-IOHDRSZ); *countp = 0; tx.type = type; tx.fid = fid; tx.offset = off; tx.count = count; if (type == Twrite) tx.data = buf; p = NULL; if ((e=rpc_9p(nmp, &tx, &rx, &p))) return e; if (type == Tread) { if (rx.count > count){ DEBUG("rx.count > count: %u > %d", rx.count, count); rx.count = count; } bcopy(rx.data, buf, rx.count); } *countp = rx.count; free_9p(p); return 0; }
static int opencreate_9p(int type, mount_9p *nmp, fid_9p fid, char *name, int nname, uint8_t mode, uint32_t perm, char *ext, qid_9p *qidp, uint32_t *iounit) { Fcall tx, rx; char *s; int e; TRACE(); tx.type = type; tx.fid = fid; tx.mode = mode; s = NULL; if (type == Tcreate) { if((e=strndup(name, nname, &s))) return e; tx.name = s; tx.perm = perm; tx.ext = ext; } e = rpc_9p(nmp, &tx, &rx, NULL); free_9p(s); if (e) return e; *qidp = rx.qid; *iounit = rx.iounit; return 0; }
static int dirpackage(uint8_t *buf, int ts, Dir **d, uint32_t *nd, int dotu) { char *s; int ss, i, n, nn; uint m; *d = nil; *nd = 0; if(ts <= 0) return 0; /* * first find number of all stats, check they look like stats, & size all associated strings */ ss = 0; n = 0; for(i = 0; i < ts; i += m){ m = BIT16SZ + GBIT16(&buf[i]); if(statcheck(&buf[i], m, dotu) < 0) break; ss += m; n++; } if(i != ts) { DEBUG("statcheck"); return EBADRPC; } *d = malloc_9p(n * sizeof(Dir) + ss); if (*d == NULL) return ENOMEM; /* * then convert all buffers */ s = (char*)*d + n * sizeof(Dir); nn = 0; for(i = 0; i < ts; i += m){ m = BIT16SZ + GBIT16(&buf[i]); if(nn >= n || convM2D(&buf[i], m, *d + nn, s, dotu) != m){ free_9p(*d); *d = nil; DEBUG("convM2D"); return EBADRPC; } nn++; s += m; } *nd = nn; return 0; }
static int vnop_reclaim_9p(struct vnop_reclaim_args *ap) { vnode_t vp; node_9p *np; TRACE(); vp = ap->a_vp; np = NTO9P(vp); nlock_9p(np, NODE_LCK_EXCLUSIVE); { SET(np->flags, NODE_RECL); ndel_9p(np); /* balance the ref added in nget_9p() */ vnode_removefsref(vp); vnode_clearfsnode(vp); cache_purge(vp); } nunlock_9p(np); /* root gets clunk in vfs_unmount_9p() */ if (!ISSET(np->nmp->flags, F_UNMOUNTING)) clunk_9p(np->nmp, np->fid); /* free it */ CLR(np->flags, NODE_RECL); if (ISSET(np->flags, NODE_WAITRECL)) { CLR(np->flags, NODE_WAITRECL); wakeup(np); } lck_rw_free(np->lck, lck_grp_9p); free_9p(np->direntries); free_9p(np); return 0; }
static int addrget_9p(user_addr_t name, int len, struct sockaddr **addrp) { struct sockaddr *addr; int e; addr = malloc_9p(len); if (addr == NULL) return ENOMEM; if ((e=copyin(name, addr, len))) { free_9p(addr); return e; } *addrp = addr; return 0; }
static int nameget_9p(user_addr_t in, char **out) { size_t size; char *p; int e; p = malloc_9p(NAME_MAX+1); if (p == NULL) return ENOMEM; if ((e=copyinstr(in, p, NAME_MAX, &size)) || !size) { free_9p(p); return e; } *out = p; return 0; }
static int vnop_rename_9p(struct vnop_rename_args *ap) { struct componentname *tcnp; vnode_t fdvp, tdvp, fvp; node_9p *fdnp, *fnp; dir_9p d; char *s; int e; TRACE(); fdvp = ap->a_fdvp; tdvp = ap->a_tdvp; fvp = ap->a_fvp; tcnp = ap->a_tcnp; fdnp = NTO9P(fdvp); fnp = NTO9P(fvp); if (fdvp!=tdvp || NTO9P(fdvp)!=NTO9P(tdvp)) return ENOTSUP; nlock_9p(fdnp, NODE_LCK_EXCLUSIVE); nlock_9p(fnp, NODE_LCK_EXCLUSIVE); nulldir(&d); e = ENOMEM; s = malloc_9p(tcnp->cn_namelen+1); if (s == NULL) goto error; bcopy(tcnp->cn_nameptr, s, tcnp->cn_namelen); s[tcnp->cn_namelen] = 0; d.name = s; e = wstat_9p(fnp->nmp, fnp->fid, &d); free_9p(s); if (e == 0) { cache_purge(fvp); cache_purge(fdvp); } error: nunlock_9p(fnp); nunlock_9p(fdnp); return e; }
static int ngetdir_9p(node_9p *np) { dir_9p *dp; struct timeval tv; int e; microtime(&tv); if (np->dirtimer && tv.tv_sec-np->dirtimer < DIRTIMEOUT) return 0; if ((e=stat_9p(np->nmp, np->fid, &dp))) return e; bcopy(dp, &np->dir, sizeof(*dp)); np->dir.name = np->dir.uid = np->dir.gid = np->dir.muid = NULL; free_9p(dp); return 0; }
__private_extern__ int version_9p(mount_9p *nmp, char *vers, char **versp) { Fcall tx, rx; void *p; int e; TRACE(); tx.tag = (uint16_t)NOTAG; tx.type = Tversion; tx.version = vers; tx.msize = nmp->msize = sizeof(nmp->rpcbuf); if ((e=rpc_9p(nmp, &tx, &rx, &p))) return e; nmp->msize = MIN(rx.msize, sizeof(nmp->rpcbuf)); e = strndup(rx.version, strlen(rx.version), versp); free_9p(p); return e; }
__private_extern__ int readdir_9p(mount_9p *nmp, fid_9p fid, off_t off, dir_9p **d, uint32_t *nd, uint32_t *nrd) { void *p; int e; TRACE(); *d = NULL; *nd = 0; *nrd = 0; p = malloc_9p(DIRMAX); if (p == NULL) return ENOMEM; e = read_9p(nmp, fid, p, DIRMAX, off, nrd); if (!e) e = dirpackage(p, *nrd, d, nd, ISSET(nmp->flags, F_DOTU)); free_9p(p); return e; }
static int nwrite_9p(node_9p *np, uio_t uio) { openfid_9p *op; user_ssize_t resid; uint32_t l, sz; off_t off; char *p; int n, e; TRACE(); op = &np->openfid[OWRITE]; if (op->fid == NOFID) op = &np->openfid[ORDWR]; if (op->fid == NOFID) return EBADF; sz = np->iounit; if (sz == 0) sz = np->nmp->msize-IOHDRSZ; p = malloc_9p(sz); if (p == NULL) return ENOMEM; e = 0; while (uio_resid(uio) > 0) { l = 0; off = uio_offset(uio); resid = uio_resid(uio); n = MIN(resid, sz); if ((e=uiomove(p, n, uio))) break; if ((e=write_9p(np->nmp, op->fid, p, n, off, &l))) break; uio_setoffset(uio, off+l); uio_setresid(uio, resid-l); } free_9p(p); return e; }
static void freemount_9p(mount_9p *nmp) { if (nmp == NULL) return; free_9p(nmp->version); free_9p(nmp->volume); free_9p(nmp->uname); free_9p(nmp->aname); free_9p(nmp->node); if (nmp->lck) lck_mtx_free(nmp->lck, lck_grp_9p); if (nmp->reqlck) lck_mtx_free(nmp->reqlck, lck_grp_9p); if (nmp->nodelck) lck_mtx_free(nmp->nodelck, lck_grp_9p); free_9p(nmp); }
__private_extern__ int nget_9p(mount_9p *nmp, fid_9p fid, qid_9p qid, vnode_t dvp, vnode_t *vpp, struct componentname *cnp, vfs_context_t ctx) { #pragma unused(ctx) struct vnode_fsparam fsp; struct hnode_9p *nhp; node_9p *np; uint32_t vid; int e, i; TRACE(); nhp = HASH9P(nmp, qid.path); loop: lck_mtx_lock(nmp->nodelck); LIST_FOREACH (np, nhp, next) { if(np->dir.qid.path != qid.path) continue; if (ISSET(np->flags, NODE_INIT)) { SET(np->flags, NODE_WAITINIT); msleep(np, nmp->nodelck, PINOD|PDROP, "nget_9p_init", NULL); goto loop; } if (ISSET(np->flags, NODE_RECL)) { SET(np->flags, NODE_WAITRECL); msleep(np, nmp->nodelck, PINOD|PDROP, "nget_9p_reclaim", NULL); goto loop; } vid = vnode_vid(np->vp); lck_mtx_unlock(nmp->nodelck); if (vnode_getwithvid(np->vp, vid)) goto loop; nlock_9p(np, NODE_LCK_EXCLUSIVE); if (dvp && cnp && ISSET(cnp->cn_flags, MAKEENTRY) && np->dir.qid.vers!=0) { // DEBUG("caching %s", np->dir->name); cache_enter(dvp, np->vp, cnp); } else { // DEBUG("not in cache qid=%d %s", qid.vers, np->dir->name); } *vpp = np->vp; return 0; } if (fid == NOFID) return EFAULT; np = malloc_9p(sizeof(*np)); if (np == NULL) { err0: lck_mtx_unlock(nmp->nodelck); return ENOMEM; } np->lck = lck_rw_alloc_init(lck_grp_9p, LCK_ATTR_NULL); if (np->lck == NULL) { free_9p(np); goto err0; } np->nmp = nmp; np->fid = fid; np->dir.qid = qid; for (i=0; i<3; i++) np->openfid[i].fid = NOFID; SET(np->flags, NODE_INIT); LIST_INSERT_HEAD(nhp, np, next); nlock_9p(np, NODE_LCK_EXCLUSIVE); lck_mtx_unlock(nmp->nodelck); if ((e=ngetdir_9p(np))) { err1: nunlock_9p(np); lck_mtx_lock(nmp->nodelck); LIST_REMOVE(np, next); CLR(np->flags, NODE_INIT); if (ISSET(np->flags, NODE_WAITINIT)) { CLR(np->flags, NODE_WAITINIT); wakeup(np); } lck_mtx_unlock(nmp->nodelck); lck_rw_free(np->lck, lck_grp_9p); free_9p(np); return e; } fsp.vnfs_mp = nmp->mp; fsp.vnfs_str = fsname; fsp.vnfs_dvp = dvp; fsp.vnfs_fsnode = np; fsp.vnfs_vops = vnode_op_9p; fsp.vnfs_markroot = dvp==NULL? TRUE: FALSE; fsp.vnfs_marksystem = FALSE; fsp.vnfs_filesize = np->dir.length; fsp.vnfs_cnp = cnp; fsp.vnfs_flags = VNFS_ADDFSREF; dirvtype_9p(&np->dir, ISSET(nmp->flags, F_DOTU), &fsp.vnfs_vtype, &fsp.vnfs_rdev); if (!dvp || !cnp || !ISSET(cnp->cn_flags, MAKEENTRY) || qid.vers==0) SET(fsp.vnfs_flags, VNFS_NOCACHE); if ((e=vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &fsp, &np->vp))) goto err1; vnode_settag(np->vp, VT_OTHER); lck_mtx_lock(nmp->nodelck); CLR(np->flags, NODE_INIT); if (ISSET(np->flags, NODE_WAITINIT)) { CLR(np->flags, NODE_WAITINIT); wakeup(np); } lck_mtx_unlock(nmp->nodelck); *vpp = np->vp; return 0; }
static int vnop_readdir_9p(struct vnop_readdir_args *ap) { struct direntry de64; struct dirent de32; vnode_t vp; node_9p *np; dir_9p *dp; fid_9p fid; off_t off; uio_t uio; uint32_t i, nd, nlen, plen; void *p; int e; TRACE(); vp = ap->a_vp; uio = ap->a_uio; np = NTO9P(vp); if (!vnode_isdir(vp)) return ENOTDIR; if (ISSET(ap->a_flags, VNODE_READDIR_REQSEEKOFF)) return EINVAL; off = uio_offset(uio); if (off < 0) return EINVAL; if (uio_resid(uio) == 0) return 0; e = 0; nlock_9p(np, NODE_LCK_EXCLUSIVE); fid = np->openfid[OREAD].fid; if (fid == NOFID) { e = EBADF; goto error; } if (ap->a_eofflag) ap->a_eofflag = 0; if (off == 0 || np->direntries==NULL) { if((e=readdirs_9p(np->nmp, fid, &np->direntries, &np->ndirentries))) goto error; if (np->ndirentries && np->direntries==NULL) panic("bug in readdir"); } dp = np->direntries; nd = np->ndirentries; for (i=off; i<nd; i++) { if (ISSET(ap->a_flags, VNODE_READDIR_EXTENDED)) { bzero(&de64, sizeof(de64)); de64.d_ino = QTOI(dp[i].qid); de64.d_type = dp[i].mode&DMDIR? DT_DIR: DT_REG; nlen = strlen(dp[i].name); de64.d_namlen = MIN(nlen, sizeof(de64.d_name)-1); bcopy(dp[i].name, de64.d_name, de64.d_namlen); de64.d_reclen = DIRENT64_LEN(de64.d_namlen); plen = de64.d_reclen; p = &de64; } else { bzero(&de32, sizeof(de32)); de32.d_ino = QTOI(dp[i].qid); de32.d_type = dp[i].mode&DMDIR? DT_DIR: DT_REG; nlen = strlen(dp[i].name); de32.d_namlen = MIN(nlen, sizeof(de32.d_name)-1); bcopy(dp[i].name, de32.d_name, de32.d_namlen); de32.d_reclen = DIRENT32_LEN(de32.d_namlen); plen = de32.d_reclen; p = &de32; } if (uio_resid(uio) < plen) break; if ((e=uiomove(p, plen, uio))) goto error; } uio_setoffset(uio, i); if (ap->a_numdirent) *ap->a_numdirent = i - off; if (i==nd && ap->a_eofflag) { *ap->a_eofflag = 1; free_9p(np->direntries); np->direntries = NULL; np->ndirentries = 0; } error: nunlock_9p(np); return e; }
static int vfs_mount_9p(mount_t mp, vnode_t devvp, user_addr_t data, vfs_context_t ctx) { #pragma unused(devvp) struct sockaddr *addr, *authaddr; struct vfsstatfs *sp; char authkey[DESKEYLEN+1]; kauth_cred_t cred; user_args_9p args; mount_9p *nmp; size_t size; fid_9p fid; qid_9p qid; char *vers; int e; TRACE(); nmp = NULL; addr = NULL; authaddr = NULL; fid = NOFID; if (vfs_isupdate(mp)) return ENOTSUP; if (vfs_context_is64bit(ctx)) { if ((e=copyin(data, &args, sizeof(args)))) goto error; } else { args_9p args32; if ((e=copyin(data, &args32, sizeof(args32)))) goto error; args.spec = CAST_USER_ADDR_T(args32.spec); args.addr = CAST_USER_ADDR_T(args32.addr); args.addrlen = args32.addrlen; args.authaddr = CAST_USER_ADDR_T(args32.authaddr); args.authaddrlen = args32.authaddrlen; args.volume = CAST_USER_ADDR_T(args32.volume); args.uname = CAST_USER_ADDR_T(args32.uname); args.aname = CAST_USER_ADDR_T(args32.aname); args.authkey = CAST_USER_ADDR_T(args32.authkey); args.flags = args32.flags; } e = ENOMEM; nmp = malloc_9p(sizeof(*nmp)); if (nmp == NULL) return e; nmp->mp = mp; TAILQ_INIT(&nmp->req); nmp->lck = lck_mtx_alloc_init(lck_grp_9p, LCK_ATTR_NULL); nmp->reqlck = lck_mtx_alloc_init(lck_grp_9p, LCK_ATTR_NULL); nmp->nodelck = lck_mtx_alloc_init(lck_grp_9p, LCK_ATTR_NULL); nmp->node = hashinit(desiredvnodes, M_TEMP, &nmp->nodelen); if (nmp->lck==NULL || nmp->reqlck==NULL || nmp->nodelck==NULL || nmp->node==NULL) goto error; if ((e=nameget_9p(args.volume, &nmp->volume))) goto error; if ((e=nameget_9p(args.uname, &nmp->uname))) goto error; if ((e=nameget_9p(args.aname, &nmp->aname))) goto error; cred = vfs_context_ucred(ctx); if (IS_VALID_CRED(cred)) { nmp->uid = kauth_cred_getuid(cred); nmp->gid = kauth_cred_getgid(cred); } else { nmp->uid = KAUTH_UID_NONE; nmp->gid = KAUTH_GID_NONE; } vfs_getnewfsid(mp); vfs_setfsprivate(mp, nmp); nmp->flags = args.flags; if ((e=addrget_9p(args.addr, args.addrlen, &addr))) goto error; if ((e=connect_9p(nmp, addr))) goto error; vers = VERSION9P; if (ISSET(nmp->flags, FLAG_DOTU)) vers = VERSION9PDOTU; if ((e=version_9p(nmp, vers, &nmp->version))) goto error; if (ISSET(nmp->flags, FLAG_DOTU) && strcmp(VERSION9PDOTU, nmp->version)==0) SET(nmp->flags, F_DOTU); nmp->afid = NOFID; if (args.authaddr && args.authaddrlen && args.authkey) { if ((e=copyin(args.authkey, authkey, DESKEYLEN))) goto error; if ((e=addrget_9p(args.authaddr, args.authaddrlen, &authaddr))) goto error; if ((e=auth_9p(nmp, nmp->uname, nmp->aname, nmp->uid, &nmp->afid, &qid))) goto error; if (nmp->afid!=NOFID && (e=authp9any_9p(nmp, nmp->afid, authaddr, nmp->uname, authkey))) goto error; bzero(authkey, DESKEYLEN); } if ((e=attach_9p(nmp, nmp->uname, nmp->aname, nmp->afid, nmp->uid, &fid, &qid))) goto error; if ((e=nget_9p(nmp, fid, qid, NULL, &nmp->root, NULL, ctx))) goto error; nunlock_9p(NTO9P(nmp->root)); e = vnode_ref(nmp->root); vnode_put(nmp->root); if (e) goto error; vfs_setauthopaque(mp); vfs_clearauthopaqueaccess(mp); vfs_setlocklocal(mp); // init stats sp = vfs_statfs(nmp->mp); copyinstr(args.spec, sp->f_mntfromname, MNAMELEN-1, &size); bzero(sp->f_mntfromname+size, MNAMELEN-size); sp->f_bsize = PAGE_SIZE; sp->f_iosize = nmp->msize-IOHDRSZ; sp->f_blocks = sp->f_bfree = sp->f_bavail = sp->f_bused = 0; sp->f_files = 65535; sp->f_ffree = sp->f_files-2; sp->f_flags = vfs_flags(mp); free_9p(addr); free_9p(authaddr); return 0; error: bzero(authkey, DESKEYLEN); free_9p(addr); free_9p(authaddr); if (nmp->so) { clunk_9p(nmp, fid); disconnect_9p(nmp); } freemount_9p(nmp); vfs_setfsprivate(mp, NULL); return e; }