diff -r e1d135a8f666 sys/vfs/nullfs/null.h --- a/sys/vfs/nullfs/null.h Sun Mar 26 07:56:54 2006 +0000 +++ b/sys/vfs/nullfs/null.h Wed Mar 29 10:29:08 2006 +0200 @@ -44,17 +44,26 @@ struct null_args { }; struct null_mount { - struct mount *nullm_vfs; - struct vnode *nullm_rootvp; /* Reference to root null_node */ + struct namecache *nullm_ncp; }; #ifdef _KERNEL + #define MOUNTTONULLMOUNT(mp) ((struct null_mount *)((mp)->mnt_data)) - #ifdef NULLFS_DEBUG -#define NULLFSDEBUG(format, args...) printf(format ,## args) +#define NULLFSDEBUG(format, args...) \ + printf(" [nullfs] %s:%d: " format, __func__, __LINE__, ## args) +#define NULLNCDEBUG(ncp) \ + NULLFSDEBUG(#ncp " %p: name %s, refs %d, exlocks %d, nc_flag 0x%x, " \ + "nc_mount %p, nc_shadowed %p, nc_shadowinfo %p, " \ + "nc_shadowheight %d, nc_vp %p\n", \ + (ncp), (ncp)->nc_name, (ncp)->nc_refs, \ + (ncp)->nc_shadowinfo->sh_exlocks, (ncp)->nc_flag, \ + (ncp)->nc_mount, (ncp)->nc_shadowed, \ + (ncp)->nc_shadowinfo, (ncp)->nc_shadowheight, (ncp)->nc_vp) #else #define NULLFSDEBUG(format, args...) +#define NULLNCDEBUG(ncp) #endif /* NULLFS_DEBUG */ #endif /* _KERNEL */ diff -r e1d135a8f666 sys/vfs/nullfs/null_vfsops.c --- a/sys/vfs/nullfs/null_vfsops.c Sun Mar 26 07:56:54 2006 +0000 +++ b/sys/vfs/nullfs/null_vfsops.c Wed Mar 29 10:29:08 2006 +0200 @@ -53,6 +53,7 @@ #include #include #include +#include #include "null.h" extern struct vnodeopv_entry_desc null_vnodeop_entries[]; @@ -80,12 +81,10 @@ nullfs_mount(struct mount *mp, char *pat { int error = 0; struct null_args args; - struct vnode *rootvp; struct null_mount *xmp; u_int size; - struct nlookupdata nd; - - NULLFSDEBUG("nullfs_mount(mp = %p)\n", (void *)mp); + + NULLFSDEBUG("mp %p\n", (void *)mp); /* * Update is a no-op @@ -98,118 +97,79 @@ nullfs_mount(struct mount *mp, char *pat * Get argument */ error = copyin(data, (caddr_t)&args, sizeof(struct null_args)); - if (error) + + xmp = malloc(sizeof(*xmp), M_NULLFSMNT, M_WAITOK | M_ZERO); + + NULLFSDEBUG("nlookup %s\n", args.target); + + xmp->nullm_ncp = nlookup_simple(args.target, + UIO_SYSSPACE, NLC_FOLLOW, &error); + + if (! xmp->nullm_ncp) { + free(xmp, M_NULLFSMNT); return (error); - - /* - * Find lower node - */ - rootvp = NULL; - error = nlookup_init(&nd, args.target, UIO_USERSPACE, NLC_FOLLOW); - if (error == 0) - error = nlookup(&nd); - if (error == 0) { - error = cache_vget(nd.nl_ncp, nd.nl_cred, LK_EXCLUSIVE, - &rootvp); - } - - xmp = (struct null_mount *) malloc(sizeof(struct null_mount), - M_NULLFSMNT, M_WAITOK); /* XXX */ - - /* - * Save reference to underlying FS - */ - /* - * As lite stacking enters the scene, the old way of doing this - * -- via the vnode -- is not good enough anymore... - */ - xmp->nullm_vfs = nd.nl_ncp->nc_mount; - nlookup_done(&nd); - - vfs_add_vnodeops(mp, &mp->mnt_vn_norm_ops, - null_vnodeop_entries, 0); - - VOP_UNLOCK(rootvp, 0, td); - - /* - * Keep a held reference to the root vnode. - * It is vrele'd in nullfs_unmount. - */ - xmp->nullm_rootvp = rootvp; - /* - * XXX What's the proper safety condition for querying - * the underlying mount? Is this flag tuning necessary - * at all? - */ - if (xmp->nullm_vfs->mnt_flag & MNT_LOCAL) + } + + cache_unlock(xmp->nullm_ncp); + + vfs_add_vnodeops(mp, &mp->mnt_vn_norm_ops, null_vnodeop_entries, 0); + + if (xmp->nullm_ncp->nc_mount->mnt_flag & MNT_LOCAL) mp->mnt_flag |= MNT_LOCAL; - mp->mnt_data = (qaddr_t) xmp; + mp->mnt_data = (void *)xmp; vfs_getnewfsid(mp); (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); - (void)nullfs_statfs(mp, &mp->mnt_stat, td); - NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n", - mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntfromname); - return (0); -} - -/* - * Free reference to null layer - */ + NULLFSDEBUG("lower %s, alias at %s\n", + mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); + return (0); +} + static int nullfs_unmount(struct mount *mp, int mntflags, struct thread *td) { - void *mntdata; - int flags = 0; - - NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp); - - if (mntflags & MNT_FORCE) - flags |= FORCECLOSE; - - /* - * Finally, throw away the null_mount structure - */ - mntdata = mp->mnt_data; - mp->mnt_data = 0; - free(mntdata, M_NULLFSMNT); - return 0; + NULLNCDEBUG(mp->mnt_ncp); + + cache_drop(MOUNTTONULLMOUNT(mp)->nullm_ncp); + free(mp->mnt_data, M_NULLFSMNT); + + return (0); +} + +static int +nullfs_start(struct mount *mp, int flags, struct thread *td) +{ + mp->mnt_ncp->nc_shadowed = MOUNTTONULLMOUNT(mp)->nullm_ncp; + + return (0); } static int nullfs_root(struct mount *mp, struct vnode **vpp) { - struct thread *td = curthread; /* XXX */ - struct vnode *vp; - - NULLFSDEBUG("nullfs_root(mp = %p, vp = %p)\n", (void *)mp, - (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp); - - /* - * Return locked reference to root. - */ - vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp; - vref(vp); - -#ifdef NULLFS_DEBUG - if (VOP_ISLOCKED(vp, NULL)) { - Debugger("root vnode is locked.\n"); - vrele(vp); - return (EDEADLK); - } -#endif - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - *vpp = vp; - return 0; + int error; + + error = cache_vget(MOUNTTONULLMOUNT(mp)->nullm_ncp, + crhold(proc0.p_ucred), LK_EXCLUSIVE | LK_RETRY, vpp); + crfree(proc0.p_ucred); + + return (error); +} + +static __inline +struct mount * +nullfs_lowermount_0(struct mount *mp) +{ + return (MOUNTTONULLMOUNT(mp)->nullm_ncp->nc_mount); } static int nullfs_quotactl(struct mount *mp, int cmd, uid_t uid, caddr_t arg, struct thread *td) { - return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg, td); + return VFS_QUOTACTL(nullfs_lowermount_0(mp), cmd, uid, arg, td); } static int @@ -218,12 +178,12 @@ nullfs_statfs(struct mount *mp, struct s int error; struct statfs mstat; - NULLFSDEBUG("nullfs_statfs(mp = %p, vp = %p)\n", (void *)mp, - (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp); + NULLFSDEBUG("mp %p, ncp %p, lower mp %p\n", + mp, mp->mnt_ncp, nullfs_lowermount_0(mp)); bzero(&mstat, sizeof(mstat)); - error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, &mstat, td); + error = VFS_STATFS(nullfs_lowermount_0(mp), &mstat, td); if (error) return (error); @@ -248,23 +208,21 @@ nullfs_checkexp(struct mount *mp, struct nullfs_checkexp(struct mount *mp, struct sockaddr *nam, int *extflagsp, struct ucred **credanonp) { - - return VFS_CHECKEXP(MOUNTTONULLMOUNT(mp)->nullm_vfs, nam, - extflagsp, credanonp); + return VFS_CHECKEXP(nullfs_lowermount_0(mp), nam, extflagsp, credanonp); } static int nullfs_extattrctl(struct mount *mp, int cmd, const char *attrname, caddr_t arg, struct thread *td) { - return VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, attrname, - arg, td); + return VFS_EXTATTRCTL(nullfs_lowermount_0(mp), cmd, attrname, arg, td); } static struct vfsops null_vfsops = { .vfs_mount = nullfs_mount, .vfs_unmount = nullfs_unmount, + .vfs_start = nullfs_start, .vfs_root = nullfs_root, .vfs_quotactl = nullfs_quotactl, .vfs_statfs = nullfs_statfs, diff -r e1d135a8f666 sys/vfs/nullfs/null_vnops.c --- a/sys/vfs/nullfs/null_vnops.c Sun Mar 26 07:56:54 2006 +0000 +++ b/sys/vfs/nullfs/null_vnops.c Wed Mar 29 10:29:08 2006 +0200 @@ -98,7 +98,7 @@ * might be able to get on with a hybrid solution: overlay some vnodes, and rely * on namecache API for the rest. */ - + #include #include #include @@ -109,6 +109,8 @@ #include #include #include +#include +#include #include "null.h" static int null_nresolve(struct vop_nresolve_args *ap); @@ -122,90 +124,190 @@ static int null_nrmdir(struct vop_nrmdir static int null_nrmdir(struct vop_nrmdir_args *ap); static int null_nrename(struct vop_nrename_args *ap); +static __inline +struct mount * +nullfs_lowermount_l(struct namecache *ncp) +{ + /* + * The code in use below allows allows passing through lower mounts. + * If we didn't want to do that, we could use + * + * MOUNTTONULLMOUNT(ncp->nc_mount)->nullm_ncp->nc_mount + * + * Eventually, the choice might be configurable. + */ + return (ncp->nc_shadowed->nc_mount); +} + + +static __inline +int +nullfs_check(struct namecache *ncp) +{ + if (ncp->nc_mount->mnt_ncp == ncp) + return (EPERM); + + if (!ncp->nc_shadowed) + return (ENOENT); + + if (ncp->nc_shadowheight == 0) + return (EINVAL); + + return (0); +} + static int null_nresolve(struct vop_nresolve_args *ap) { - ap->a_head.a_ops = MOUNTTONULLMOUNT(ap->a_ncp->nc_mount)->nullm_vfs->mnt_vn_norm_ops; - - return vop_nresolve_ap(ap); -} - -static int -null_ncreate(struct vop_ncreate_args *ap) -{ - ap->a_head.a_ops = MOUNTTONULLMOUNT(ap->a_ncp->nc_mount)->nullm_vfs->mnt_vn_norm_ops; - - return vop_ncreate_ap(ap); -} - -static int -null_nmkdir(struct vop_nmkdir_args *ap) -{ - ap->a_head.a_ops = MOUNTTONULLMOUNT(ap->a_ncp->nc_mount)->nullm_vfs->mnt_vn_norm_ops; - - return vop_nmkdir_ap(ap); -} - -static int -null_nmknod(struct vop_nmknod_args *ap) -{ - ap->a_head.a_ops = MOUNTTONULLMOUNT(ap->a_ncp->nc_mount)->nullm_vfs->mnt_vn_norm_ops; - - return vop_nmknod_ap(ap); -} - -static int -null_nlink(struct vop_nlink_args *ap) -{ - ap->a_head.a_ops = MOUNTTONULLMOUNT(ap->a_ncp->nc_mount)->nullm_vfs->mnt_vn_norm_ops; - - return vop_nlink_ap(ap); -} - -static int -null_nsymlink(struct vop_nsymlink_args *ap) -{ - ap->a_head.a_ops = MOUNTTONULLMOUNT(ap->a_ncp->nc_mount)->nullm_vfs->mnt_vn_norm_ops; - - return vop_nsymlink_ap(ap); -} - -static int -null_nwhiteout(struct vop_nwhiteout_args *ap) -{ - ap->a_head.a_ops = MOUNTTONULLMOUNT(ap->a_ncp->nc_mount)->nullm_vfs->mnt_vn_norm_ops; - - return vop_nwhiteout_ap(ap); -} - -static int -null_nremove(struct vop_nremove_args *ap) -{ - ap->a_head.a_ops = MOUNTTONULLMOUNT(ap->a_ncp->nc_mount)->nullm_vfs->mnt_vn_norm_ops; - - return vop_nremove_ap(ap); -} - -static int -null_nrmdir(struct vop_nrmdir_args *ap) -{ - ap->a_head.a_ops = MOUNTTONULLMOUNT(ap->a_ncp->nc_mount)->nullm_vfs->mnt_vn_norm_ops; - - return vop_nrmdir_ap(ap); -} + struct namecache *ncp = ap->a_ncp; + struct nlcomponent nlc; + struct namecache *sncp, *psncp; + int error = 0; + + sncp = ncp->nc_shadow_next; + if (sncp) { + cache_hold(sncp); + cache_setunresolved(sncp); + cache_put(sncp); + } + + cache_unlock(ncp); + cache_lock(ncp->nc_parent); + psncp = ncp->nc_parent->nc_shadowed; + if (psncp) + cache_hold(psncp); + cache_unlock(ncp->nc_parent); + + if (! psncp) { + cache_lock(ncp); + if ((ncp->nc_flag & NCF_UNRESOLVED) == 0) + cache_setvp(ncp, NULL); + return (ncp->nc_error); + } + + nlc.nlc_nameptr = ncp->nc_name; + nlc.nlc_namelen = ncp->nc_nlen; + sncp = cache_nlookup(psncp, &nlc); + cache_drop(psncp); + + if ((sncp->nc_flag & NCF_UNRESOLVED) == 0) + goto postdowncall; + + ap->a_head.a_ops = sncp->nc_mount->mnt_vn_use_ops; + ap->a_ncp = sncp; + /* + * According to cache_resolve(), the primary place for + * VOP_NRESOLVE calls, the caller of the nresolve method + * is the one who should take care about ncp->nc_error. + */ + ap->a_ncp->nc_error = vop_nresolve_ap(ap); + +postdowncall: + + error = cache_shadow_attach(ncp, sncp); + + NULLNCDEBUG(ncp); + NULLNCDEBUG(sncp); + NULLFSDEBUG("attach error %d\n", error); + + if (error) { + cache_put(sncp); + if (ncp->nc_flag & NCF_UNRESOLVED) { + cache_setvp(ncp, NULL); + error = ENOENT; + } else if (error == EEXIST) + error = ncp->nc_error; + } else { + error = sncp->nc_error; + cache_setvp(ncp, sncp->nc_vp); + ncp->nc_shadowed = sncp; + cache_drop(sncp); + } + + NULLFSDEBUG("error %d\n", error); + return (error); +} + +#define NULL_NVOP_TEMPLATE(OP) \ +static int \ +null_ ## OP(struct vop_ ## OP ## _args *ap) \ +{ \ + struct namecache *ncp = ap->a_ncp; \ + struct namecache *sncp = ncp->nc_shadowed; \ + int error; \ + \ + NULLNCDEBUG(ap->a_ncp); \ + \ + if ((error = nullfs_check(ncp))) \ + return (error); \ + cache_hold(sncp); \ + \ + NULLNCDEBUG(ap->a_ncp->nc_shadowed); \ + \ + ap->a_head.a_ops = nullfs_lowermount_l(ap->a_ncp)->mnt_vn_use_ops; \ + ap->a_ncp = ncp->nc_shadowed; \ + \ + error = vop_ ## OP ## _ap(ap); \ + NULLNCDEBUG(ncp); \ + NULLNCDEBUG(sncp); \ + sncp->nc_shadowinfo == ncp->nc_shadowinfo ? \ + cache_drop(sncp) : \ + cache_put(sncp); \ + \ + return (error); \ +} + +NULL_NVOP_TEMPLATE(ncreate) +NULL_NVOP_TEMPLATE(nmkdir) +NULL_NVOP_TEMPLATE(nmknod) +NULL_NVOP_TEMPLATE(nlink) +NULL_NVOP_TEMPLATE(nsymlink) +NULL_NVOP_TEMPLATE(nwhiteout) +NULL_NVOP_TEMPLATE(nremove) +NULL_NVOP_TEMPLATE(nrmdir) static int null_nrename(struct vop_nrename_args *ap) { + struct namecache *fncp = ap->a_fncp; + struct namecache *tncp = ap->a_tncp; + struct namecache *sfncp = fncp->nc_shadowed; + struct namecache *stncp = tncp->nc_shadowed; struct mount *lmp; - - lmp = MOUNTTONULLMOUNT(ap->a_fncp->nc_mount)->nullm_vfs; - if (lmp != MOUNTTONULLMOUNT(ap->a_tncp->nc_mount)->nullm_vfs) - return (EINVAL); - - ap->a_head.a_ops = lmp->mnt_vn_norm_ops; - - return vop_nrename_ap(ap); + int error; + + NULLNCDEBUG(ap->a_fncp); + NULLNCDEBUG(ap->a_tncp); + + if ((error = nullfs_check(fncp))) + return (error); + if ((error = nullfs_check(tncp))) + return (error); + + lmp = nullfs_lowermount_l(fncp); + if (lmp != nullfs_lowermount_l(tncp)) + return (EXDEV); + + cache_hold(sfncp); + cache_hold(stncp); + + NULLNCDEBUG(ap->a_fncp->nc_shadowed); + NULLNCDEBUG(ap->a_tncp->nc_shadowed); + + ap->a_head.a_ops = lmp->mnt_vn_use_ops; + ap->a_fncp = fncp->nc_shadowed; + ap->a_tncp = tncp->nc_shadowed; + + error = vop_nrename_ap(ap); + + sfncp->nc_shadowinfo == fncp->nc_shadowinfo ? + cache_drop(sfncp) : + cache_put(sfncp); + stncp->nc_shadowinfo == tncp->nc_shadowinfo ? + cache_drop(stncp) : + cache_put(stncp); + + return (error); } /* @@ -224,4 +326,3 @@ struct vnodeopv_entry_desc null_vnodeop_ { &vop_nrename_desc, (vnodeopv_entry_t) null_nrename }, { NULL, NULL } }; -