From 690a3127c6bcff1aec2edae24fd0fe5f41633faf Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Tue, 28 Sep 2004 00:25:34 +0000 Subject: [PATCH] VFS messaging/interfacing work stage 6/99. Populate and maintain the namecache pointers previously attached to struct filedesc, giving the new lookup code a base from which to work. Implement the new lookup API (it is not yet being used by anything) and augment the namecache API to handle the new functions, in particular adding cache_setvp() to resolve an unresolved namecache entry into a positive or negative hit and set various flags. Note that we do not yet cache symlink data but we could very easily. The new API is greatly simplified. Basically nlookups need only returned a locked namecache pointer (guarenteeing namespace atomicy). Related vnodes are not locked. Both the leaf and governing directory vnodes can be extracted from the returned namecache pointer. namecache pointers may also represent negative hits, which means that their namespace locking feature serves to reserve a filename that has not yet been created (e.g. open+create, rename). The kernel is still using the old API as of this commit. This commit is primarily introducing the management infrastructure required to actually start writing code to use the new API. VOP_RESOLVE() has been added, along with a default function which falls back to VOP_LOOKUP()/VOP_CACHEDLOOKUP(). This VOP function is not yet being used as of this commit. This VOP will be responsible for taking an unresolved but locked namecache structure (hence the namespace is locked), and actually does the directory lookup. But unlike the far more complex VOP_LOOKUP()/VOP_CACHEDLOOKUP() API the VOP_RESOLVE() API only needs to attach a vnode (or NULL if the entry does not exist) to the passed-in namecache structure. It is likely that timeouts, e.g. for NFS, will also be attached via this API. This commit does not implement any of the cache-coherency infrastructure but keeps this future requirement in mind in its design. --- sys/conf/files | 3 +- sys/emulation/svr4/svr4_misc.c | 7 +- sys/kern/init_main.c | 7 +- sys/kern/kern_descrip.c | 33 ++- sys/kern/vfs_cache.c | 357 +++++++++++++++++++++++------ sys/kern/vfs_default.c | 58 ++++- sys/kern/vfs_lookup.c | 4 +- sys/kern/vfs_nlookup.c | 399 +++++++++++++++++++++++++++++++++ sys/kern/vfs_syscalls.c | 16 +- sys/kern/vfs_vopops.c | 34 ++- sys/sys/namecache.h | 18 +- sys/sys/namei.h | 4 +- sys/sys/nlookup.h | 84 +++++++ sys/sys/vfsops.h | 13 +- 14 files changed, 941 insertions(+), 96 deletions(-) create mode 100644 sys/kern/vfs_nlookup.c create mode 100644 sys/sys/nlookup.h diff --git a/sys/conf/files b/sys/conf/files index 46b9b582ff..0468751856 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,5 +1,5 @@ # $FreeBSD: src/sys/conf/files,v 1.340.2.137 2003/06/04 17:10:30 sam Exp $ -# $DragonFly: src/sys/conf/files,v 1.75 2004/09/23 06:52:05 simokawa Exp $ +# $DragonFly: src/sys/conf/files,v 1.76 2004/09/28 00:25:25 dillon Exp $ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and @@ -723,6 +723,7 @@ kern/vfs_conf.c standard kern/vfs_default.c standard kern/vfs_init.c standard kern/vfs_lookup.c standard +kern/vfs_nlookup.c standard kern/vfs_subr.c standard kern/vfs_syscalls.c standard kern/vfs_vnops.c standard diff --git a/sys/emulation/svr4/svr4_misc.c b/sys/emulation/svr4/svr4_misc.c index 31cc7cce70..0f263eb3af 100644 --- a/sys/emulation/svr4/svr4_misc.c +++ b/sys/emulation/svr4/svr4_misc.c @@ -26,7 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/svr4/svr4_misc.c,v 1.13.2.7 2003/01/14 21:33:58 dillon Exp $ - * $DragonFly: src/sys/emulation/svr4/Attic/svr4_misc.c,v 1.22 2004/04/24 04:32:03 drhodus Exp $ + * $DragonFly: src/sys/emulation/svr4/Attic/svr4_misc.c,v 1.23 2004/09/28 00:25:34 dillon Exp $ */ /* @@ -618,9 +618,12 @@ svr4_sys_fchroot(struct svr4_sys_fchroot_args *uap) if (error) return error; vref(vp); - if (fdp->fd_rdir != NULL) + if (fdp->fd_rdir != NULL) { vrele(fdp->fd_rdir); + cache_drop(fdp->fd_nrdir); + } fdp->fd_rdir = vp; + fdp->fd_nrdir = cache_vptoncp(vp); /* stopgap */ return 0; } diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index e1c08bbedb..ff471520da 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -40,7 +40,7 @@ * * @(#)init_main.c 8.9 (Berkeley) 1/21/94 * $FreeBSD: src/sys/kern/init_main.c,v 1.134.2.8 2003/06/06 20:21:32 tegge Exp $ - * $DragonFly: src/sys/kern/init_main.c,v 1.36 2004/09/20 06:32:58 dillon Exp $ + * $DragonFly: src/sys/kern/init_main.c,v 1.37 2004/09/28 00:25:29 dillon Exp $ */ #include "opt_init_path.h" @@ -455,6 +455,7 @@ start_init(void *dummy) char *var, *path, *next, *s; char *ucp, **uap, *arg0, *arg1; struct proc *p; + struct namecache *rootncp; p = curproc; @@ -465,8 +466,10 @@ start_init(void *dummy) vref(p->p_fd->fd_cdir); p->p_fd->fd_rdir = rootvnode; vref(p->p_fd->fd_rdir); - vfs_cache_setroot(rootvnode); + rootncp = vfs_cache_setroot(rootvnode); VOP_UNLOCK(rootvnode, NULL, 0, curthread); + p->p_fd->fd_ncdir = cache_hold(rootncp); + p->p_fd->fd_nrdir = cache_hold(rootncp); /* * Need just enough stack to hold the faked-up "execve()" arguments. diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index 5e21b49e59..1952cf1eba 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -37,7 +37,7 @@ * * @(#)kern_descrip.c 8.6 (Berkeley) 4/19/94 * $FreeBSD: src/sys/kern/kern_descrip.c,v 1.81.2.19 2004/02/28 00:43:31 tegge Exp $ - * $DragonFly: src/sys/kern/kern_descrip.c,v 1.27 2004/07/29 20:32:24 dillon Exp $ + * $DragonFly: src/sys/kern/kern_descrip.c,v 1.28 2004/09/28 00:25:29 dillon Exp $ */ #include "opt_compat.h" @@ -967,13 +967,19 @@ fdinit(struct proc *p) newfdp = malloc(sizeof(struct filedesc0), M_FILEDESC, M_WAITOK|M_ZERO); newfdp->fd_fd.fd_cdir = fdp->fd_cdir; - if (newfdp->fd_fd.fd_cdir) + if (newfdp->fd_fd.fd_cdir) { vref(newfdp->fd_fd.fd_cdir); + newfdp->fd_fd.fd_ncdir = cache_hold(fdp->fd_ncdir); + } newfdp->fd_fd.fd_rdir = fdp->fd_rdir; + newfdp->fd_fd.fd_nrdir = cache_hold(fdp->fd_nrdir); vref(newfdp->fd_fd.fd_rdir); newfdp->fd_fd.fd_jdir = fdp->fd_jdir; - if (newfdp->fd_fd.fd_jdir) + if (newfdp->fd_fd.fd_jdir) { vref(newfdp->fd_fd.fd_jdir); + newfdp->fd_fd.fd_njdir = cache_hold(fdp->fd_njdir); + } + /* Create the file descriptor table. */ newfdp->fd_fd.fd_refcnt = 1; @@ -1012,17 +1018,23 @@ fdcopy(struct proc *p) newfdp = malloc(sizeof(struct filedesc0), M_FILEDESC, M_WAITOK); bcopy(fdp, newfdp, sizeof(struct filedesc)); - if (newfdp->fd_cdir) + if (newfdp->fd_cdir) { vref(newfdp->fd_cdir); + newfdp->fd_ncdir = cache_hold(newfdp->fd_ncdir); + } /* * We must check for fd_rdir here, at least for now because * the init process is created before we have access to the * rootvode to take a reference to it. */ - if (newfdp->fd_rdir) + if (newfdp->fd_rdir) { vref(newfdp->fd_rdir); - if (newfdp->fd_jdir) + newfdp->fd_nrdir = cache_hold(newfdp->fd_nrdir); + } + if (newfdp->fd_jdir) { vref(newfdp->fd_jdir); + newfdp->fd_njdir = cache_hold(newfdp->fd_njdir); + } newfdp->fd_refcnt = 1; /* @@ -1178,11 +1190,16 @@ fdfree(struct proc *p) } if (fdp->fd_nfiles > NDFILE) free(fdp->fd_ofiles, M_FILEDESC); - if (fdp->fd_cdir) + if (fdp->fd_cdir) { + cache_drop(fdp->fd_ncdir); vrele(fdp->fd_cdir); + } + cache_drop(fdp->fd_nrdir); vrele(fdp->fd_rdir); - if (fdp->fd_jdir) + if (fdp->fd_jdir) { + cache_drop(fdp->fd_njdir); vrele(fdp->fd_jdir); + } if (fdp->fd_knlist) free(fdp->fd_knlist, M_KQUEUE); if (fdp->fd_knhash) diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index b6df1f1a2b..c0368868b9 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -67,7 +67,7 @@ * * @(#)vfs_cache.c 8.5 (Berkeley) 3/22/95 * $FreeBSD: src/sys/kern/vfs_cache.c,v 1.42.2.6 2001/10/05 20:07:03 dillon Exp $ - * $DragonFly: src/sys/kern/vfs_cache.c,v 1.27 2004/09/26 06:00:05 dillon Exp $ + * $DragonFly: src/sys/kern/vfs_cache.c,v 1.28 2004/09/28 00:25:29 dillon Exp $ */ #include @@ -80,6 +80,7 @@ #include #include #include +#include #include #include #include @@ -208,6 +209,61 @@ _cache_drop(struct namecache *ncp) } } +/* + * Link a new namecache entry to its parent. Be careful to avoid races + * if vhold() blocks in the future. + */ +static void +cache_link_parent(struct namecache *ncp, struct namecache *par) +{ + KKASSERT(ncp->nc_parent == NULL); + ncp->nc_parent = par; + if (TAILQ_EMPTY(&par->nc_list)) { + TAILQ_INSERT_HEAD(&par->nc_list, ncp, nc_entry); + if (par->nc_vp) + vhold(par->nc_vp); + } else { + TAILQ_INSERT_HEAD(&par->nc_list, ncp, nc_entry); + } +} + +/* + * Remove the parent association from a namecache structure. + */ +static void +cache_unlink_parent(struct namecache *ncp) +{ + struct namecache *par; + + if ((par = ncp->nc_parent) != NULL) { + ncp->nc_parent = NULL; + par = cache_hold(par); + TAILQ_REMOVE(&par->nc_list, ncp, nc_entry); + if (par->nc_vp && TAILQ_EMPTY(&par->nc_list)) + vdrop(par->nc_vp); + cache_drop(par); + } +} + +/* + * Allocate a new namecache structure. + */ +static struct namecache * +cache_alloc(void) +{ + struct namecache *ncp; + + ncp = malloc(sizeof(*ncp), M_VFSCACHE, M_WAITOK|M_ZERO); + ncp->nc_flag = NCF_UNRESOLVED; + ncp->nc_error = ENOTCONN; /* needs to be resolved */ + TAILQ_INIT(&ncp->nc_list); + return(ncp); +} + + +/* + * Ref and deref a namecache structure. + */ struct namecache * cache_hold(struct namecache *ncp) { @@ -279,72 +335,122 @@ cache_unlock(struct namecache *ncp) } /* - * Unlock and release a namecache entry. + * ref-and-lock, unlock-and-deref functions. */ void -cache_put(struct namecache *ncp) +cache_get(struct namecache *ncp) { - cache_unlock(ncp); - _cache_drop(ncp); + _cache_hold(ncp); + cache_lock(ncp); } -static void -cache_link_parent(struct namecache *ncp, struct namecache *par) +void +cache_put(struct namecache *ncp) { - KKASSERT(ncp->nc_parent == NULL); - ncp->nc_parent = par; - if (TAILQ_EMPTY(&par->nc_list)) { - if (par->nc_vp) - vhold(par->nc_vp); - } - TAILQ_INSERT_HEAD(&par->nc_list, ncp, nc_entry); + cache_unlock(ncp); + _cache_drop(ncp); } -static void -cache_unlink_parent(struct namecache *ncp) +/* + * Locate or create a dummy namecache entry for the vnode, reference, + * and return it. The namecache entry will be unhashed, unnamed, and not + * have any parent. + * + * This routine is primarily a stopgap to allow us to track the current, + * root, and jail directories until the whole system is shifted over to + * the new namecache API. However, we might need it permanently to handle + * things like fchdir() and fchroot(). + */ +struct namecache * +cache_vptoncp(struct vnode *vp) { - struct namecache *par; + struct namecache *ncp; + struct namecache *new_ncp; - if ((par = ncp->nc_parent) != NULL) { - ncp->nc_parent = NULL; - par = cache_hold(par); - TAILQ_REMOVE(&par->nc_list, ncp, nc_entry); - if (par->nc_vp && TAILQ_EMPTY(&par->nc_list)) - vdrop(par->nc_vp); - cache_drop(par); + new_ncp = NULL; +retry: + TAILQ_FOREACH(ncp, &vp->v_namecache, nc_vnode) { + if (ncp->nc_flag & NCF_UNRESOLVED) + continue; + if (ncp->nc_name == NULL && ncp->nc_parent == NULL) { + if (new_ncp) + free(new_ncp, M_VFSCACHE); + goto done; + } + } + if (new_ncp == NULL) { + new_ncp = cache_alloc(); + goto retry; } + ncp = new_ncp; + cache_setvp(ncp, vp); +done: + cache_hold(ncp); + return(ncp); } -static struct namecache * -cache_alloc(struct vnode *vp) +/* + * Resolve an unresolved ncp by associating a vnode with it. If the + * vnode is NULL, a negative cache entry is created. + * + * The ncp should be locked on entry and will remain locked on return. + */ +void +cache_setvp(struct namecache *ncp, struct vnode *vp) { - struct namecache *ncp; - - ncp = malloc(sizeof(*ncp), M_VFSCACHE, M_WAITOK|M_ZERO); - TAILQ_INIT(&ncp->nc_list); + KKASSERT(ncp->nc_flag & NCF_UNRESOLVED); ncp->nc_vp = vp; if (vp != NULL) { TAILQ_INSERT_HEAD(&vp->v_namecache, ncp, nc_vnode); + switch(vp->v_type) { + case VDIR: + ncp->nc_flag |= NCF_ISDIR; + break; + case VLNK: + ncp->nc_flag |= NCF_ISSYMLINK; + /* XXX cache the contents of the symlink */ + break; + default: + break; + } ++numcache; } else { TAILQ_INSERT_TAIL(&ncneglist, ncp, nc_vnode); ++numneg; } - return(ncp); + ncp->nc_error = 0; + ncp->nc_flag &= ~NCF_UNRESOLVED; } -#if 0 -static struct namecache * -cache_alloc_unresolved(struct vnode *vp) +/* + * Disassociate the vnode or negative-cache association and mark a + * namecache entry as unresolved again. Note that the ncp is still + * left in the hash table and still linked to its parent. + * + * The ncp should be locked on entry and will remain locked on return. + */ +void +cache_setunresolved(struct namecache *ncp) { - struct namecache *ncp; + struct vnode *vp; - ncp = malloc(sizeof(*ncp), M_VFSCACHE, M_WAITOK|M_ZERO); - TAILQ_INIT(&ncp->nc_list); - ncp->nc_flag = NCF_UNRESOLVED; - return(ncp); + if ((ncp->nc_flag & NCF_UNRESOLVED) == 0) { + ncp->nc_flag |= NCF_UNRESOLVED; + ncp->nc_flag &= ~(NCF_WHITEOUT|NCF_ISDIR|NCF_ISSYMLINK); + ncp->nc_error = ENOTCONN; + ++numunres; + if ((vp = ncp->nc_vp) != NULL) { + --numcache; + ncp->nc_vp = NULL; /* safety */ + TAILQ_REMOVE(&vp->v_namecache, ncp, nc_vnode); + if (!TAILQ_EMPTY(&ncp->nc_list)) + vdrop(vp); + } else { + TAILQ_REMOVE(&ncneglist, ncp, nc_vnode); + --numneg; + } + } } -#endif /* * Try to destroy a namecache entry. The entry is disassociated from its @@ -369,25 +475,11 @@ static void cache_zap(struct namecache *ncp) { struct namecache *par; - struct vnode *vp; /* * Disassociate the vnode or negative cache ref and set NCF_UNRESOLVED. */ - if ((ncp->nc_flag & NCF_UNRESOLVED) == 0) { - ncp->nc_flag |= NCF_UNRESOLVED; - ++numunres; - if ((vp = ncp->nc_vp) != NULL) { - --numcache; - ncp->nc_vp = NULL; /* safety */ - TAILQ_REMOVE(&vp->v_namecache, ncp, nc_vnode); - if (!TAILQ_EMPTY(&ncp->nc_list)) - vdrop(vp); - } else { - TAILQ_REMOVE(&ncneglist, ncp, nc_vnode); - --numneg; - } - } + cache_setunresolved(ncp); /* * Try to scrap the entry and possibly tail-recurse on its parent. @@ -475,10 +567,118 @@ done: * specifically allows whole chains to be created in an unresolved state. */ struct namecache * -cache_nclookup(struct namecache *par, struct componentname *cnp) +cache_nlookup(struct namecache *par, struct nlcomponent *nlc) +{ + struct namecache *ncp; + struct namecache *new_ncp; + struct nchashhead *nchpp; + u_int32_t hash; + globaldata_t gd; + + numcalls++; + gd = mycpu; + + /* + * Deal with "." and "..". Note that 'par' is not locked, so the + * ".." case can simply locate the parent without having to do + * anything special. + */ + if (nlc->nlc_nameptr[0] == '.') { + if (nlc->nlc_namelen == 1) { + ++dothits; + ++numposhits; + ncp = par; + goto found; + } + if (nlc->nlc_namelen == 2 && nlc->nlc_nameptr[1] == '.') { + if (par->nc_parent == NULL) + return (NULL); /* XXX do not return null */ + ++dotdothits; + ++numposhits; + ncp = par->nc_parent; + goto found; + } + } + + /* + * Try to locate an existing entry + */ + hash = fnv_32_buf(nlc->nlc_nameptr, nlc->nlc_namelen, FNV1_32_INIT); + hash = fnv_32_buf(&par, sizeof(par), hash); + new_ncp = NULL; +restart: + LIST_FOREACH(ncp, (NCHHASH(hash)), nc_hash) { + numchecks++; + + /* + * Zap entries that have timed out. + */ + if (ncp->nc_timeout && + (int)(ncp->nc_timeout - ticks) < 0 + ) { + cache_zap(cache_hold(ncp)); + goto restart; + } + + /* + * Break out if we find a matching entry. Note that + * UNRESOLVED entries may match. + */ + if (ncp->nc_parent == par && + ncp->nc_nlen == nlc->nlc_namelen && + bcmp(ncp->nc_name, nlc->nlc_nameptr, ncp->nc_nlen) == 0 + ) { + if (new_ncp) { + free(new_ncp->nc_name, M_VFSCACHE); + free(new_ncp, M_VFSCACHE); + } + goto found; + } + } + + /* + * We failed to locate an entry, create a new entry and add it to + * the cache. We have to relookup after possibly blocking in + * malloc. + */ + if (new_ncp == NULL) { + new_ncp = cache_alloc(); + new_ncp->nc_name = malloc(nlc->nlc_namelen, + M_VFSCACHE, M_WAITOK); + goto restart; + } + + ncp = new_ncp; + + /* + * Initialize as a new UNRESOLVED entry, lock (non-blocking), + * and link to the parent. + */ + ncp->nc_nlen = nlc->nlc_namelen; + bcopy(nlc->nlc_nameptr, ncp->nc_name, nlc->nlc_namelen); + nchpp = NCHHASH(hash); + LIST_INSERT_HEAD(nchpp, ncp, nc_hash); + ncp->nc_flag |= NCF_HASHED; + cache_get(ncp); + cache_link_parent(ncp, par); + return(ncp); + + /* + * Entry found. Cleanup any dangling new_ncp, ref and lock + * the ncp. + */ +found: + cache_get(ncp); + return(ncp); +} + +/* + * Resolve an unresolved namecache entry, generally by looking it up + */ +int +cache_resolve(struct namecache *ncp) { - KKASSERT(0); - return(NULL); + panic("cache_resolve() not yet implemented"); } /* @@ -517,8 +717,10 @@ cache_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp) * NOTE: in this stage of development, the passed 'par' is * almost always NULL. */ - if ((par = TAILQ_FIRST(&dvp->v_namecache)) == NULL) - par = cache_alloc(dvp); + if ((par = TAILQ_FIRST(&dvp->v_namecache)) == NULL) { + par = cache_alloc(); + cache_setvp(par, dvp); + } /* * Deal with "." and "..". In this stage of code development we leave @@ -687,14 +889,20 @@ cache_mount(struct vnode *dvp, struct vnode *tvp) } } - if ((par = TAILQ_FIRST(&dvp->v_namecache)) == NULL) - par = cache_alloc(dvp); + /* + * XXX + */ + if ((par = TAILQ_FIRST(&dvp->v_namecache)) == NULL) { + par = cache_alloc(); + cache_setvp(par, dvp); + } /* * Otherwise create a new linkage. */ - ncp = cache_alloc(tvp); - ncp->nc_flag = NCF_MOUNTPT; + ncp = cache_alloc(); + ncp->nc_flag |= NCF_MOUNTPT; + cache_setvp(ncp, tvp); cache_link_parent(ncp, par); /* @@ -725,8 +933,10 @@ cache_enter(struct vnode *dvp, struct namecache *par, struct vnode *vp, struct c * it. The name of the entry is not known so it isn't hashed. */ if (par == NULL) { - if ((par = TAILQ_FIRST(&dvp->v_namecache)) == NULL) - par = cache_alloc(dvp); + if ((par = TAILQ_FIRST(&dvp->v_namecache)) == NULL) { + par = cache_alloc(); + cache_setvp(par, dvp); + } } /* @@ -747,8 +957,10 @@ cache_enter(struct vnode *dvp, struct namecache *par, struct vnode *vp, struct c if (par->nc_parent) cache_unlink_parent(par); } else { - if ((ncp = TAILQ_FIRST(&vp->v_namecache)) == NULL) - ncp = cache_alloc(vp); + if ((ncp = TAILQ_FIRST(&vp->v_namecache)) == NULL) { + ncp = cache_alloc(); + cache_setvp(ncp, vp); + } cache_hold(par); if (par->nc_parent) cache_unlink_parent(par); @@ -783,7 +995,8 @@ again: name = malloc(cnp->cn_namelen, M_VFSCACHE, M_WAITOK); - ncp = cache_alloc(vp); + ncp = cache_alloc(); + cache_setvp(ncp, vp); if (nczapcheck > 1) printf("alloc\n"); @@ -861,8 +1074,11 @@ nchinit(void) * Create an association between the root of our namecache and * the root vnode. This routine may be called several times during * booting. + * + * If the caller intends to save the returned namecache pointer somewhere + * it must cache_hold() it. */ -void +struct namecache * vfs_cache_setroot(struct vnode *nvp) { KKASSERT(rootnamecache.nc_refs > 0); /* don't accidently free */ @@ -880,6 +1096,7 @@ vfs_cache_setroot(struct vnode *nvp) TAILQ_INSERT_TAIL(&ncneglist, &rootnamecache, nc_vnode); rootnamecache.nc_flag &= ~NCF_WHITEOUT; } + return(&rootnamecache); } /* diff --git a/sys/kern/vfs_default.c b/sys/kern/vfs_default.c index 03fe3ab6c7..e607017140 100644 --- a/sys/kern/vfs_default.c +++ b/sys/kern/vfs_default.c @@ -37,7 +37,7 @@ * * * $FreeBSD: src/sys/kern/vfs_default.c,v 1.28.2.7 2003/01/10 18:23:26 bde Exp $ - * $DragonFly: src/sys/kern/vfs_default.c,v 1.13 2004/08/28 19:02:05 dillon Exp $ + * $DragonFly: src/sys/kern/vfs_default.c,v 1.14 2004/09/28 00:25:29 dillon Exp $ */ #include @@ -50,6 +50,7 @@ #include #include #include +#include #include #include @@ -61,6 +62,7 @@ #include static int vop_nolookup (struct vop_lookup_args *); +static int vop_noresolve (struct vop_resolve_args *); static int vop_nostrategy (struct vop_strategy_args *); /* @@ -84,6 +86,7 @@ static struct vnodeopv_entry_desc default_vnodeop_entries[] = { { &vop_lease_desc, vop_null }, { &vop_lock_desc, (void *) vop_stdlock }, { &vop_mmap_desc, vop_einval }, + { &vop_resolve_desc, (void *) vop_noresolve }, { &vop_lookup_desc, (void *) vop_nolookup }, { &vop_open_desc, vop_null }, { &vop_pathconf_desc, vop_einval }, @@ -152,6 +155,59 @@ vop_panic(struct vop_generic_args *ap) panic("filesystem goof: vop_panic[%s]", ap->a_desc->vdesc_name); } +/* + * vop_noresolve { struct namecache *a_ncp } XXX STOPGAP FUNCTION + * + * Resolve a ncp for VFSs which do not support the VOP. Eventually all + * VFSs will support this VOP and this routine can be removed, since + * vop_resolve() is far less complex then the older LOOKUP/CACHEDLOOKUP + * API. + * + * A locked ncp is passed in to be resolved. An NCP is resolved by + * calling cache_setvp() on it. No vnode locks are retained and the + * ncp is left locked on return. + */ +static int +vop_noresolve(struct vop_resolve_args *ap) +{ + int error; + struct vnode *dvp; + struct vnode *vp; + struct namecache *ncp; + struct componentname cnp; + + ncp = ap->a_ncp; /* locked namecache node */ + if (ncp->nc_parent == NULL) + return(EPERM); + if ((dvp = ncp->nc_parent->nc_vp) == NULL) + return(EPERM); + vget(dvp, NULL, LK_EXCLUSIVE, curthread); + + bzero(&cnp, sizeof(cnp)); + cnp.cn_nameiop = NAMEI_LOOKUP; + cnp.cn_flags = CNP_ISLASTCN; + cnp.cn_nameptr = ncp->nc_name; + cnp.cn_namelen = ncp->nc_nlen; + /* creds */ + /* td */ + error = vop_lookup(ap->a_head.a_ops, dvp, &vp, &cnp); + if (error == 0) { + KKASSERT(vp != NULL); + cache_setvp(ncp, vp); + vrele(vp); + } else if (error == ENOENT) { + KKASSERT(vp == NULL); + if (cnp.cn_flags & CNP_ISWHITEOUT) + ncp->nc_flag |= NCF_WHITEOUT; + cache_setvp(ncp, NULL); + } + if (cnp.cn_flags & CNP_PDIRUNLOCK) + vrele(dvp); + else + vput(dvp); + return(error); +} + static int vop_nolookup(ap) struct vop_lookup_args /* { diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c index 488e0852e3..9693abdb1d 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -37,7 +37,7 @@ * * @(#)vfs_lookup.c 8.4 (Berkeley) 2/16/94 * $FreeBSD: src/sys/kern/vfs_lookup.c,v 1.38.2.3 2001/08/31 19:36:49 dillon Exp $ - * $DragonFly: src/sys/kern/vfs_lookup.c,v 1.15 2004/09/26 06:00:05 dillon Exp $ + * $DragonFly: src/sys/kern/vfs_lookup.c,v 1.16 2004/09/28 00:25:29 dillon Exp $ */ #include "opt_ktrace.h" @@ -58,7 +58,7 @@ #include -static int varsym_enable = 0; +int varsym_enable = 0; SYSCTL_INT(_vfs, OID_AUTO, varsym_enable, CTLFLAG_RW, &varsym_enable, 0, "Enable Variant Symlinks"); diff --git a/sys/kern/vfs_nlookup.c b/sys/kern/vfs_nlookup.c new file mode 100644 index 0000000000..c9e22834c3 --- /dev/null +++ b/sys/kern/vfs_nlookup.c @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2004 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Matthew Dillon + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $DragonFly: src/sys/kern/vfs_nlookup.c,v 1.1 2004/09/28 00:25:29 dillon Exp $ + */ +/* + * nlookup() is the 'new' namei interface. Rather then return directory and + * leaf vnodes (in various lock states) the new interface instead deals in + * namecache records. Namecache records may represent both a positive or + * a negative hit. The namespace is locked via the namecache record instead + * of via the vnode, and only the leaf namecache record (representing the + * filename) needs to be locked. + * + * This greatly improves filesystem parallelism and is a huge simplification + * of the API verses the old vnode locking / namei scheme. + * + * Filesystems must actively control the caching aspects of the namecache, + * and since namecache pointers are used as handles they are non-optional + * even for filesystems which do not generally wish to cache things. It is + * intended that a separate cache coherency API will be constructed to handle + * these issues. + */ + +#include "opt_ktrace.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef KTRACE +#include +#endif + +/* + * Initialize a nlookup() structure, early error return for copyin faults + * or a degenerate empty string (which is not allowed). + */ +int +nlookup_init(struct nlookupdata *nd, const char *path, enum uio_seg seg, + int flags) +{ + size_t pathlen; + struct proc *p; + thread_t td; + int error; + + td = curthread; + p = td->td_proc; + + /* + * note: the pathlen set by copy*str() includes the terminating \0. + */ + bzero(nd, sizeof(struct nlookupdata)); + nd->nl_path = zalloc(namei_zone); + nd->nl_flags |= NLC_HASBUF; + if (seg == UIO_SYSSPACE) + error = copystr(path, nd->nl_path, MAXPATHLEN, &pathlen); + else + error = copyinstr(path, nd->nl_path, MAXPATHLEN, &pathlen); + + /* + * Don't allow empty pathnames. + * POSIX.1 requirement: "" is not a vaild file name. + */ + if (error == 0 && pathlen <= 1) + error = ENOENT; + + if (error == 0) { + if (p->p_fd) { + nd->nl_ncp = cache_hold(p->p_fd->fd_ncdir); + nd->nl_rootncp = cache_hold(p->p_fd->fd_nrdir); + if (p->p_fd->fd_njdir) + nd->nl_jailncp = cache_hold(p->p_fd->fd_njdir); + } else { + nd->nl_ncp = cache_vptoncp(rootvnode); + nd->nl_rootncp = cache_hold(nd->nl_ncp); + nd->nl_jailncp = cache_hold(nd->nl_ncp); + } + nd->nl_td = td; + nd->nl_cred = crhold(p->p_ucred); + nd->nl_flags |= flags; + } else { + nlookup_done(nd); + } + return(error); +} + +/* + * Cleanup a nlookupdata structure after we are through with it. + */ +void +nlookup_done(struct nlookupdata *nd) +{ + if (nd->nl_ncp) { + cache_drop(nd->nl_ncp); + nd->nl_ncp = NULL; + } + if (nd->nl_rootncp) { + cache_drop(nd->nl_rootncp); + nd->nl_rootncp = NULL; + } + if (nd->nl_jailncp) { + cache_drop(nd->nl_jailncp); + nd->nl_jailncp = NULL; + } + if ((nd->nl_flags & NLC_HASBUF) && nd->nl_path) { + zfree(namei_zone, nd->nl_path); + nd->nl_path = NULL; + } + if (nd->nl_cred) { + crfree(nd->nl_cred); + nd->nl_cred = NULL; + } +} + +/* + * Simple all-in-one nlookup + */ +struct namecache * +nlookup_simple(const char *str, enum uio_seg seg, int niflags, int *error) +{ + struct nlookupdata nd; + struct namecache *ncp; + + *error = nlookup_init(&nd, str, seg, niflags); + if (*error == 0) { + *error = nlookup(&nd); + ncp = nd.nl_ncp; /* keep hold ref from structure */ + nd.nl_ncp = NULL; /* and NULL out */ + nlookup_done(&nd); + } else { + ncp = NULL; + } + return(ncp); +} + +/* + * Do a generic nlookup. Note that the passed nd is not nlookup_done()'d + * on return, even if an error occurs. If no error occurs the returned + * nl_ncp is always referenced and locked, otherwise it may or may not be. + */ +int +nlookup(struct nlookupdata *nd) +{ + struct nlcomponent nlc; + struct namecache *ncp; + char *ptr; + int error; + int len; + +#ifdef KTRACE + if (KTRPOINT(nd->nl_td, KTR_NAMEI)) + ktrnamei(nd->nl_td->td_proc->p_tracep, nd->nl_path); +#endif + bzero(&nlc, sizeof(nlc)); + + /* + * Setup for the loop. The current working namecache element must + * be in an unlocked state. This typically the case on entry except + * when stringing nlookup()'s along in a chain, since nlookup(0 always + * returns nl_ncp in a locked state. + */ + nd->nl_loopcnt = 0; + if (nd->nl_flags & NLC_NCPISLOCKED) { + nd->nl_flags &= ~NLC_NCPISLOCKED; + cache_unlock(nd->nl_ncp); + } + ptr = nd->nl_path; + + /* + * Loop on the path compoenents + */ + for (;;) { + /* + * Check if the root directory should replace the current + * directory. This is done at the start of a translation + * or after a symbolic link has been found. In other cases + * ptr will never be pointing at a '/'. + */ + if (*ptr == '/') { + do { + ++ptr; + } while (*ptr == '/'); + cache_drop(nd->nl_ncp); + nd->nl_ncp = cache_hold(nd->nl_rootncp); + continue; + } + + /* + * Extract the path component + */ + nlc.nlc_nameptr = ptr; + while (*ptr && *ptr != '/') + ++ptr; + nlc.nlc_namelen = ptr - nlc.nlc_nameptr; + + /* + * Resolve the namespace. The ncp returned by cache_nlookup() + * is referenced and locked. + */ + ncp = cache_nlookup(nd->nl_ncp, &nlc); + if (ncp->nc_flag & NCF_UNRESOLVED) { + error = cache_resolve(ncp); + } else { + error = ncp->nc_error; + } + if (error) { + cache_put(ncp); + break; + } + + /* + * If the element is a symlink and it is either not the last + * element or it is the last element and we are allowed to + * follow symlinks, resolve the symlink. + */ + if ((ncp->nc_flag & NCF_ISSYMLINK) && + (*ptr || (nd->nl_flags & NLC_FOLLOW)) + ) { + if (nd->nl_loopcnt++ >= MAXSYMLINKS) { + error = ELOOP; + cache_put(ncp); + break; + } + error = nreadsymlink(nd, ncp, &nlc); + if (error) { + cache_put(ncp); + break; + } + + /* + * Concatenate trailing path elements onto the returned symlink. + * Note that if the path component (ptr) is not exhausted, it + * will being with a '/', so we do not have to add another one. + * + * The symlink may not be empty. + */ + len = strlen(ptr); + if (nlc.nlc_namelen == 0 || nlc.nlc_namelen + len >= MAXPATHLEN) { + error = nlc.nlc_namelen ? ENAMETOOLONG : ENOENT; + zfree(namei_zone, nlc.nlc_nameptr); + cache_put(ncp); + break; + } + bcopy(ptr, nlc.nlc_nameptr + nlc.nlc_namelen, len + 1); + if (nd->nl_flags & NLC_HASBUF) + zfree(namei_zone, nd->nl_path); + nd->nl_path = nlc.nlc_nameptr; + nd->nl_flags |= NLC_HASBUF; + ptr = nd->nl_path; + + /* + * Go back up to the top to resolve any initial '/'s in the + * symlink. + */ + continue; + } + + /* + * Skip any slashes to get to the next element. If there + * are any slashes at all the current element must be a + * directory. If it isn't we break without incrementing + * ptr and fall through to the failure case below. + */ + while (*ptr == '/') { + if ((ncp->nc_flag & NCF_ISDIR) == 0) + break; + ++ptr; + } + + /* + * Continuation case: additional elements and the current + * element is a directory. + */ + if (*ptr && (ncp->nc_flag & NCF_ISDIR)) { + cache_drop(nd->nl_ncp); + cache_unlock(ncp); + nd->nl_ncp = ncp; + continue; + } + + /* + * Failure case: additional elements and the current element + * is not a directory + */ + if (*ptr) { + cache_put(ncp); + error = ENOTDIR; + break; + } + + /* + * XXX vnode canvmio (test in mmap(), read(), and write()) + */ + + /* + * Termination: no more elements. + */ + cache_drop(nd->nl_ncp); + nd->nl_ncp = ncp; + nd->nl_flags |= NLC_NCPISLOCKED; + error = 0; + break; + } + return(error); +} + +/* + * Read the contents of a symlink, allocate a path buffer out of the + * namei_zone and initialize the supplied nlcomponent with the result. + * + * If an error occurs no buffer will be allocated or returned in the nlc. + */ +int +nreadsymlink(struct nlookupdata *nd, struct namecache *ncp, + struct nlcomponent *nlc) +{ + struct iovec aiov; + struct uio auio; + int linklen; + int error; + char *cp; + + nlc->nlc_nameptr = NULL; + nlc->nlc_namelen = 0; + if (ncp->nc_vp == NULL) + return(ENOENT); + if ((error = vget(ncp->nc_vp, NULL, LK_SHARED, nd->nl_td)) != 0) + return(error); + cp = zalloc(namei_zone); + aiov.iov_base = cp; + aiov.iov_len = MAXPATHLEN; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_offset = 0; + auio.uio_rw = UIO_READ; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_td = nd->nl_td; + auio.uio_resid = MAXPATHLEN - 1; + error = VOP_READLINK(ncp->nc_vp, &auio, nd->nl_cred); + if (error) + goto fail; + linklen = MAXPATHLEN - auio.uio_resid; + if (varsym_enable) { + linklen = varsymreplace(cp, linklen, MAXPATHLEN - 1); + if (linklen < 0) { + error = ENAMETOOLONG; + goto fail; + } + } + cp[linklen] = 0; + nlc->nlc_nameptr = cp; + nlc->nlc_namelen = linklen; + vput(ncp->nc_vp); + return(0); +fail: + zfree(namei_zone, cp); + vput(ncp->nc_vp); + return(error); +} + diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index fac313d0e7..c1f969c309 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -37,7 +37,7 @@ * * @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94 * $FreeBSD: src/sys/kern/vfs_syscalls.c,v 1.151.2.18 2003/04/04 20:35:58 tegge Exp $ - * $DragonFly: src/sys/kern/vfs_syscalls.c,v 1.38 2004/08/17 18:57:32 dillon Exp $ + * $DragonFly: src/sys/kern/vfs_syscalls.c,v 1.39 2004/09/28 00:25:29 dillon Exp $ */ #include @@ -347,11 +347,13 @@ checkdirs(struct vnode *olddp) { struct filedesc *fdp; struct vnode *newdp; + struct mount *mp; struct proc *p; if (olddp->v_usecount == 1) return; - if (VFS_ROOT(olddp->v_mountedhere, &newdp)) + mp = olddp->v_mountedhere; + if (VFS_ROOT(mp, &newdp)) panic("mount: lost mount"); FOREACH_PROC_IN_SYSTEM(p) { fdp = p->p_fd; @@ -359,11 +361,15 @@ checkdirs(struct vnode *olddp) vrele(fdp->fd_cdir); vref(newdp); fdp->fd_cdir = newdp; + cache_drop(fdp->fd_ncdir); + fdp->fd_ncdir = cache_vptoncp(newdp); } if (fdp->fd_rdir == olddp) { vrele(fdp->fd_rdir); vref(newdp); fdp->fd_rdir = newdp; + cache_drop(fdp->fd_nrdir); + fdp->fd_nrdir = cache_vptoncp(newdp); } } if (rootvnode == olddp) { @@ -796,6 +802,7 @@ fchdir(struct fchdir_args *uap) VOP_UNLOCK(vp, NULL, 0, td); vrele(fdp->fd_cdir); fdp->fd_cdir = vp; + fdp->fd_ncdir = cache_vptoncp(vp); return (0); } @@ -811,7 +818,9 @@ kern_chdir(struct nameidata *nd) return (error); if ((error = checkvp_chdir(nd->ni_vp, td)) == 0) { vrele(fdp->fd_cdir); + cache_drop(fdp->fd_ncdir); fdp->fd_cdir = nd->ni_vp; + fdp->fd_ncdir = cache_vptoncp(nd->ni_vp); /* stopgap */ vref(fdp->fd_cdir); } NDFREE(nd, ~(NDF_NO_FREE_PNBUF | NDF_NO_VP_PUT)); @@ -911,11 +920,14 @@ kern_chroot(struct vnode *vp) */ if ((error = checkvp_chdir(vp, td)) == 0) { vrele(fdp->fd_rdir); + cache_drop(fdp->fd_nrdir); fdp->fd_rdir = vp; vref(fdp->fd_rdir); + fdp->fd_nrdir = cache_vptoncp(vp); if (fdp->fd_jdir == NULL) { fdp->fd_jdir = vp; vref(fdp->fd_jdir); + fdp->fd_njdir = cache_vptoncp(vp); } } return (error); diff --git a/sys/kern/vfs_vopops.c b/sys/kern/vfs_vopops.c index 932e35d6fa..bb3a19fa20 100644 --- a/sys/kern/vfs_vopops.c +++ b/sys/kern/vfs_vopops.c @@ -32,7 +32,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sys/kern/vfs_vopops.c,v 1.6 2004/09/26 06:00:05 dillon Exp $ + * $DragonFly: src/sys/kern/vfs_vopops.c,v 1.7 2004/09/28 00:25:29 dillon Exp $ */ #include @@ -121,6 +121,13 @@ VDESC_NO_OFFSET, \ VDESC_NO_OFFSET) +#define VNODEOP_DESC_INIT_NCP(name) \ + VNODEOP_DESC_INIT(name, 0, NULL, \ + VDESC_NO_OFFSET, \ + VDESC_NO_OFFSET, \ + VDESC_NO_OFFSET, \ + VDESC_NO_OFFSET) + #define VNODEOP_DESC_INIT_DVP_VPP_CNP(name) \ static int VOFFNAME(name)[] = { \ __offsetof(VARGSSTRUCT(name), a_dvp), \ @@ -165,6 +172,7 @@ VNODEOP_DESC_INIT_SIMPLE(default); VNODEOP_DESC_INIT_VP(islocked); +VNODEOP_DESC_INIT_NCP(resolve); VNODEOP_DESC_INIT_DVP_VPP_CNP(lookup); VNODEOP_DESC_INIT_DVP_VPP_CNP(cachedlookup); VNODEOP_DESC_INIT_DVP_VPP_CNP(create); @@ -275,6 +283,20 @@ vop_islocked(struct vop_ops *ops, struct vnode *vp, struct thread *td) return(error); } +int +vop_resolve(struct vop_ops *ops, struct namecache *ncp) +{ + struct vop_resolve_args ap; + int error; + + ap.a_head.a_desc = &vop_resolve_desc; + ap.a_head.a_ops = ops; + ap.a_ncp = ncp; + + DO_OPS(ops, error, &ap, vop_resolve); + return(error); +} + int vop_lookup(struct vop_ops *ops, struct vnode *dvp, struct vnode **vpp, struct componentname *cnp) @@ -1216,6 +1238,16 @@ vop_islocked_ap(struct vop_islocked_args *ap) return(error); } +int +vop_resolve_ap(struct vop_resolve_args *ap) +{ + int error; + + DO_OPS(ap->a_head.a_ops, error, ap, vop_resolve); + return(error); +} + + int vop_lookup_ap(struct vop_lookup_args *ap) { diff --git a/sys/sys/namecache.h b/sys/sys/namecache.h index 00695230d5..b9bf1ada02 100644 --- a/sys/sys/namecache.h +++ b/sys/sys/namecache.h @@ -62,7 +62,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sys/sys/namecache.h,v 1.8 2004/09/26 01:24:54 dillon Exp $ + * $DragonFly: src/sys/sys/namecache.h,v 1.9 2004/09/28 00:25:31 dillon Exp $ */ #ifndef _SYS_NAMECACHE_H_ @@ -105,6 +105,7 @@ struct namecache { u_char nc_nlen; /* The length of the name, 255 max */ u_char nc_unused; char *nc_name; /* Separately allocated seg name */ + int nc_error; int nc_timeout; /* compared against ticks, or 0 */ int nc_exlocks; /* namespace locking */ struct thread *nc_locktd; /* namespace locking */ @@ -122,6 +123,9 @@ typedef struct namecache *namecache_t; #define NCF_ROOT 0x0010 /* namecache root (static) */ #define NCF_HASHED 0x0020 /* namecache entry in hash table */ #define NCF_LOCKREQ 0x0040 +#define NCF_UNUSED080 0x0080 +#define NCF_ISSYMLINK 0x0100 /* represents a symlink */ +#define NCF_ISDIR 0x0200 /* represents a directory */ #define CINV_SELF 0x0001 /* invalidate a specific (dvp,vp) entry */ #define CINV_CHILDREN 0x0002 /* invalidate all children of vp */ @@ -133,20 +137,24 @@ typedef struct namecache *namecache_t; struct vop_lookup_args; struct componentname; +struct nlcomponent; struct mount; void cache_lock(struct namecache *ncp); void cache_unlock(struct namecache *ncp); +void cache_setvp(struct namecache *ncp, struct vnode *vp); +void cache_setunresolved(struct namecache *ncp); +void cache_get(struct namecache *ncp); void cache_put(struct namecache *ncp); -struct namecache *cache_nclookup(struct namecache *par, - struct componentname *cnp); - int cache_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp); void cache_mount(struct vnode *dvp, struct vnode *tvp); void cache_enter(struct vnode *dvp, struct namecache *par, struct vnode *vp, struct componentname *cnp); -void vfs_cache_setroot(struct vnode *vp); +struct namecache *cache_nlookup(struct namecache *par, struct nlcomponent *nlc); +struct namecache *vfs_cache_setroot(struct vnode *vp); +struct namecache *cache_vptoncp(struct vnode *vp); +int cache_resolve(struct namecache *ncp); void cache_purge(struct vnode *vp); void cache_purgevfs (struct mount *mp); void cache_drop(struct namecache *ncp); diff --git a/sys/sys/namei.h b/sys/sys/namei.h index 05fecbc3dd..9ed779292e 100644 --- a/sys/sys/namei.h +++ b/sys/sys/namei.h @@ -32,7 +32,7 @@ * * @(#)namei.h 8.5 (Berkeley) 1/9/95 * $FreeBSD: src/sys/sys/namei.h,v 1.29.2.2 2001/09/30 21:12:54 luigi Exp $ - * $DragonFly: src/sys/sys/namei.h,v 1.13 2004/09/26 20:14:21 dillon Exp $ + * $DragonFly: src/sys/sys/namei.h,v 1.14 2004/09/28 00:25:31 dillon Exp $ */ #ifndef _SYS_NAMEI_H_ @@ -209,6 +209,8 @@ NDINIT2(struct nameidata *ndp, u_long op, u_long flags, enum uio_seg segflg, void NDFREE (struct nameidata *, const uint); +extern int varsym_enable; + int namei (struct nameidata *ndp); int lookup (struct nameidata *ndp); int relookup (struct vnode *dvp, struct vnode **vpp, diff --git a/sys/sys/nlookup.h b/sys/sys/nlookup.h new file mode 100644 index 0000000000..b57c1b85e9 --- /dev/null +++ b/sys/sys/nlookup.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2004 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Matthew Dillon + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $DragonFly: src/sys/sys/nlookup.h,v 1.1 2004/09/28 00:25:31 dillon Exp $ + */ + +#ifndef _SYS_NLOOKUP_H_ +#define _SYS_NLOOKUP_H_ + +/* + * nlookup component + */ +struct nlcomponent { + char *nlc_nameptr; + int nlc_namelen; +}; + +/* + * Encapsulation of nlookup parameters + */ +struct nlookupdata { + struct namecache *nl_ncp; /* start-point and result */ + struct namecache *nl_rootncp; /* root directory */ + struct namecache *nl_jailncp; /* jail directory */ + + char *nl_path; /* path buffer */ + struct thread *nl_td; /* thread requesting the nlookup */ + struct ucred *nl_cred; /* credentials for nlookup */ + + int nl_flags; /* operations flags */ + int nl_loopcnt; /* symlinks encountered */ +}; + +#define NLC_FOLLOW CNP_FOLLOW +#define NLC_NOCROSSMOUNT CNP_NOCROSSMOUNT +#define NLC_HASBUF CNP_HASBUF +#define NLC_ISWHITEOUT CNP_ISWHITEOUT +#define NLC_WILLBEDIR CNP_WILLBEDIR +#define NLC_NCPISLOCKED CNP_LOCKLEAF + +#ifdef _KERNEL + +int nlookup_init(struct nlookupdata *, const char *, enum uio_seg, int); +void nlookup_done(struct nlookupdata *); +struct namecache *nlookup_simple(const char *str, enum uio_seg seg, + int niflags, int *error); +int nlookup(struct nlookupdata *); +int nreadsymlink(struct nlookupdata *nd, struct namecache *ncp, + struct nlcomponent *nlc); + + +#endif + +#endif /* !_SYS_NAMEI_H_ */ diff --git a/sys/sys/vfsops.h b/sys/sys/vfsops.h index 002232c087..6c8e6d370d 100644 --- a/sys/sys/vfsops.h +++ b/sys/sys/vfsops.h @@ -31,7 +31,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sys/sys/vfsops.h,v 1.5 2004/09/26 06:00:06 dillon Exp $ + * $DragonFly: src/sys/sys/vfsops.h,v 1.6 2004/09/28 00:25:31 dillon Exp $ */ /* @@ -93,6 +93,12 @@ struct vop_islocked_args { struct thread *a_td; }; +struct vop_resolve_args { + struct vop_generic_args a_head; + struct namecache *a_ncp; + struct componentname *a_cnp; +}; + struct vop_lookup_args { struct vop_generic_args a_head; struct vnode *a_dvp; @@ -520,6 +526,7 @@ struct vop_ops { #define vop_ops_first_field vop_default int (*vop_default)(struct vop_generic_args *); int (*vop_islocked)(struct vop_islocked_args *); + int (*vop_resolve)(struct vop_resolve_args *); int (*vop_lookup)(struct vop_lookup_args *); int (*vop_cachedlookup)(struct vop_cachedlookup_args *); int (*vop_create)(struct vop_create_args *); @@ -600,6 +607,7 @@ union vop_args_union { struct vop_generic_args vu_head; struct vop_generic_args vu_default; struct vop_islocked_args vu_islocked; + struct vop_resolve_args vu_resolve; struct vop_lookup_args vu_lookup; struct vop_cachedlookup_args vu_cachedlookup; struct vop_create_args vu_create; @@ -664,6 +672,7 @@ union vop_args_union { * in a message and dispatch it to the correct thread. */ int vop_islocked(struct vop_ops *ops, struct vnode *vp, struct thread *td); +int vop_resolve(struct vop_ops *ops, struct namecache *ncp); int vop_lookup(struct vop_ops *ops, struct vnode *dvp, struct vnode **vpp, struct componentname *cnp); int vop_cachedlookup(struct vop_ops *ops, struct vnode *dvp, @@ -778,6 +787,7 @@ int vop_vfsset(struct vop_ops *ops, int op, const char *opstr); */ int vop_vnoperate_ap(struct vop_generic_args *ap); int vop_islocked_ap(struct vop_islocked_args *ap); +int vop_resolve_ap(struct vop_resolve_args *ap); int vop_lookup_ap(struct vop_lookup_args *ap); int vop_cachedlookup_ap(struct vop_cachedlookup_args *ap); int vop_create_ap(struct vop_create_args *ap); @@ -837,6 +847,7 @@ int vop_vfsset_ap(struct vop_vfsset_args *ap); */ extern struct vnodeop_desc vop_default_desc; extern struct vnodeop_desc vop_islocked_desc; +extern struct vnodeop_desc vop_resolve_desc; extern struct vnodeop_desc vop_lookup_desc; extern struct vnodeop_desc vop_cachedlookup_desc; extern struct vnodeop_desc vop_create_desc; -- 2.41.0