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 <sys/lock.h>
+#ifdef DOTDOT
+#include <sys/syslimits.h>
+#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)
