Beispiel #1
0
__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;
}
Beispiel #2
0
__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;
}
Beispiel #3
0
__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;
}
Beispiel #4
0
__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;
}
Beispiel #5
0
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;
}
Beispiel #6
0
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;
}
Beispiel #7
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;
}
Beispiel #8
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;
}
Beispiel #9
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;
}
Beispiel #10
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;
}
Beispiel #11
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;
}
Beispiel #12
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;
}
Beispiel #13
0
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;
}
Beispiel #14
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;
}
Beispiel #15
0
__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;
}
Beispiel #16
0
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;
}
Beispiel #17
0
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);
}
Beispiel #18
0
__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;
}
Beispiel #19
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;
}
Beispiel #20
0
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;
}