Fix Plan 9 partial write() return values
[akaros.git] / kern / src / ns / sysfile.c
index 8a0d15b..16cd623 100644 (file)
@@ -1,4 +1,31 @@
-// INFERNO
+/* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+ * Portions Copyright © 1997-1999 Vita Nuova Limited
+ * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
+ *                                (www.vitanuova.com)
+ * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+ *
+ * Modified for the Akaros operating system:
+ * Copyright (c) 2013-2014 The Regents of the University of California
+ * Copyright (c) 2013-2015 Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
+
 #include <vfs.h>
 #include <kfs.h>
 #include <slab.h>
@@ -40,22 +67,20 @@ struct chan *fdtochan(struct fd_table *fdt, int fd, int mode, int chkmnt,
        if (!c) {
                /* We lost the info about why there was a problem (we used to track file
                 * group closed too, can add that in later). */
-               set_errno(EBADF);
-               error("Bad FD %d\n", fd);
+               error(EBADF, ERROR_FIXME);
        }
        if (chkmnt && (c->flag & CMSG)) {
                if (iref)
                        cclose(c);
-               set_errno(EBADF);
-               error(Ebadusefd);
+               error(EBADF, ERROR_FIXME);
        }
        if (mode < 0)
                return c;
        if ((mode & c->mode) != mode) {
                if (iref)
                        cclose(c);
-               set_errno(EBADF);
-               error("FD access mode failure: chan mode 0x%x, wanted 0x%x",
+               error(EBADF,
+                     "FD access mode failure: chan mode 0x%x, wanted 0x%x (opened with 0 instead of O_READ?)",
                      c->mode, mode);
        }
        return c;
@@ -78,7 +103,7 @@ long kchanio(void *vc, void *buf, int n, int mode)
        else if (mode == O_WRITE)
                r = devtab[c->type].write(c, buf, n, c->offset);
        else
-               error("kchanio: use only O_READ xor O_WRITE");
+               error(ENOSYS, "kchanio: use only O_READ xor O_WRITE");
 
        spin_lock(&c->lock);
        c->offset += r;
@@ -100,10 +125,10 @@ int openmode(uint32_t omode)
         * Note that we no longer convert OEXEC/O_EXEC to O_READ, and instead return
         * just the O_ACCMODE bits. */
        if (o >= (OTRUNC | OCEXEC | ORCLOSE | OEXEC))
-               error(Ebadarg);
+               error(EINVAL, ERROR_FIXME);
        o &= ~(OTRUNC | OCEXEC | ORCLOSE);
        if (o > OEXEC)
-               error(Ebadarg);
+               error(EINVAL, ERROR_FIXME);
        if (o == OEXEC)
                return OREAD;
        return o;
@@ -176,7 +201,7 @@ int syscreate(char *path, int mode, uint32_t perm)
        }
        fd = newfd(c, mode);    /* 9ns mode is the O_FLAGS and perm is glibc mode */
        if (fd < 0)
-               error(Enofd);
+               error(-fd, ERROR_FIXME);
        poperror();
 
        poperror();
@@ -196,12 +221,12 @@ int sysdup(int old)
        c = fdtochan(&current->open_files, old, -1, 0, 1);
        if (c->qid.type & QTAUTH) {
                cclose(c);
-               error(Eperm);
+               error(EPERM, ERROR_FIXME);
        }
        fd = newfd(c, 0);
        if (fd < 0) {
                cclose(c);
-               error(Enofd);
+               error(-fd, ERROR_FIXME);
        }
        poperror();
        return fd;
