version 1.13.2.3, 2007/02/26 09:10:57 |
version 1.13.2.4, 2007/09/03 14:40:30 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
* 2. Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* documentation and/or other materials provided with the distribution. |
* 3. The name of the company nor the name of the author may be used to |
|
* endorse or promote products derived from this software without specific |
|
* prior written permission. |
|
* |
* |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS |
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
Line 38 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 35 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/conf.h> |
#include <sys/conf.h> |
#include <sys/hash.h> |
#include <sys/hash.h> |
|
#include <sys/kauth.h> |
#include <sys/malloc.h> |
#include <sys/malloc.h> |
#include <sys/mount.h> |
#include <sys/mount.h> |
|
#include <sys/namei.h> |
|
#include <sys/poll.h> |
#include <sys/socketvar.h> |
#include <sys/socketvar.h> |
#include <sys/vnode.h> |
#include <sys/vnode.h> |
#include <sys/kauth.h> |
#include <sys/proc.h> |
#include <sys/namei.h> |
|
|
|
#include <fs/puffs/puffs_msgif.h> |
#include <fs/puffs/puffs_msgif.h> |
#include <fs/puffs/puffs_sys.h> |
#include <fs/puffs/puffs_sys.h> |
Line 51 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 50 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <miscfs/genfs/genfs_node.h> |
#include <miscfs/genfs/genfs_node.h> |
#include <miscfs/specfs/specdev.h> |
#include <miscfs/specfs/specdev.h> |
|
|
POOL_INIT(puffs_pnpool, sizeof(struct puffs_node), 0, 0, 0, "puffspnpl", |
struct pool puffs_pnpool; |
&pool_allocator_nointr); |
|
|
|
#ifdef PUFFSDEBUG |
#ifdef PUFFSDEBUG |
int puffsdebug; |
int puffsdebug; |
Line 87 puffs_getvnode(struct mount *mp, void *c |
|
Line 85 puffs_getvnode(struct mount *mp, void *c |
|
struct puffs_node_hashlist *plist; |
struct puffs_node_hashlist *plist; |
int error; |
int error; |
|
|
|
if (type <= VNON || type >= VBAD) |
|
return EINVAL; |
|
|
pmp = MPTOPUFFSMP(mp); |
pmp = MPTOPUFFSMP(mp); |
|
|
/* |
/* |
Line 134 puffs_getvnode(struct mount *mp, void *c |
|
Line 135 puffs_getvnode(struct mount *mp, void *c |
|
* clerical tasks & footwork |
* clerical tasks & footwork |
*/ |
*/ |
|
|
|
/* default size */ |
|
uvm_vnp_setsize(vp, 0); |
|
|
/* dances based on vnode type. almost ufs_vinit(), but not quite */ |
/* dances based on vnode type. almost ufs_vinit(), but not quite */ |
switch (type) { |
switch (type) { |
case VCHR: |
case VCHR: |
Line 183 puffs_getvnode(struct mount *mp, void *c |
|
Line 187 puffs_getvnode(struct mount *mp, void *c |
|
} |
} |
|
|
pnode = pool_get(&puffs_pnpool, PR_WAITOK); |
pnode = pool_get(&puffs_pnpool, PR_WAITOK); |
|
memset(pnode, 0, sizeof(struct puffs_node)); |
|
|
pnode->pn_cookie = cookie; |
pnode->pn_cookie = cookie; |
pnode->pn_stat = 0; |
pnode->pn_refcount = 1; |
|
|
|
mutex_init(&pnode->pn_mtx, MUTEX_DEFAULT, IPL_NONE); |
|
SLIST_INIT(&pnode->pn_sel.sel_klist); |
|
|
plist = puffs_cookie2hashlist(pmp, cookie); |
plist = puffs_cookie2hashlist(pmp, cookie); |
LIST_INSERT_HEAD(plist, pnode, pn_hashent); |
LIST_INSERT_HEAD(plist, pnode, pn_hashent); |
vp->v_data = pnode; |
vp->v_data = pnode; |
vp->v_type = type; |
vp->v_type = type; |
pnode->pn_vp = vp; |
pnode->pn_vp = vp; |
|
pnode->pn_serversize = vsize; |
|
|
genfs_node_init(vp, &puffs_genfsops); |
genfs_node_init(vp, &puffs_genfsops); |
*vpp = vp; |
*vpp = vp; |
Line 223 puffs_newnode(struct mount *mp, struct v |
|
Line 234 puffs_newnode(struct mount *mp, struct v |
|
* XXX: technically this error check should punish the fs, |
* XXX: technically this error check should punish the fs, |
* not the caller. |
* not the caller. |
*/ |
*/ |
simple_lock(&pmp->pmp_lock); |
mutex_enter(&pmp->pmp_lock); |
if (cookie == pmp->pmp_rootcookie |
if (cookie == pmp->pmp_root_cookie |
|| puffs_cookie2pnode(pmp, cookie) != NULL) { |
|| puffs_cookie2pnode(pmp, cookie) != NULL) { |
simple_unlock(&pmp->pmp_lock); |
mutex_exit(&pmp->pmp_lock); |
error = EEXIST; |
error = EEXIST; |
return error; |
return error; |
} |
} |
simple_unlock(&pmp->pmp_lock); |
mutex_exit(&pmp->pmp_lock); |
|
|
error = puffs_getvnode(dvp->v_mount, cookie, type, 0, rdev, &vp); |
error = puffs_getvnode(dvp->v_mount, cookie, type, 0, rdev, &vp); |
if (error) |
if (error) |
Line 240 puffs_newnode(struct mount *mp, struct v |
|
Line 251 puffs_newnode(struct mount *mp, struct v |
|
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
*vpp = vp; |
*vpp = vp; |
|
|
if ((cnp->cn_flags & MAKEENTRY) && PUFFS_DOCACHE(pmp)) |
if ((cnp->cn_flags & MAKEENTRY) && PUFFS_USE_NAMECACHE(pmp)) |
cache_enter(dvp, vp, cnp); |
cache_enter(dvp, vp, cnp); |
|
|
return 0; |
return 0; |
} |
} |
|
|
|
/* |
|
* Release pnode structure which dealing with references to the |
|
* puffs_node instead of the vnode. Can't use vref()/vrele() on |
|
* the vnode there, since that causes the lovely VOP_INACTIVE(), |
|
* which in turn causes the lovely deadlock when called by the one |
|
* who is supposed to handle it. |
|
*/ |
|
void |
|
puffs_releasenode(struct puffs_node *pn) |
|
{ |
|
|
|
mutex_enter(&pn->pn_mtx); |
|
if (--pn->pn_refcount == 0) { |
|
mutex_exit(&pn->pn_mtx); |
|
mutex_destroy(&pn->pn_mtx); |
|
pool_put(&puffs_pnpool, pn); |
|
} else { |
|
mutex_exit(&pn->pn_mtx); |
|
} |
|
} |
|
|
|
/* |
|
* Add reference to node. |
|
* mutex held on entry and return |
|
*/ |
|
void |
|
puffs_referencenode(struct puffs_node *pn) |
|
{ |
|
|
|
KASSERT(mutex_owned(&pn->pn_mtx)); |
|
pn->pn_refcount++; |
|
} |
|
|
void |
void |
puffs_putvnode(struct vnode *vp) |
puffs_putvnode(struct vnode *vp) |
{ |
{ |
Line 262 puffs_putvnode(struct vnode *vp) |
|
Line 306 puffs_putvnode(struct vnode *vp) |
|
|
|
LIST_REMOVE(pnode, pn_hashent); |
LIST_REMOVE(pnode, pn_hashent); |
genfs_node_destroy(vp); |
genfs_node_destroy(vp); |
pool_put(&puffs_pnpool, vp->v_data); |
puffs_releasenode(pnode); |
vp->v_data = NULL; |
vp->v_data = NULL; |
|
|
return; |
return; |
Line 297 puffs_cookie2pnode(struct puffs_mount *p |
|
Line 341 puffs_cookie2pnode(struct puffs_mount *p |
|
} |
} |
|
|
/* |
/* |
|
* Make sure root vnode exists and reference it. Does NOT lock. |
|
*/ |
|
static int |
|
puffs_makeroot(struct puffs_mount *pmp) |
|
{ |
|
struct vnode *vp; |
|
int rv; |
|
|
|
/* |
|
* pmp_lock must be held if vref()'ing or vrele()'ing the |
|
* root vnode. the latter is controlled by puffs_inactive(). |
|
* |
|
* pmp_root is set here and cleared in puffs_reclaim(). |
|
*/ |
|
retry: |
|
mutex_enter(&pmp->pmp_lock); |
|
vp = pmp->pmp_root; |
|
if (vp) { |
|
simple_lock(&vp->v_interlock); |
|
mutex_exit(&pmp->pmp_lock); |
|
if (vget(vp, LK_INTERLOCK) == 0) |
|
return 0; |
|
} else |
|
mutex_exit(&pmp->pmp_lock); |
|
|
|
/* |
|
* So, didn't have the magic root vnode available. |
|
* No matter, grab another an stuff it with the cookie. |
|
*/ |
|
if ((rv = puffs_getvnode(pmp->pmp_mp, pmp->pmp_root_cookie, |
|
pmp->pmp_root_vtype, pmp->pmp_root_vsize, pmp->pmp_root_rdev, &vp))) |
|
return rv; |
|
|
|
/* |
|
* Someone magically managed to race us into puffs_getvnode? |
|
* Put our previous new vnode back and retry. |
|
*/ |
|
mutex_enter(&pmp->pmp_lock); |
|
if (pmp->pmp_root) { |
|
mutex_exit(&pmp->pmp_lock); |
|
puffs_putvnode(vp); |
|
goto retry; |
|
} |
|
|
|
/* store cache */ |
|
vp->v_flag = VROOT; |
|
pmp->pmp_root = vp; |
|
mutex_exit(&pmp->pmp_lock); |
|
|
|
return 0; |
|
} |
|
|
|
/* |
* Locate the in-kernel vnode based on the cookie received given |
* Locate the in-kernel vnode based on the cookie received given |
* from userspace. Returns a vnode, if found, NULL otherwise. |
* from userspace. Returns a vnode, if found, NULL otherwise. |
* The parameter "lock" control whether to lock the possible or |
* The parameter "lock" control whether to lock the possible or |
Line 304 puffs_cookie2pnode(struct puffs_mount *p |
|
Line 401 puffs_cookie2pnode(struct puffs_mount *p |
|
* in situations where we want the vnode but don't care for the |
* in situations where we want the vnode but don't care for the |
* vnode lock, e.g. file server issued putpages. |
* vnode lock, e.g. file server issued putpages. |
*/ |
*/ |
struct vnode * |
int |
puffs_pnode2vnode(struct puffs_mount *pmp, void *cookie, int lock) |
puffs_pnode2vnode(struct puffs_mount *pmp, void *cookie, int lock, |
|
struct vnode **vpp) |
{ |
{ |
struct puffs_node *pnode; |
struct puffs_node *pnode; |
struct vnode *vp; |
struct vnode *vp; |
int vgetflags; |
int vgetflags, rv; |
|
|
/* |
/* |
* If we're trying to get the root vnode, return it through |
* Handle root in a special manner, since we want to make sure |
* puffs_root() to get all the right things set. Lock must |
* pmp_root is properly set. |
* be set, since VFS_ROOT() always locks the returned vnode. |
|
*/ |
*/ |
if (cookie == pmp->pmp_rootcookie) { |
if (cookie == pmp->pmp_root_cookie) { |
if (!lock) |
if ((rv = puffs_makeroot(pmp))) |
return NULL; |
return rv; |
if (VFS_ROOT(pmp->pmp_mp, &vp)) |
if (lock) |
return NULL; |
vn_lock(pmp->pmp_root, LK_EXCLUSIVE | LK_RETRY); |
|
|
return vp; |
*vpp = pmp->pmp_root; |
|
return 0; |
} |
} |
|
|
vgetflags = LK_INTERLOCK; |
mutex_enter(&pmp->pmp_lock); |
if (lock) |
|
vgetflags |= LK_EXCLUSIVE | LK_RETRY; |
|
|
|
simple_lock(&pmp->pmp_lock); |
|
pnode = puffs_cookie2pnode(pmp, cookie); |
pnode = puffs_cookie2pnode(pmp, cookie); |
|
|
if (pnode == NULL) { |
if (pnode == NULL) { |
simple_unlock(&pmp->pmp_lock); |
mutex_exit(&pmp->pmp_lock); |
return NULL; |
return ENOENT; |
} |
} |
vp = pnode->pn_vp; |
|
|
|
|
vp = pnode->pn_vp; |
simple_lock(&vp->v_interlock); |
simple_lock(&vp->v_interlock); |
simple_unlock(&pmp->pmp_lock); |
mutex_exit(&pmp->pmp_lock); |
|
|
if (vget(vp, vgetflags)) |
vgetflags = LK_INTERLOCK; |
return NULL; |
if (lock) |
|
vgetflags |= LK_EXCLUSIVE | LK_RETRY; |
|
if ((rv = vget(vp, vgetflags))) |
|
return rv; |
|
|
return vp; |
*vpp = vp; |
|
return 0; |
} |
} |
|
|
void |
void |
puffs_makecn(struct puffs_kcn *pkcn, const struct componentname *cn) |
puffs_makecn(struct puffs_kcn *pkcn, struct puffs_kcred *pkcr, |
|
struct puffs_kcid *pkcid, const struct componentname *cn, int full) |
{ |
{ |
|
|
pkcn->pkcn_nameiop = cn->cn_nameiop; |
pkcn->pkcn_nameiop = cn->cn_nameiop; |
pkcn->pkcn_flags = cn->cn_flags; |
pkcn->pkcn_flags = cn->cn_flags; |
pkcn->pkcn_pid = cn->cn_lwp->l_proc->p_pid; |
puffs_cidcvt(pkcid, cn->cn_lwp); |
puffs_credcvt(&pkcn->pkcn_cred, cn->cn_cred); |
|
|
|
(void)memcpy(&pkcn->pkcn_name, cn->cn_nameptr, cn->cn_namelen); |
if (full) { |
pkcn->pkcn_name[cn->cn_namelen] = '\0'; |
(void)strcpy(pkcn->pkcn_name, cn->cn_nameptr); |
|
} else { |
|
(void)memcpy(pkcn->pkcn_name, cn->cn_nameptr, cn->cn_namelen); |
|
pkcn->pkcn_name[cn->cn_namelen] = '\0'; |
|
} |
pkcn->pkcn_namelen = cn->cn_namelen; |
pkcn->pkcn_namelen = cn->cn_namelen; |
|
pkcn->pkcn_consume = 0; |
|
|
|
puffs_credcvt(pkcr, cn->cn_cred); |
} |
} |
|
|
/* |
/* |
* Convert given credentials to struct puffs_cred for userspace. |
* Convert given credentials to struct puffs_kcred for userspace. |
*/ |
*/ |
void |
void |
puffs_credcvt(struct puffs_cred *pcr, const kauth_cred_t cred) |
puffs_credcvt(struct puffs_kcred *pkcr, const kauth_cred_t cred) |
{ |
{ |
|
|
memset(pcr, 0, sizeof(struct puffs_cred)); |
memset(pkcr, 0, sizeof(struct puffs_kcred)); |
|
|
if (cred == NOCRED || cred == FSCRED) { |
if (cred == NOCRED || cred == FSCRED) { |
pcr->pcr_type = PUFFCRED_TYPE_INTERNAL; |
pkcr->pkcr_type = PUFFCRED_TYPE_INTERNAL; |
if (cred == NOCRED) |
if (cred == NOCRED) |
pcr->pcr_internal = PUFFCRED_CRED_NOCRED; |
pkcr->pkcr_internal = PUFFCRED_CRED_NOCRED; |
if (cred == FSCRED) |
if (cred == FSCRED) |
pcr->pcr_internal = PUFFCRED_CRED_FSCRED; |
pkcr->pkcr_internal = PUFFCRED_CRED_FSCRED; |
} else { |
} else { |
pcr->pcr_type = PUFFCRED_TYPE_UUC; |
pkcr->pkcr_type = PUFFCRED_TYPE_UUC; |
kauth_cred_to_uucred(&pcr->pcr_uuc, cred); |
kauth_cred_to_uucred(&pkcr->pkcr_uuc, cred); |
} |
} |
} |
} |
|
|
/* |
void |
* Return pid. In case the operation is coming from within the |
puffs_cidcvt(struct puffs_kcid *pkcid, const struct lwp *l) |
* kernel without any process context, borrow the swapper's pid. |
|
*/ |
|
pid_t |
|
puffs_lwp2pid(struct lwp *l) |
|
{ |
{ |
|
|
return l ? l->l_proc->p_pid : 0; |
if (l) { |
|
pkcid->pkcid_type = PUFFCID_TYPE_REAL; |
|
pkcid->pkcid_pid = l->l_proc->p_pid; |
|
pkcid->pkcid_lwpid = l->l_lid; |
|
} else { |
|
pkcid->pkcid_type = PUFFCID_TYPE_FAKE; |
|
pkcid->pkcid_pid = 0; |
|
pkcid->pkcid_lwpid = 0; |
|
} |
} |
} |
|
|
|
|
static void |
static void |
puffs_gop_size(struct vnode *vp, off_t size, off_t *eobp, |
puffs_gop_size(struct vnode *vp, off_t size, off_t *eobp, |
int flags) |
int flags) |
Line 418 puffs_gop_markupdate(struct vnode *vp, i |
|
Line 526 puffs_gop_markupdate(struct vnode *vp, i |
|
void |
void |
puffs_updatenode(struct vnode *vp, int flags) |
puffs_updatenode(struct vnode *vp, int flags) |
{ |
{ |
|
struct puffs_node *pn; |
struct timespec ts; |
struct timespec ts; |
struct puffs_vnreq_setattr *setattr_arg; |
|
|
|
if (flags == 0) |
if (flags == 0) |
return; |
return; |
|
|
setattr_arg = malloc(sizeof(struct puffs_vnreq_setattr), M_PUFFS, |
pn = VPTOPP(vp); |
M_NOWAIT | M_ZERO); |
|
if (setattr_arg == NULL) |
|
return; /* 2bad */ |
|
|
|
nanotime(&ts); |
nanotime(&ts); |
|
|
VATTR_NULL(&setattr_arg->pvnr_va); |
if (flags & PUFFS_UPDATEATIME) { |
if (flags & PUFFS_UPDATEATIME) |
pn->pn_mc_atime = ts; |
setattr_arg->pvnr_va.va_atime = ts; |
pn->pn_stat |= PNODE_METACACHE_ATIME; |
if (flags & PUFFS_UPDATECTIME) |
} |
setattr_arg->pvnr_va.va_ctime = ts; |
if (flags & PUFFS_UPDATECTIME) { |
if (flags & PUFFS_UPDATEMTIME) |
pn->pn_mc_ctime = ts; |
setattr_arg->pvnr_va.va_mtime = ts; |
pn->pn_stat |= PNODE_METACACHE_CTIME; |
if (flags & PUFFS_UPDATESIZE) |
} |
setattr_arg->pvnr_va.va_size = vp->v_size; |
if (flags & PUFFS_UPDATEMTIME) { |
|
pn->pn_mc_mtime = ts; |
setattr_arg->pvnr_pid = 0; |
pn->pn_stat |= PNODE_METACACHE_MTIME; |
puffs_credcvt(&setattr_arg->pvnr_cred, NOCRED); |
} |
|
if (flags & PUFFS_UPDATESIZE) { |
/* setattr_arg ownership shifted to callee */ |
pn->pn_mc_size = vp->v_size; |
puffs_vntouser_faf(MPTOPUFFSMP(vp->v_mount), PUFFS_VN_SETATTR, |
pn->pn_stat |= PNODE_METACACHE_SIZE; |
setattr_arg, sizeof(struct puffs_vnreq_setattr), VPTOPNC(vp)); |
} |
} |
} |
|
|
void |
void |
Line 461 puffs_updatevpsize(struct vnode *vp) |
|
Line 565 puffs_updatevpsize(struct vnode *vp) |
|
vp->v_size = va.va_size; |
vp->v_size = va.va_size; |
} |
} |
|
|
/* |
|
* We're dead, kaput, RIP, slightly more than merely pining for the |
|
* fjords, belly-up, fallen, lifeless, finished, expired, gone to meet |
|
* our maker, ceased to be, etcetc. YASD. It's a dead FS! |
|
* |
|
* Caller must hold puffs spinlock. |
|
*/ |
|
void |
void |
puffs_userdead(struct puffs_mount *pmp) |
puffs_parkdone_asyncbioread(struct puffs_req *preq, void *arg) |
{ |
{ |
struct puffs_park *park; |
struct puffs_vnreq_read *read_argp = (void *)preq; |
|
struct buf *bp = arg; |
|
size_t moved; |
|
|
|
bp->b_error = preq->preq_rv; |
|
if (bp->b_error == 0) { |
|
moved = bp->b_bcount - read_argp->pvnr_resid; |
|
bp->b_resid = read_argp->pvnr_resid; |
|
|
/* |
memcpy(bp->b_data, read_argp->pvnr_data, moved); |
* Mark filesystem status as dying so that operations don't |
|
* attempt to march to userspace any longer. |
|
*/ |
|
pmp->pmp_status = PUFFSTAT_DYING; |
|
|
|
/* and wakeup processes waiting for a reply from userspace */ |
|
TAILQ_FOREACH(park, &pmp->pmp_req_replywait, park_entries) { |
|
park->park_preq->preq_rv = ENXIO; |
|
TAILQ_REMOVE(&pmp->pmp_req_replywait, park, park_entries); |
|
wakeup(park); |
|
} |
} |
|
|
/* wakeup waiters for completion of vfs/vnode requests */ |
biodone(bp); |
TAILQ_FOREACH(park, &pmp->pmp_req_touser, park_entries) { |
free(preq, M_PUFFS); |
park->park_preq->preq_rv = ENXIO; |
} |
TAILQ_REMOVE(&pmp->pmp_req_touser, park, park_entries); |
|
wakeup(park); |
/* XXX: userspace can leak kernel resources */ |
} |
void |
|
puffs_parkdone_poll(struct puffs_req *preq, void *arg) |
|
{ |
|
struct puffs_vnreq_poll *poll_argp = (void *)preq; |
|
struct puffs_node *pn = arg; |
|
int revents; |
|
|
|
if (preq->preq_rv == 0) |
|
revents = poll_argp->pvnr_events; |
|
else |
|
revents = POLLERR; |
|
|
|
mutex_enter(&pn->pn_mtx); |
|
pn->pn_revents |= revents; |
|
mutex_exit(&pn->pn_mtx); |
|
|
|
selnotify(&pn->pn_sel, 0); |
|
free(preq, M_PUFFS); |
|
|
|
puffs_releasenode(pn); |
|
} |
|
|
|
void |
|
puffs_mp_reference(struct puffs_mount *pmp) |
|
{ |
|
|
|
KASSERT(mutex_owned(&pmp->pmp_lock)); |
|
pmp->pmp_refcount++; |
|
} |
|
|
|
void |
|
puffs_mp_release(struct puffs_mount *pmp) |
|
{ |
|
|
|
KASSERT(mutex_owned(&pmp->pmp_lock)); |
|
if (--pmp->pmp_refcount == 0) |
|
cv_broadcast(&pmp->pmp_refcount_cv); |
} |
} |