Support O_PATH for open() (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 10 Sep 2015 19:02:36 +0000 (15:02 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 28 Sep 2015 19:14:00 +0000 (15:14 -0400)
Opening a path with O_PATH, which is also supported in Linux, walks to
an FD, grabs the chan, and refers to an object, but it does not fully
open the object for I/O.  Our O_PATH is similar to Linux's and can
eventually be used for openat().

The particular meaning of O_PATH will depend on the devices.  At a bare
minimum, FDs opened with O_PATH are not able to be used for I/O; they
have no access permissions.  They both refer to a location (path) in the
namespace, as well as refer to the object underneath.

In earlier versions of this code, I just did a namec() without the
device open or any device op.  You'd have an FD that pointed to a chan.
However, that chan was one of the kernel-only intermediate chans (9ns
uses them all the time).  The chan had a QID and a device ID, which
uniquely identifies the object, but that object had no permanence.  In
the case of #I, it did not grab a reference to the conversation, so it
was possible that the object would disappear!  (Or in the case of #I,
just be reused for another conversation).  Considering we are supposed
to be able to perform some non-I/O syscalls on the FD, e.g. fstat, that
seemed like a bad idea.  Plus, the reason I was doing this was to tap a
Qlisten FD (don't open it, since that'd give us a new conversation).

Calling the device open may be slightly different than the Linux
implementation, but it appears necessary due the 9ns device model.  See
https://lwn.net/Articles/356029/ for a discussion (and note the flag
where FSs could request an open op for O_NODE).

Anyway, the basic infrastructure is in place for O_PATH calls.  Devices
can do something special if they want.  If they do nothing, O_PATH will
be set, and there will be no ACC_MODE flags set on the file/chan.  The
same goes for the VFS, but I don't plan to do anything special there.

If a device does something weird on an open, such as opening a clone in
 #I, you'll get a conversation back and a chan for the Qctl, but the FD
for that chan will have no read or write permissions.

Reinstall your kernel headers.

kern/include/ns.h
kern/include/ros/fs.h
kern/src/ns/sysfile.c
kern/src/syscall.c

index 13ecb1f..6561bcb 100644 (file)
@@ -319,6 +319,7 @@ enum {
            O_REMCLO       | /* remove on close (also, maybe should be on FD) */
            O_APPEND       | /* append on write */
            O_NONBLOCK     | /* don't block, can't be set via setfl */
+           O_PATH         | /* path open, just the name, no I/O */
            0),
 };
 
index e32f39c..025c077 100644 (file)
@@ -57,6 +57,7 @@ struct kstat {
 #define O_FSYNC                        O_SYNC
 #define O_ASYNC                        00020000
 #define O_DIRECT               00040000        /* Direct disk access. */
+#define O_PATH                 00100000        /* Path only, no I/O */
 #define O_DIRECTORY            00200000        /* Must be a directory. */
 #define O_NOFOLLOW             00400000        /* Do not follow links. */
 #define O_NOATIME              01000000        /* Do not set atime. */
index 511fefc..2e6445e 100644 (file)
@@ -1418,6 +1418,10 @@ int fd_setfl(int fd, int flags)
                set_errno(EINVAL);
                error("can't toggle O_CLOEXEC with setfl");
        }
+       if (cexternal_flags_differ(flags, c->flag, O_PATH)) {
+               set_errno(EINVAL);
+               error("can't toggle O_PATH with setfl");
+       }
        if (cexternal_flags_differ(flags, c->flag, O_NONBLOCK)) {
                /* If we want to let them toggle NONBLOCK, it'd require a device op */
                set_errno(EINVAL);
index b3933a8..c85e0ec 100644 (file)
@@ -1371,9 +1371,15 @@ static intreg_t sys_open(struct proc *p, const char *path, size_t path_l,
        struct systrace_record *t = pcpui->cur_kthread->trace;
        int fd = -1;
        struct file *file;
+       char *t_path;
 
        printd("File %s Open attempt oflag %x mode %x\n", path, oflag, mode);
-       char *t_path = copy_in_path(p, path, path_l);
+       if ((oflag & O_PATH) && (oflag & O_ACCMODE)) {
+               set_errno(EINVAL);
+               set_errstr("Cannot open O_PATH with any I/O perms (O%o)", oflag);
+               return -1;
+       }
+       t_path = copy_in_path(p, path, path_l);
        if (!t_path)
                return -1;
        if (t) {