@@ -223,14 +248,14 @@ int sys_dup_to(struct proc *from_proc, unsigned int from_fd,
        c = fdtochan(&from_proc->open_files, from_fd, -1, 0, 1);
        if (c->qid.type & QTAUTH) {
                cclose(c);
-               error(Eperm);
+               error(EPERM, ERROR_FIXME);
        }
        ret = insert_obj_fdt(&to_proc->open_files, c, to_fd, 0, TRUE, FALSE);
        /* drop the ref from fdtochan.  if insert succeeded, there is one other ref
         * stored in the FDT */
        cclose(c);
        if (ret < 0)
-               error("Can't insert FD %d into FDG", to_fd);
+               error(EFAIL, "Can't insert FD %d into FDG", to_fd);
        poperror();
        return 0;
 }
@@ -251,7 +276,7 @@ char *sysfd2path(int fd)
                s = kzmalloc(c->name->len + 1, 0);
                if (s == NULL) {
                        cclose(c);
-                       error(Enomem);
+                       error(ENOMEM, ERROR_FIXME);
                }
                memmove(s, c->name->s, c->name->len + 1);
        }
@@ -290,7 +315,7 @@ int sysfauth(int fd, char *aname)
 
        fd = newfd(ac, 0);
        if (fd < 0)
-               error(Enofd);
+               error(-fd, ERROR_FIXME);
        poperror();     /* ac */
 
        poperror();
@@ -311,7 +336,7 @@ int sysfversion(int fd, unsigned int msize, char *vers, unsigned int arglen)
 
        /* check there's a NUL in the version string */
        if (arglen == 0 || memchr(vers, 0, arglen) == 0)
-               error(Ebadarg);
+               error(EINVAL, ERROR_FIXME);
 
        c = fdtochan(&current->open_files, fd, O_RDWR, 0, 1);
        if (waserror()) {
@@ -328,52 +353,6 @@ int sysfversion(int fd, unsigned int msize, char *vers, unsigned int arglen)
        return m;
 }
 
-int syspipe(int fd[2])
-{
-       ERRSTACK(1);
-       struct dev *d;
-       struct chan *c[2];
-       static char *names[] = { "data", "data1" };
-
-       d = &devtab[devno("pipe", 0)];
-       c[0] = 0;
-       c[1] = 0;
-       fd[0] = -1;
-       fd[1] = -1;
-       if (waserror()) {
-               /* need to remove from the fd table and make sure the chan is closed
-                * exactly once.  if fd[i] >= 0, then the fd is valid (or it was!) and
-                * the fd table has the only ref (newfd() currently decrefs/consumes the
-                * reference).  cclose() doesn't care if you pass it 0 (like kfree()). */
-               if (fd[0] >= 0)
-                       close_fd(&current->open_files, fd[0]);
-               else
-                       cclose(c[0]);
-               if (fd[1] >= 0)
-                       close_fd(&current->open_files, fd[1]);
-               else
-                       cclose(c[1]);
-               poperror();
-               return -1;
-       }
-       c[0] = namec("#pipe", Atodir, 0, 0);
-       c[1] = cclone(c[0]);
-       if (walk(&c[0], &names[0], 1, FALSE, NULL) < 0)
-               error(Egreg);
-       if (walk(&c[1], &names[1], 1, FALSE, NULL) < 0)
-               error(Egreg);
-       c[0] = d->open(c[0], O_RDWR);
-       c[1] = d->open(c[1], O_RDWR);
-       fd[0] = newfd(c[0], 0);
-       if (fd[0] < 0)
-               error(Enofd);
-       fd[1] = newfd(c[1], 0);
-       if (fd[1] < 0)
-               error(Enofd);
-       poperror();
-       return 0;
-}
-
 int sysfwstat(int fd, uint8_t * buf, int n)
 {
        ERRSTACK(2);
@@ -405,7 +384,7 @@ long bindmount(struct chan *c, char *old, int flag, char *spec)
        struct chan *c1;
 
        if (flag > MMASK || (flag & MORDER) == (MBEFORE | MAFTER))
-               error(Ebadarg);
+               error(EINVAL, ERROR_FIXME);
 
        c1 = namec(old, Amount, 0, 0);
        if (waserror()) {
@@ -523,32 +502,42 @@ int sysunmount(char *src_path, char *onto_path)
        return 0;
 }
 
-int sysopen(char *path, int vfs_flags)
+int sysopenat(int fromfd, char *path, int vfs_flags)
 {
-       ERRSTACK(2);
+       ERRSTACK(1);
        int fd;
-       struct chan *c;
+       struct chan *c = 0, *from = 0;
 
        if (waserror()) {
+               cclose(c);
                poperror();
                return -1;
        }
-
        openmode(vfs_flags);    /* error check only */
-       c = namec(path, Aopen, vfs_flags, 0);
-       if (waserror()) {
-               cclose(c);
-               nexterror();
+       if ((path[0] == '/') || (fromfd == AT_FDCWD)) {
+               c = namec(path, Aopen, vfs_flags, 0);
+       } 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 internally,
+                * and give us something new for c.  On error, namec_from will cclose
+                * from. */
+               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);
        }
        fd = newfd(c, vfs_flags);
        if (fd < 0)
-               error(Enofd);
-       poperror();
-
+               error(-fd, ERROR_FIXME);
        poperror();
        return fd;
 }
 
