Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/sys/kern/vfs_lookup.c,v rcsdiff: /ftp/cvs/cvsroot/src/sys/kern/vfs_lookup.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.84 retrieving revision 1.84.4.4 diff -u -p -r1.84 -r1.84.4.4 --- src/sys/kern/vfs_lookup.c 2007/02/22 06:34:45 1.84 +++ src/sys/kern/vfs_lookup.c 2007/08/20 21:27:43 1.84.4.4 @@ -1,4 +1,4 @@ -/* $NetBSD: vfs_lookup.c,v 1.84 2007/02/22 06:34:45 thorpej Exp $ */ +/* $NetBSD: vfs_lookup.c,v 1.84.4.4 2007/08/20 21:27:43 ad Exp $ */ /* * Copyright (c) 1982, 1986, 1989, 1993 @@ -37,9 +37,8 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: vfs_lookup.c,v 1.84 2007/02/22 06:34:45 thorpej Exp $"); +__KERNEL_RCSID(0, "$NetBSD: vfs_lookup.c,v 1.84.4.4 2007/08/20 21:27:43 ad Exp $"); -#include "opt_ktrace.h" #include "opt_systrace.h" #include "opt_magiclinks.h" @@ -58,10 +57,8 @@ __KERNEL_RCSID(0, "$NetBSD: vfs_lookup.c #include #include #include - -#ifdef KTRACE #include -#endif + #ifdef SYSTRACE #include #endif @@ -196,53 +193,6 @@ symlink_magic(struct proc *p, char *cp, #undef MATCH #undef SUBSTITUTE -int -pathname_get(const char *dirp, enum uio_seg segflg, pathname_t *path) -{ - int error; - - if (dirp == NULL) - return (EFAULT); - - *path = malloc(sizeof(struct pathname_internal), M_TEMP, - M_ZERO|M_WAITOK); - - if (segflg == UIO_USERSPACE) { - (*path)->pathbuf = PNBUF_GET(); - error = copyinstr(dirp, (*path)->pathbuf, MAXPATHLEN, - NULL); - if (error) { - PNBUF_PUT((*path)->pathbuf); - free(*path, M_TEMP); - *path = NULL; - return (error); - } - (*path)->needfree = true; - } else { - (*path)->pathbuf = __UNCONST(dirp); - (*path)->needfree = false; - } - - return (0); -} - -const char * -pathname_path(pathname_t path) -{ - KASSERT(path != NULL); - return (path->pathbuf); -} - -void -pathname_put(pathname_t path) -{ - if (path != NULL) { - if (path->pathbuf != NULL && path->needfree) - PNBUF_PUT(path->pathbuf); - free(path, M_TEMP); - } -} - /* * Convert a pathname into a pointer to a locked vnode. * @@ -282,7 +232,6 @@ namei(struct nameidata *ndp) if (cnp->cn_flags & OPMASK) panic("namei: flags contaminated with nameiops"); #endif - cwdi = cnp->cn_lwp->l_proc->p_cwdi; /* * Get a buffer for the name to be translated, and copy the @@ -290,6 +239,7 @@ namei(struct nameidata *ndp) */ if ((cnp->cn_flags & HASBUF) == 0) cnp->cn_pnbuf = PNBUF_GET(); + emul_retry: if (ndp->ni_segflg == UIO_SYSSPACE) error = copystr(ndp->ni_dirp, cnp->cn_pnbuf, MAXPATHLEN, &ndp->ni_pathlen); @@ -310,31 +260,72 @@ namei(struct nameidata *ndp) } ndp->ni_loopcnt = 0; -#ifdef KTRACE - if (KTRPOINT(cnp->cn_lwp->l_proc, KTR_NAMEI)) - ktrnamei(cnp->cn_lwp, cnp->cn_pnbuf); -#endif -#ifdef SYSTRACE - if (ISSET(cnp->cn_lwp->l_proc->p_flag, PK_SYSTRACE)) - systrace_namei(ndp); -#endif - /* - * Get starting point for the translation. + * Get root directory for the translation. */ - if ((ndp->ni_rootdir = cwdi->cwdi_rdir) == NULL) - ndp->ni_rootdir = rootvnode; + cwdi = cnp->cn_lwp->l_proc->p_cwdi; + rw_enter(&cwdi->cwdi_lock, RW_READER); + dp = cwdi->cwdi_rdir; + if (dp == NULL) + dp = rootvnode; + ndp->ni_rootdir = dp; + /* * Check if starting from root directory or current directory. */ if (cnp->cn_pnbuf[0] == '/') { - dp = ndp->ni_rootdir; - VREF(dp); + if (cnp->cn_flags & TRYEMULROOT) { + if (cnp->cn_flags & EMULROOTSET) { + /* Called from (eg) emul_find_interp() */ + dp = ndp->ni_erootdir; + } else { + if (cwdi->cwdi_edir == NULL + || (cnp->cn_pnbuf[1] == '.' + && cnp->cn_pnbuf[2] == '.' + && cnp->cn_pnbuf[3] == '/')) { + ndp->ni_erootdir = NULL; + } else { + dp = cwdi->cwdi_edir; + ndp->ni_erootdir = dp; + } + } + } else + ndp->ni_erootdir = NULL; } else { dp = cwdi->cwdi_cdir; - VREF(dp); + ndp->ni_erootdir = NULL; + } + VREF(dp); + rw_exit(&cwdi->cwdi_lock); + + if (ktrpoint(KTR_NAMEI)) { + if (ndp->ni_erootdir != NULL) { + /* + * To make any sense, the trace entry need to have the + * text of the emulation path prepended. + * Usually we can get this from the current process, + * but when called from emul_find_interp() it is only + * in the exec_package - so we get it passed in ni_next + * (this is a hack). + */ + const char *emul_path; + if (cnp->cn_flags & EMULROOTSET) + emul_path = ndp->ni_next; + else + emul_path = cnp->cn_lwp->l_proc->p_emul->e_path; + ktrnamei2(emul_path, strlen(emul_path), + cnp->cn_pnbuf, ndp->ni_pathlen); + } else + ktrnamei(cnp->cn_pnbuf, ndp->ni_pathlen); } + +#ifdef SYSTRACE + if (ISSET(cnp->cn_lwp->l_proc->p_flag, PK_SYSTRACE)) + systrace_namei(ndp); +#endif + vn_lock(dp, LK_EXCLUSIVE | LK_RETRY); + /* Loop through symbolic links */ for (;;) { if (!dp->v_mount) { /* Give up if the directory is no longer mounted */ @@ -349,6 +340,11 @@ namei(struct nameidata *ndp) if (ndp->ni_dvp) { vput(ndp->ni_dvp); } + if (ndp->ni_erootdir != NULL) { + /* Retry the whole thing from the normal root */ + cnp->cn_flags &= ~TRYEMULROOT; + goto emul_retry; + } PNBUF_PUT(cnp->cn_pnbuf); return (error); } @@ -431,11 +427,19 @@ badlink: */ if (cnp->cn_pnbuf[0] == '/') { vput(dp); - dp = ndp->ni_rootdir; + /* Keep absolute symbolic links inside emulation root */ + dp = ndp->ni_erootdir; + if (dp == NULL || (cnp->cn_pnbuf[1] == '.' + && cnp->cn_pnbuf[2] == '.' + && cnp->cn_pnbuf[3] == '/')) { + ndp->ni_erootdir = NULL; + dp = ndp->ni_rootdir; + } VREF(dp); vn_lock(dp, LK_EXCLUSIVE | LK_RETRY); } } + /* Failed to process a symbolic link */ KASSERT(ndp->ni_dvp != ndp->ni_vp); vput(ndp->ni_dvp); vput(ndp->ni_vp); @@ -554,26 +558,9 @@ lookup(struct nameidata *ndp) /* * If we've exhausted the path name, then just return the - * current node. If the caller requested the parent node (i.e. - * it's a CREATE, DELETE, or RENAME), and we don't have one - * (because this is the root directory), then we must fail. + * current node. */ if (cnp->cn_nameptr[0] == '\0') { - if (ndp->ni_dvp == NULL && cnp->cn_nameiop != LOOKUP) { - switch (cnp->cn_nameiop) { - case CREATE: - error = EEXIST; - break; - case DELETE: - case RENAME: - error = EBUSY; - break; - default: - KASSERT(0); - } - vput(dp); - goto bad; - } ndp->ni_vp = dp; cnp->cn_flags |= ISLASTCN; goto terminal; @@ -652,7 +639,9 @@ dirloop: * 1. If at root directory (e.g. after chroot) * or at absolute root directory * then ignore it so can't get out. - * 1a. If we have somehow gotten out of a jail, warn + * 1a. If at the root of the emulation filesystem go to the real + * root. So "/../" is always absolute. + * 1b. If we have somehow gotten out of a jail, warn * and also ignore it so we can't get farther out. * 2. If this vnode is the root of a mounted * filesystem, then replace it with the @@ -693,7 +682,7 @@ dirloop: goto nextname; } } - if ((dp->v_flag & VROOT) == 0 || + if ((dp->v_vflag & VV_ROOT) == 0 || (cnp->cn_flags & NOCROSSMOUNT)) break; tdp = dp; @@ -721,7 +710,7 @@ unionlookup: printf("not found\n"); #endif /* NAMEI_DIAGNOSTIC */ if ((error == ENOENT) && - (dp->v_flag & VROOT) && + (dp->v_vflag & VV_ROOT) && (dp->v_mount->mnt_flag & MNT_UNION)) { tdp = dp; dp = dp->v_mount->mnt_vnodecovered; @@ -851,17 +840,54 @@ nextname: } terminal: + if (dp == ndp->ni_erootdir) { + /* + * We are about to return the emulation root. + * This isn't a good idea because code might repeatedly + * lookup ".." until the file matches that returned + * for "/" and loop forever. + * So convert it to the real root. + */ + if (ndp->ni_dvp == dp) + vrele(dp); + else + if (ndp->ni_dvp != NULL) + vput(ndp->ni_dvp); + ndp->ni_dvp = NULL; + vput(dp); + dp = ndp->ni_rootdir; + VREF(dp); + vn_lock(dp, LK_EXCLUSIVE | LK_RETRY); + ndp->ni_vp = dp; + } + + /* + * If the caller requested the parent node (i.e. + * it's a CREATE, DELETE, or RENAME), and we don't have one + * (because this is the root directory), then we must fail. + */ + if (ndp->ni_dvp == NULL && cnp->cn_nameiop != LOOKUP) { + switch (cnp->cn_nameiop) { + case CREATE: + error = EEXIST; + break; + case DELETE: + case RENAME: + error = EBUSY; + break; + default: + KASSERT(0); + } + vput(dp); + goto bad; + } /* - * Disallow directory write attempts on read-only file systems. + * Disallow directory write attempts on read-only lookups. + * Prefers EEXIST over EROFS for the CREATE case. */ if (rdonly && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) { - - /* - * Disallow directory write attempts on read-only - * file systems. - */ error = EROFS; if (dp != ndp->ni_dvp) { vput(dp); @@ -959,7 +985,7 @@ relookup(struct vnode *dvp, struct vnode #endif /* - * Check for read-only file systems. + * Check for read-only lookups. */ if (rdonly && cnp->cn_nameiop != LOOKUP) { error = EROFS;