Allow fchdir() from non-O_PATH fds

If you chdir to an open FD, later when you did something like create a
file from DOT, we'd panic.  The FD in current->dot was opened and not
O_PATH, which you can't walk from (see devclone()).

The regular sys_chdir does a namec(Atodir), which does not call the
device's open, so that chan is a kernel-internal, non-open chan (to use
the parlance of devclone()).

The current fix works for most cases.  If you do something like
dfd = open(some_dir, O_READ); unlink(some_dir); fchdir(dfd);, the lookup
will fail.  Using O_PATH instead of O_READ should work.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
diff --git a/kern/src/ns/sysfile.c b/kern/src/ns/sysfile.c
index c1a6780..dc6cf1f 100644
--- a/kern/src/ns/sysfile.c
+++ b/kern/src/ns/sysfile.c
@@ -177,7 +177,27 @@
 	}
 	c = fdtochan(&current->open_files, fd, -1, 0, 1);
 	poperror();
-	set_dot(c);
+
+	/* This is a little hokey.  Ideally, we'd only allow O_PATH fds to be
+	 * fchdir'd.  Linux/POSIX lets you do arbitrary FDs.  Luckily, we stored the
+	 * name when we walked (__namec_from), so we should be able to recreate the
+	 * chan.  Using namec() with channame() is a more heavy-weight cclone(), but
+	 * also might have issues if the chan has since been removed or the
+	 * namespace is otherwise different from when the original fd/chan was first
+	 * created. */
+	if (c->flag & O_PATH) {
+		set_dot(c);
+		return 0;
+	}
+	if (waserror()) {
+		cclose(c);
+		poperror();
+		return -1;
+	}
+	syschdir(channame(c));
+	cclose(c);
+	poperror();
+
 	return 0;
 }