Support O_CREATE with SYS_openat
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 12 Jun 2019 16:27:56 +0000 (12:27 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 12 Jun 2019 16:27:56 +0000 (12:27 -0400)
Previously, we would attempt a sysopenat(), then when it failed, do
syscreate().  However, the syscreate() can't handle an 'at' FD.  The
file would be created in the current directory, as if it was a normal
open.

The simplest thing was to push the O_CREATE fallback business into
openat itself.  That cleaned up the syscall.c code, and eventually will
lead to the removal of syscreate().  Currently, it is still used by
mkdir, which also cannot handle 'at' FDs.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/include/ns.h
kern/src/ns/sysfile.c
kern/src/process.c
kern/src/syscall.c

index 5425e47..68733cd 100644 (file)
@@ -1088,7 +1088,7 @@ int sysbind(char *new, char *old, int flags);
 int syssymlink(char *new_path, char *old_path);
 int sysmount(int fd, int afd, char *old, int flags, char *spec);
 int sysunmount(char *old, char *new);
-int sysopenat(int dirfd, char *path, int vfs_flags);
+int sysopenat(int dirfd, char *path, int vfs_flags, int perm);
 int sysopen(char *path, int vfs_flags);
 long unionread(struct chan *c, void *va, long n);
 void read_exactly_n(struct chan *c, void *vp, long n);
index 7f91293..a0a5130 100644 (file)
@@ -556,20 +556,33 @@ int sysunmount(char *src_path, char *onto_path)
        return 0;
 }
 
-int sysopenat(int fromfd, char *path, int vfs_flags)
+int sysopenat(int fromfd, char *path, int vfs_flags, int perm)
 {
        ERRSTACK(1);
        int fd;
        struct chan *c = 0, *from = 0;
+       int open_or_create = Aopen;
 
+       /* O_EXCL must be O_CREATE (checked in syscall.c); we can skip the Aopen
+        * call.  Note namec(Acreate) checks O_EXCL internally. */
+       if (vfs_flags & O_EXCL)
+               open_or_create = Acreate;
        if (waserror()) {
+               if (open_or_create == Aopen && vfs_flags & O_CREATE
+                   && get_errno() == ENOENT) {
+                       open_or_create = Acreate;
+                       /* Don't poperror - we're keeping ourselves at the
+                        * current waserror() depth.  Returns thrice! */
+                       goto retry;
+               }
                cclose(c);
                poperror();
                return -1;
        }
-       openmode(vfs_flags);    /* error check only */
+retry:
+       openmode(vfs_flags & ~O_EXCL);  /* error check only; O_EXCL okay here */
        if ((path[0] == '/') || (fromfd == AT_FDCWD)) {
-               c = namec(path, Aopen, vfs_flags, 0, NULL);
+               c = namec(path, open_or_create, vfs_flags, perm, NULL);
        } else {
                /* We don't cclose from.  namec_from will convert it to the new
                 * chan during the walk process (c).  It'll probably close from
@@ -578,7 +591,8 @@ int sysopenat(int fromfd, char *path, int vfs_flags)
                from = fdtochan(&current->open_files, fromfd, -1, FALSE, TRUE);
                if (!(from->flag & O_PATH))
                        error(EINVAL, "Cannot openat from a non-O_PATH FD");
-               c = namec_from(from, path, Aopen, vfs_flags, 0, NULL);
+               c = namec_from(from, path, open_or_create, vfs_flags, perm,
+                              NULL);
        }
        /* Devices should catch this, but just in case, we'll catch it. */
        if ((c->qid.type & QTSYMLINK) && (vfs_flags & O_NOFOLLOW))
@@ -592,7 +606,7 @@ int sysopenat(int fromfd, char *path, int vfs_flags)
 
 int sysopen(char *path, int vfs_flags)
 {
-       return sysopenat(AT_FDCWD, path, vfs_flags);
+       return sysopenat(AT_FDCWD, path, vfs_flags, 0);
 }
 
 long unionread(struct chan *c, void *va, long n)
index 4e3f78c..80795f3 100644 (file)
@@ -349,11 +349,11 @@ static void proc_open_stdfds(struct proc *p)
         * empty or incomplete.  These syscalls shouldn't access user memory,
         * especially considering how we're probably in the boot pgdir. */
        current = p;
-       fd = sysopenat(AT_FDCWD, "#cons/stdin", O_READ);
+       fd = sysopenat(AT_FDCWD, "#cons/stdin", O_READ, 0);
        assert(fd == 0);
-       fd = sysopenat(AT_FDCWD, "#cons/stdout", O_WRITE);
+       fd = sysopenat(AT_FDCWD, "#cons/stdout", O_WRITE, 0);
        assert(fd == 1);
-       fd = sysopenat(AT_FDCWD, "#cons/stderr", O_WRITE);
+       fd = sysopenat(AT_FDCWD, "#cons/stderr", O_WRITE, 0);
        assert(fd == 2);
        current = old_current;
 }
index e22171b..66b2601 100644 (file)
@@ -1803,27 +1803,18 @@ static intreg_t sys_openat(struct proc *p, int fromfd, const char *path,
                          oflag);
                return -1;
        }
+       if (oflag & O_EXCL && !(oflag & O_CREATE)) {
+               set_error(EINVAL, "Cannot open O_EXCL without O_CREATE");
+               return -1;
+       }
        t_path = copy_in_path(p, path, path_l);
        if (!t_path)
                return -1;
        sysc_save_str("open %s at fd %d", t_path, fromfd);
-       fd = sysopenat(fromfd, t_path, oflag);
-       /* successful lookup with CREATE and EXCL is an error */
-       if (fd != -1) {
-               if ((oflag & O_CREATE) && (oflag & O_EXCL)) {
-                       set_errno(EEXIST);
-                       sysclose(fd);
-                       free_path(p, t_path);
-                       return -1;
-               }
-       } else {
-               if (oflag & O_CREATE) {
-                       mode &= ~p->umask;
-                       mode &= S_PMASK;
-                       static_assert(!(DMMODE_BITS & S_PMASK));
-                       fd = syscreate(t_path, oflag, mode);
-               }
-       }
+       mode &= ~p->umask;
+       mode &= S_PMASK;
+       static_assert(!(DMMODE_BITS & S_PMASK));
+       fd = sysopenat(fromfd, t_path, oflag, mode);
        free_path(p, t_path);
        printd("File %s Open, fd=%d\n", path, fd);
        return fd;