Index: sys/sys/filedesc.h =================================================================== RCS file: /pub/NetBSD-CVS/src/sys/sys/filedesc.h,v retrieving revision 1.36 diff -u -r1.36 filedesc.h --- sys/sys/filedesc.h 23 Jul 2006 22:06:14 -0000 1.36 +++ sys/sys/filedesc.h 2 Mar 2007 20:37:15 -0000 @@ -35,6 +35,9 @@ #define _SYS_FILEDESC_H_ #include +#ifdef DOTDOT +#include +#endif /* * This structure is used for the management of descriptors. It may be @@ -90,6 +93,9 @@ struct vnode *cwdi_rdir; /* root directory */ u_short cwdi_cmask; /* mask for file creation */ u_short cwdi_refcnt; /* reference count */ +#ifdef DOTDOT + char cwdi_cwd[PATH_MAX+1]; /* current path */ +#endif struct simplelock cwdi_slock; /* mutex */ }; @@ -151,6 +157,10 @@ void cwdshare(struct proc *, struct proc *); void cwdunshare(struct proc *); void cwdfree(struct cwdinfo *); +#ifdef DOTDOT +int cwdcat(struct lwp *, const char *); +void cwdclean(struct proc *p); +#endif /* DOTDOT */ #define GETCWD_CHECK_ACCESS 0x0001 int getcwd_common(struct vnode *, struct vnode *, char **, char *, int, int, struct lwp *); Index: sys/kern/kern_descrip.c =================================================================== RCS file: /pub/NetBSD-CVS/src/sys/kern/kern_descrip.c,v retrieving revision 1.151 diff -u -r1.151 kern_descrip.c --- sys/kern/kern_descrip.c 17 Feb 2007 22:31:42 -0000 1.151 +++ sys/kern/kern_descrip.c 2 Mar 2007 20:37:16 -0000 @@ -1054,6 +1054,9 @@ VREF(cwdi->cwdi_rdir); cwdi->cwdi_cmask = p->p_cwdi->cwdi_cmask; cwdi->cwdi_refcnt = 1; +#ifdef DOTDOT + strcpy(cwdi->cwdi_cwd,p->p_cwdi->cwdi_cwd); +#endif return (cwdi); } @@ -1110,6 +1113,159 @@ pool_put(&cwdi_pool, cwdi); } +#ifdef DOTDOT +/* + * Modify a cwdi_cwd, called by sys_chdir + * + * Return values: + * 0: no problem OR using "." as cwdi_cwd + * (probably that the new cwdi_cwd is too long) + * else: error + * XXX error management + */ +int +cwdcat(struct lwp *l, const char *path) +{ + struct cwdinfo *cwdi = l->l_proc->p_cwdi; + char *bp, *bend; + struct vnode *dvp; + int error; + + if (*path == '/') { + strlcpy(cwdi->cwdi_cwd, path, sizeof(cwdi->cwdi_cwd)); + return 0; + } + + if (*cwdi->cwdi_cwd != '/') { /*cwd[0] == '.'*/ + /* If cwd isn't a good path (after a fchdir, chroot...) */ + bp = bend = cwdi->cwdi_cwd + sizeof(cwdi->cwdi_cwd); + *--bp = '\0'; + + error = vget(cwdi->cwdi_cdir, LK_EXCLUSIVE | LK_RETRY); + if (error != 0) + return error; + + error = cache_revlookup(cwdi->cwdi_cdir, &dvp, &bp, + cwdi->cwdi_cwd); + vput(cwdi->cwdi_cdir); + if (error != 0) + return (error == -1 ? ENOENT : error); + + error = vget(dvp, 0); + if (error != 0) + return error; + + *--bp = '/'; + + error = getcwd_common(dvp, NULL, &bp, + cwdi->cwdi_cwd, MAXPATHLEN/2, + GETCWD_CHECK_ACCESS, l); + if (error) + return -1; + + memcpy(cwdi->cwdi_cwd, bp, bend-bp); + cwdi->cwdi_cwd[bend-bp] = '\0'; + } + + /* + * If the new path seems to be too long, maybe a cwdclean + * will reduce the path... + * If not goto toolong will set cwdi_cwd to "." and getcwd will + * use the old method. + */ + if ((strlen(cwdi->cwdi_cwd)+1+strlen(path)+1) > sizeof(cwdi->cwdi_cwd)) + cwdclean(l->l_proc); /*XXX error management*/ + + if (strlcat(cwdi->cwdi_cwd, "/", sizeof(cwdi->cwdi_cwd)) + >= sizeof(cwdi->cwdi_cwd)) + goto toolong; + + if (strlcat(cwdi->cwdi_cwd, path, sizeof(cwdi->cwdi_cwd)) + >= sizeof(cwdi->cwdi_cwd)) + goto toolong; + + return 0; +toolong: + strcpy(cwdi->cwdi_cwd, "."); + return 0; +} + +/* + * Clean name called by sys___getcwd: + * For each char: + * - Replace /./ by / + * - If /../: + * - If .. is the first directory, replace by / + * - Else delete it and the last directory + * - Replace // by / + * If the path is null (after a /../..) put / as path. + * After: delete final / if present + */ +#define cwdlen strlen(cwd) +/* + * If the pointer is at the beginning of the cwd string, we have to + * restart the loop at this point to analyse the new first char. + * If we are not at the beginning, we have just to decrement cwd. + */ +#define contatbegin if ((cwd+1) == cwdi->cwdi_cwd) continue +void +cwdclean(struct proc *p) +{ + struct cwdinfo *cwdi = p->p_cwdi; + char *cwd = cwdi->cwdi_cwd; + char *lastslash = cwd; + + do { + /* Delete /./ and /. in end of line */ + if (cwdlen > 1) + if (*cwd == '/' && *(cwd+1) == '.' && + (*(cwd+2) == '/' || *(cwd+2) == '\0')) { + strlcpy(cwd, cwd+2, cwdlen); + cwd--; + contatbegin; + } + + /* Replace or delete /../ and /.. in end of line */ + if (cwdlen > 2) + if (*cwd == '/' && *(cwd+1) == '.' && *(cwd+2) == '.' + && (*(cwd+3) == '/' || *(cwd+3) == '\0')) { + if (cwd == cwdi->cwdi_cwd) { /* Begin: delete */ + strlcpy(cwd, cwd+3, cwdlen); + } else { /* Not at the beginning: replace */ + strlcpy(lastslash,cwd+3, cwdlen); + cwd = lastslash-1; + contatbegin; + /* Look for the first / before */ + for (lastslash = cwd; + *lastslash != '/'; + lastslash--); + } + } + + /* Delete double / */ + if (*cwd == '/' && *(cwd+1) == '/') { + strlcpy(cwd, cwd+1, cwdlen); + cwd--; + contatbegin; + } + + if (*cwd == '/') + lastslash = cwd; + } while (*(++cwd) != '\0'); + + /* If we got a string "/.." or "/." */ + if (*cwdi->cwdi_cwd == '\0') + strcpy(cwdi->cwdi_cwd,"/"); + + /* Delete final / */ + if ((cwd-1) > cwdi->cwdi_cwd) + if (*(cwd-1) == '/') + *(cwd-1) = '\0'; +} +#undef cwdlen +#undef contatbegin +#endif /* DOTDOT */ + /* * Create an initial filedesc structure, using the same current and root * directories as p. Index: sys/kern/init_main.c =================================================================== RCS file: /pub/NetBSD-CVS/src/sys/kern/init_main.c,v retrieving revision 1.295 diff -u -r1.295 init_main.c --- sys/kern/init_main.c 17 Feb 2007 22:31:42 -0000 1.295 +++ sys/kern/init_main.c 2 Mar 2007 20:37:17 -0000 @@ -544,6 +544,7 @@ VREF(cwdi0.cwdi_cdir); VOP_UNLOCK(rootvnode, 0); cwdi0.cwdi_rdir = NULL; + strcpy(cwdi0.cwdi_cwd, "."); /* * Now that root is mounted, we can fixup initproc's CWD Index: sys/kern/vfs_syscalls.c =================================================================== RCS file: /pub/NetBSD-CVS/src/sys/kern/vfs_syscalls.c,v retrieving revision 1.302 diff -u -r1.302 vfs_syscalls.c --- sys/kern/vfs_syscalls.c 18 Feb 2007 20:36:36 -0000 1.302 +++ sys/kern/vfs_syscalls.c 2 Mar 2007 20:37:19 -0000 @@ -481,7 +481,7 @@ * Scan all active processes to see if any of them have a current * or root directory onto which the new filesystem has just been * mounted. If so, replace them with the new mount point. - */ +*/ void checkdirs(struct vnode *olddp) { @@ -502,6 +502,9 @@ vrele(cwdi->cwdi_cdir); VREF(newdp); cwdi->cwdi_cdir = newdp; +#ifdef DOTDOT + strcpy(cwdi->cwdi_cwd,"."); +#endif } if (cwdi->cwdi_rdir == olddp) { vrele(cwdi->cwdi_rdir); @@ -1023,6 +1026,10 @@ vrele(cwdi->cwdi_cdir); cwdi->cwdi_cdir = vp; + +#ifdef DOTDOT + strcpy(cwdi->cwdi_cwd,"."); +#endif out: FILE_UNUSE(fp, l); return (error); @@ -1078,6 +1085,11 @@ if (cwdi->cwdi_rdir != NULL) vrele(cwdi->cwdi_rdir); cwdi->cwdi_rdir = vp; + +#ifdef DOTDOT + strcpy(cwdi->cwdi_cwd,"."); +#endif + out: FILE_UNUSE(fp, l); return (error); @@ -1103,6 +1115,10 @@ if ((error = change_dir(&nd, l)) != 0) return (error); vrele(cwdi->cwdi_cdir); +#ifdef DOTDOT + /* XXX Error management */ + cwdcat(l,SCARG(uap, path)); +#endif cwdi->cwdi_cdir = nd.ni_vp; return (0); } @@ -1149,6 +1165,9 @@ VREF(vp); cwdi->cwdi_cdir = vp; } +#ifdef DOTDOT + strcpy(cwdi->cwdi_cwd,"."); +#endif return (0); } Index: sys/kern/vfs_getcwd.c =================================================================== RCS file: /pub/NetBSD-CVS/src/sys/kern/vfs_getcwd.c,v retrieving revision 1.35 diff -u -r1.35 vfs_getcwd.c --- sys/kern/vfs_getcwd.c 9 Feb 2007 21:55:32 -0000 1.35 +++ sys/kern/vfs_getcwd.c 2 Mar 2007 20:37:20 -0000 @@ -520,17 +520,29 @@ syscallarg(size_t) length; } */ *uap = v; - int error; - char *path; - char *bp, *bend; - int len = SCARG(uap, length); + int error; + char *path; + char *bp, *bend; + int len = SCARG(uap, length); int lenused; + struct cwdinfo *cwdi = l->l_proc->p_cwdi; if (len > MAXPATHLEN * 4) len = MAXPATHLEN * 4; else if (len < 2) return ERANGE; +#ifdef DOTDOT + if (*cwdi->cwdi_cwd == '/') { /*cwd[0] == '.'*/ + if (len < (strlen(cwdi->cwdi_cwd)+1)) + return ERANGE; + cwdclean(l->l_proc); + error = copyout(cwdi->cwdi_cwd, SCARG(uap, bufp), + strlen(cwdi->cwdi_cwd)+1); + return error; + } +#endif /* DOTDOT */ + path = (char *)malloc(len, M_TEMP, M_WAITOK); if (!path) return ENOMEM; @@ -544,7 +556,7 @@ * Since each entry takes up at least 2 bytes in the output buffer, * limit it to N/2 vnodes for an N byte buffer. */ - error = getcwd_common(l->l_proc->p_cwdi->cwdi_cdir, NULL, &bp, path, + error = getcwd_common(cwdi->cwdi_cdir, NULL, &bp, path, len/2, GETCWD_CHECK_ACCESS, l); if (error)