+int sysopen(char *path, int vfs_flags)
+{
+       return sysopenat(AT_FDCWD, path, vfs_flags);
+}
+
 long unionread(struct chan *c, void *va, long n)
 {
        ERRSTACK(1);
@@ -570,9 +559,9 @@ long unionread(struct chan *c, void *va, long n)
                /* Error causes component of union to be skipped */
                if (mount->to) {
                        /* normally we want to discard the error, but for our ghetto kdirent
-                        * hack, we need to repeat unionread if we saw a Eshort */
+                        * hack, we need to repeat unionread if we saw a ENODATA */
                        if (waserror()) {
-                               if (!strcmp(current_errstr(), Eshort)) {
+                               if (get_errno() == ENODATA) {
                                        runlock(&m->lock);
                                        qunlock(&c->umqlock);
                                        nexterror();
@@ -641,7 +630,7 @@ static long rread(int fd, void *va, long n, int64_t * offp)
        }
 
        if (n < 0)
-               error(Etoosmall);
+               error(EINVAL, ERROR_FIXME);
 
        dir = c->qid.type & QTDIR;
 
@@ -654,7 +643,7 @@ static long rread(int fd, void *va, long n, int64_t * offp)
                /* expecting only one dirent at a time, o/w we're busted */
                assert(n >= sizeof(struct kdirent));
                if (!c->buf) {
-                       c->buf=kmalloc(DIRREADSIZE, KMALLOC_WAIT);
+                       c->buf = kmalloc(DIRREADSIZE, MEM_WAIT);
                        c->bufused = 0;
                }
                /* Attempt to extract an M, in case there was some already */
@@ -685,7 +674,7 @@ static long rread(int fd, void *va, long n, int64_t * offp)
                } else
                        off = *offp;
                if (off < 0)
-                       error(Enegoff);
+                       error(EINVAL, ERROR_FIXME);
                if (off == 0) {
                        if (offp == NULL) {
                                spin_lock(&c->lock);
@@ -728,9 +717,9 @@ out:
 }
 
 /* Reads exactly n bytes from chan c, starting at its offset.  Can block, but if
- * we get 0 back too soon (EOF or error), then we'll error out with Eshort.
+ * we get 0 back too soon (EOF or error), then we'll error out with ENODATA.
  * That might need a little work - if there was a previous error, then we
- * clobbered it and only know Eshort but not why we completed early. */
+ * clobbered it and only know ENODATA but not why we completed early. */
 void read_exactly_n(struct chan *c, void *vp, long n)
 {
        char *p;
@@ -742,7 +731,7 @@ void read_exactly_n(struct chan *c, void *vp, long n)
                nn = devtab[c->type].read(c, p, n, c->offset);
                printd("readn: Got %d@%lld\n", nn, c->offset);
                if (nn == 0)
-                       error("%s: wanted %d, got %d", Eshort, want, total);
+                       error(ENODATA, "wanted %d, got %d", want, total);
                spin_lock(&c->lock);
                c->offset += nn;
                spin_unlock(&c->lock);
@@ -807,20 +796,14 @@ int64_t sysseek(int fd, int64_t off, int whence)
                cclose(c);
                nexterror();
        }
-
-       /* TODO: WTF is this?  Is pipe magically the only device that isn't
-        * seekable? */
-       if (!strcmp(devtab[c->type].name, "pipe"))
-               error(Eisstream);
-
        switch (whence) {
                case 0:
                        if (c->qid.type & QTDIR) {
                                if (off != 0)
-                                       error(Eisdir);
+                                       error(EISDIR, ERROR_FIXME);
                                unionrewind(c);
                        } else if (off < 0)
-                               error(Enegoff);
+                               error(EINVAL, ERROR_FIXME);
                        spin_lock(&c->lock);    /* lock for int64_t assignment */
                        c->offset = off;
                        spin_unlock(&c->lock);
@@ -828,12 +811,12 @@ int64_t sysseek(int fd, int64_t off, int whence)
 
                case 1:
                        if (c->qid.type & QTDIR)
-                               error(Eisdir);
+                               error(EISDIR, ERROR_FIXME);
                        spin_lock(&c->lock);    /* lock for read/write update */
                        off += c->offset;
                        if (off < 0) {
                                spin_unlock(&c->lock);
-                               error(Enegoff);
+                               error(EINVAL, ERROR_FIXME);
                        }
                        c->offset = off;
                        spin_unlock(&c->lock);
@@ -841,21 +824,21 @@ int64_t sysseek(int fd, int64_t off, int whence)
 
                case 2:
                        if (c->qid.type & QTDIR)
-                               error(Eisdir);
+                               error(EISDIR, ERROR_FIXME);
                        dir = chandirstat(c);
                        if (dir == NULL)
-                               error("internal error: stat error in seek");
+                               error(EFAIL, "internal error: stat error in seek");
                        off += dir->length;
                        kfree(dir);
                        if (off < 0)
-                               error(Enegoff);
+                               error(EINVAL, ERROR_FIXME);
                        spin_lock(&c->lock);    /* lock for read/write update */
                        c->offset = off;
                        spin_unlock(&c->lock);
                        break;
 
                default:
-                       error(Ebadarg);
+                       error(EINVAL, ERROR_FIXME);
                        break;
        }
        poperror();
@@ -867,12 +850,12 @@ int64_t sysseek(int fd, int64_t off, int whence)
 
 void validstat(uint8_t * s, int n, int slashok)
 {
-       
+
        int m;
        char buf[64];
 
        if (statcheck(s, n) < 0)
-               error(Ebadstat);
+               error(EINVAL, ERROR_FIXME);
        /* verify that name entry is acceptable */
        s += STATFIXLEN - 4 * BIT16SZ;  /* location of first string */
        /*
@@ -919,10 +902,10 @@ int sysfstat(int fd, uint8_t *buf, int n)
 
 int sysfstatakaros(int fd, struct kstat *ks)
 {
-       
+
        int n = 4096;
        uint8_t *buf;
-       buf = kmalloc(n, KMALLOC_WAIT);
+       buf = kmalloc(n, MEM_WAIT);
        n = sysfstat(fd, buf, n);
        if (n > 0) {
                convM2kstat(buf, n, ks);
@@ -958,10 +941,10 @@ int sysstat(char *path, uint8_t *buf, int n)
 
 int sysstatakaros(char *path, struct kstat *ks)
 {
-       
+
        int n = 4096;
        uint8_t *buf;
-       buf = kmalloc(n, KMALLOC_WAIT);
+       buf = kmalloc(n, MEM_WAIT);
        n = sysstat(path, buf, n);
        if (n > 0) {
                convM2kstat(buf, n, ks);
@@ -989,10 +972,10 @@ static long rwrite(int fd, void *va, long n, int64_t * offp)
                nexterror();
        }
        if (c->qid.type & QTDIR)
-               error(Eisdir);
+               error(EISDIR, ERROR_FIXME);
 
        if (n < 0)
-               error(Etoosmall);
+               error(EINVAL, ERROR_FIXME);
 
        if (offp == NULL) {
                /* append changes the offset to the end, and even if we fail later, this
@@ -1000,7 +983,7 @@ static long rwrite(int fd, void *va, long n, int64_t * offp)
                if (c->flag & O_APPEND) {
                        dir = chandirstat(c);
                        if (!dir)
-                               error("internal error: stat error in append write");
+                               error(EFAIL, "internal error: stat error in append write");
                        spin_lock(&c->lock);    /* legacy lock for int64 assignment */
                        c->offset = dir->length;
                        spin_unlock(&c->lock);
@@ -1022,7 +1005,7 @@ static long rwrite(int fd, void *va, long n, int64_t * offp)
                nexterror();
        }
        if (off < 0)
-               error(Enegoff);
+               error(EINVAL, ERROR_FIXME);
        m = devtab[c->type].write(c, va, n, off);
        poperror();
 
@@ -1036,7 +1019,7 @@ static long rwrite(int fd, void *va, long n, int64_t * offp)
        cclose(c);
 
        poperror();
-       return n;
+       return m;
 }
 
 long syswrite(int fd, void *va, long n)
@@ -1157,7 +1140,7 @@ struct dir *sysdirfstat(int fd)
 
 int sysdirwstat(char *name, struct dir *dir)
 {
-       
+
        uint8_t *buf;
        int r;
 
@@ -1171,7 +1154,7 @@ int sysdirwstat(char *name, struct dir *dir)
 
 int sysdirfwstat(int fd, struct dir *dir)
 {
-       
+
        uint8_t *buf;
        int r;
 
@@ -1185,7 +1168,7 @@ int sysdirfwstat(int fd, struct dir *dir)
 
 static long dirpackage(uint8_t * buf, long ts, struct kdirent **d)
 {
-       
+
        char *s;
        long ss, i, n, nn, m = 0;
 
@@ -1208,11 +1191,11 @@ static long dirpackage(uint8_t * buf, long ts, struct kdirent **d)
        }
 
        if (i != ts)
-               error("bad directory format");
+               error(EFAIL, "bad directory format");
 
        *d = kzmalloc(n * sizeof(**d) + ss, 0);
        if (*d == NULL)
-               error(Enomem);
+               error(ENOMEM, ERROR_FIXME);
 
        /*
         * then convert all buffers
@@ -1224,7 +1207,7 @@ static long dirpackage(uint8_t * buf, long ts, struct kdirent **d)
                if (nn >= n || /*convM2D */ convM2kdirent(&buf[i], m, *d + nn, s) != m) {
                        kfree(*d);
                        *d = NULL;
-                       error("bad directory entry");
+                       error(EFAIL, "bad directory entry");
                }
                nn++;
                s += m;
@@ -1246,7 +1229,7 @@ long sysdirread(int fd, struct kdirent **d)
        }
        buf = kzmalloc(DIRREADLIM, 0);
        if (buf == NULL)
-               error(Enomem);
+               error(ENOMEM, ERROR_FIXME);
        if (waserror()) {
                kfree(buf);
                nexterror();
@@ -1280,19 +1263,18 @@ int sysiounit(int fd)
 
 void print_chaninfo(struct chan *c)
 {
-       
-       char buf[64] = { 0 };
+
+       char buf[128] = { 0 };
        bool has_dev = c->type != -1;
-       if (has_dev && !devtab[c->type].chaninfo) {
-               printk("Chan type %d has no chaninfo!\n", c->type);
-               has_dev = FALSE;
-       }
-       printk("Chan pathname: %s ref %d, Dev: %s, Devinfo: %s",
+       bool has_chaninfo = has_dev && devtab[c->type].chaninfo;
+
+       printk("Chan flags: %p, pathname: %s, ref: %d, Dev: %s, Devinfo: %s",
+                  c->flag,
                   c->name ? c->name->s : "no cname",
                   kref_refcnt(&c->ref),
                   has_dev ? devtab[c->type].name : "no dev",
-                  has_dev ? devtab[c->type].chaninfo(c, buf, sizeof(buf)) : "");
-       if (!has_dev)
+                  has_chaninfo ? devtab[c->type].chaninfo(c, buf, sizeof(buf)) : "");
+       if (!has_chaninfo)
                printk("qid.path: %p\n", c->qid.path);
        printk("\n");
 }
@@ -1302,8 +1284,7 @@ void print_chaninfo(struct chan *c)
  * the pgrp. */
 int plan9setup(struct proc *new_proc, struct proc *parent, int flags)
 {
-       
-       struct proc *old_current;
+
        struct kref *new_dot_ref;
        ERRSTACK(1);
        if (waserror()) {
@@ -1313,17 +1294,11 @@ int plan9setup(struct proc *new_proc, struct proc *parent, int flags)
        }
        if (!parent) {
                /* We are probably spawned by the kernel directly, and have no parent to
-                * inherit from.
-                *
-                * TODO: One problem is namec wants a current set for things like
-                * genbuf.  So we'll use new_proc for this bootstrapping.  Note
-                * switch_to() also loads the cr3. */
+                * inherit from. */
                new_proc->pgrp = newpgrp();
-               old_current = switch_to(new_proc);
                new_proc->slash = namec("#root", Atodir, 0, 0);
                if (!new_proc->slash)
                        panic("no root device");
-               switch_back(new_proc, old_current);
                /* Want the name to be "/" instead of "#root" */
                cnameclose(new_proc->slash->name);
                new_proc->slash->name = newcname("/");
@@ -1408,30 +1383,35 @@ static bool cexternal_flags_differ(int set1, int set2, int flags)
 
 int fd_setfl(int fd, int flags)
 {
-       ERRSTACK(1);
+       ERRSTACK(2);
        struct chan *c;
+       int ret = 0;
 
        if (waserror()) {
                poperror();
                return -1;
        }
        c = fdtochan(&current->open_files, fd, -1, 0, 1);
+       if (waserror()) {
+               cclose(c);
+               nexterror();
+       }
        if (cexternal_flags_differ(flags, c->flag, O_CLOEXEC)) {
                /* TODO: The whole CCEXEC / O_CLOEXEC on 9ns needs work */
-               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);
-               error("can't set O_NONBLOCK, use a device-specific ctl command");
-       }
+               error(EINVAL, "can't toggle O_CLOEXEC with setfl");
+       }
+       if (cexternal_flags_differ(flags, c->flag, O_REMCLO))
+               error(EINVAL, "can't toggle O_REMCLO with setfl");
+       if (cexternal_flags_differ(flags, c->flag, O_PATH))
+               error(EINVAL, "can't toggle O_PATH with setfl");
+       /* Devices can do various prep work, including RPCs to other servers (#mnt)
+        * for a chan_ctl operation.  If they want to not support the new flags,
+        * they can throw an error. */
+       if (devtab[c->type].chan_ctl)
+               ret = devtab[c->type].chan_ctl(c, flags & CEXTERNAL_FLAGS);
        c->flag = (c->flag & ~CEXTERNAL_FLAGS) | (flags & CEXTERNAL_FLAGS);
+       poperror();
        cclose(c);
        poperror();
-       return 0;
+       return ret;
 }