Add the Inferno license to files we got from Inferno
[akaros.git] / kern / src / ns / dev.c
index 7fb59d0..8b23bbd 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>
@@ -23,16 +50,16 @@ void mkqid(struct qid *q, int64_t path, uint32_t vers, int type)
        q->path = path;
 }
 
-int devno(int c, int user)
+int devno(const char *name, int user)
 {
        int i;
 
        for (i = 0; &devtab[i] < __devtabend; i++) {
-               if (devtab[i].dc == c)
+               if (!strcmp(devtab[i].name, name))
                        return i;
        }
        if (user == 0)
-               panic("devno %c 0x%u", c, c);
+               panic("Lookup of dev :%s: failed", name);
 
        return -1;
 }
@@ -45,7 +72,7 @@ devdir(struct chan *c, struct qid qid, char *n,
        if (c->flag & CMSG)
                qid.type |= QTMOUNT;
        db->qid = qid;
-       db->type = devtab[c->type].dc;
+       db->type = c->type;     /* used to use the dev's dc here */
        db->dev = c->dev;
        db->mode = perm;
        db->mode |= qid.type << 24;
@@ -59,6 +86,9 @@ devdir(struct chan *c, struct qid qid, char *n,
 
 /*
  * the zeroth element of the table MUST be the directory itself for ..
+ * Any entry with qid vers of -1 will return 0, indicating that the value is
+ * valid but there is nothing there continue walk.
+ * TODO(gvdl): Update akaros devgen man page.
 */
 int
 devgen(struct chan *c, char *unused_char_p_t, struct dirtab *tab, int ntab,
@@ -73,8 +103,10 @@ devgen(struct chan *c, char *unused_char_p_t, struct dirtab *tab, int ntab,
                        return -1;
                tab += i;
        }
-       devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
-       return 1;
+       int ret = (tab->qid.vers == -1)? 0 : 1;
+       if (ret)
+               devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
+       return ret;
 }
 
 void devreset(void)
@@ -89,19 +121,21 @@ void devshutdown(void)
 {
 }
 
-struct chan *devattach(int tc, char *spec)
+struct chan *devattach(const char *name, char *spec)
 {
        struct chan *c;
        char *buf;
+       size_t buflen;
 
        c = newchan();
        mkqid(&c->qid, 0, 0, QTDIR);
-       c->type = devno(tc, 0);
+       c->type = devno(name, 0);
        if (spec == NULL)
                spec = "";
-       /* 2 for #c, 1 for \0 */
-       buf = kzmalloc(2 + strlen(spec) + 1, KMALLOC_WAIT);
-       snprintf(buf, sizeof(buf), "#%c%s", tc, spec);
+       /* 1 for #, 1 for ., 1 for \0 */
+       buflen = strlen(name) + strlen(spec) + 3;
+       buf = kzmalloc(buflen, KMALLOC_WAIT);
+       snprintf(buf, sizeof(buf), "#%s.%s", name, spec);
        c->name = newcname(buf);
        kfree(buf);
        return c;
@@ -111,11 +145,23 @@ struct chan *devclone(struct chan *c)
 {
        struct chan *nc;
 
-       if (c->flag & COPEN)
-               panic("clone of open file type %c\n", devtab[c->type].dc);
+       /* In plan 9, you couldn't clone an open chan.  We're allowing it, possibly
+        * foolishly.  The new chan is a non-open, "kernel internal" chan.  Note
+        * that c->flag isn't set, for instance.  c->mode is, which might be a
+        * problem.  The newchan should eventually have a device's open called on
+        * it, at which point it upgrades from a kernel internal chan to one that
+        * can refer to an object in the device (e.g. grab a refcnt on a
+        * conversation in #ip).
+        *
+        * Either we allow devclones of open chans, or O_PATH walks do not open a
+        * file.  It's nice to allow the device to do something for O_PATH, but
+        * perhaps that is not critical.  However, if we can't clone an opened chan,
+        * then we can *only* openat from an FD that is O_PATH, which is not the
+        * spec (and not as useful). */
+       if ((c->flag & COPEN) && !(c->flag & O_PATH))
+               panic("clone of non-O_PATH open file type %s\n", devtab[c->type].name);
 
        nc = newchan();
-
        nc->type = c->type;
        nc->dev = c->dev;
        nc->mode = c->mode;
@@ -166,7 +212,7 @@ struct walkqid *devwalk(struct chan *c,
        for (j = 0; j < nname; j++) {
                if (!(nc->qid.type & QTDIR)) {
                        if (j == 0)
-                               error(Enotdir);
+                               error(ENOTDIR, NULL);
                        goto Done;
                }
                n = name[j];
@@ -197,10 +243,9 @@ Accept:
                                        printd("DEVWALK -1, i was %d, want path %p\n", i,
                                                   c->qid.path);
 Notfound:
-                                       set_errno(ENOENT);
                                        if (j == 0)
-                                               error(Enonexist);
-                                       set_errstr(Enonexist);
+                                               error(ENOENT, NULL);
+                                       set_error(ENOENT, NULL);
                                        goto Done;
                                case 0:
                                        printd("DEVWALK continue, i was %d\n", i);
@@ -260,13 +305,12 @@ devstat(struct chan *c, uint8_t * db, int n,
                                        devdir(c, c->qid, elem, 0, eve, DMDIR | 0555, &dir);
                                        n = convD2M(&dir, db, n);
                                        if (n == 0)
-                                               error(Ebadarg);
+                                               error(EINVAL, NULL);
                                        return n;
                                }
-                               printd("DEVSTAT fails:%c %llu\n", devtab[c->type].dc,
+                               printd("DEVSTAT fails:%s %llu\n", devtab[c->type].name,
                                           c->qid.path);
-                               set_errno(ENOENT);
-                               error(Enonexist);
+                               error(ENOENT, NULL);
                        case 0:
                                printd("DEVSTAT got 0\n");
                                break;
@@ -278,7 +322,7 @@ devstat(struct chan *c, uint8_t * db, int n,
                                                dir.mode |= DMMOUNT;
                                        n = convD2M(&dir, db, n);
                                        if (n == 0)
-                                               error(Ebadarg);
+                                               error(EINVAL, NULL);
                                        return n;
                                }
                                break;
@@ -311,7 +355,7 @@ devdirread(struct chan *c, char *d, long n,
                                dsz = convD2M(&dir[0], (uint8_t *) d, n - m);
                                if (dsz <= BIT16SZ) {   /* <= not < because this isn't stat; read is stuck */
                                        if (m == 0)
-                                               error(Eshort);
+                                               error(ENODATA, NULL);
                                        return m;
                                }
                                m += dsz;
@@ -324,24 +368,25 @@ devdirread(struct chan *c, char *d, long n,
 }
 
 /*
- * error(Eperm) if open permission not granted for up->env->user.
+ * error(EPERM, NULL) if open permission not granted for up->env->user.
  */
 void devpermcheck(char *fileuid, uint32_t perm, int omode)
 {
-       uint32_t t;
-       static int access[] = { 0400, 0200, 0600, 0100 };
-
+       int rwx;
+       /* select user, group, or other from the traditional rwxrwxrwx, shifting
+        * into the upper-most position */
        if (strcmp(current->user, fileuid) == 0)
                perm <<= 0;
        else if (strcmp(current->user, eve) == 0)
                perm <<= 3;
        else
                perm <<= 6;
-
-       t = access[omode & 3];
-       if ((t & perm) != t)
-               error("%s: devpermcheck(%s,0x%x,0x%x) failed", Eperm, fileuid, perm,
-                         omode);
+       /* translate omode into things like S_IRUSR (just one set of rwx------).
+        * Plan 9 originally only returned 0400 0200 0600 and 0100 here; it didn't
+        * seem to handle O_EXEC being mixed readable or writable. */
+       rwx = omode_to_rwx(omode);
+       if ((rwx & perm) != rwx)
+               error(EPERM, "devpermcheck(%s, 0%o, 0%o) failed", fileuid, perm, omode);
 }
 
 struct chan *devopen(struct chan *c, int omode, struct dirtab *tab, int ntab,
@@ -367,8 +412,8 @@ struct chan *devopen(struct chan *c, int omode, struct dirtab *tab, int ntab,
        }
 Return:
        c->offset = 0;
-       if ((c->qid.type & QTDIR) && !IS_RDONLY(omode))
-               error("Tried opening dir with non-read-only mode %o", omode);
+       if ((c->qid.type & QTDIR) && (omode & O_WRITE))
+               error(EACCES, "Tried opening dir with non-read-only mode %o", omode);
        c->mode = openmode(omode);
        c->flag |= COPEN;
        return c;
@@ -377,7 +422,7 @@ Return:
 void
 devcreate(struct chan *c, char *unused_char_p_t, int unused_int, uint32_t u)
 {
-       error(Eperm);
+       error(EPERM, NULL);
 }
 
 struct block *devbread(struct chan *c, long n, uint32_t offset)
@@ -387,7 +432,7 @@ struct block *devbread(struct chan *c, long n, uint32_t offset)
 
        bp = allocb(n);
        if (bp == 0)
-               error(Enomem);
+               error(ENOMEM, NULL);
        if (waserror()) {
                freeb(bp);
                nexterror();
@@ -415,24 +460,24 @@ long devbwrite(struct chan *c, struct block *bp, uint32_t offset)
 
 void devremove(struct chan *c)
 {
-       error(Eperm);
+       error(EPERM, NULL);
 }
 
 int devwstat(struct chan *c, uint8_t * unused_uint8_p_t, int i)
 {
-       error(Eperm);
+       error(EPERM, NULL);
        return 0;
 }
 
 void devpower(int i)
 {
-       error(Eperm);
+       error(EPERM, NULL);
 }
 
 #if 0
 int devconfig(int unused_int, char *c, DevConf *)
 {
-       error(Eperm);
+       error(EPERM, NULL);
        return 0;
 }
 #endif
@@ -451,7 +496,7 @@ void validwstatname(char *name)
 {
        validname(name, 0);
        if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
-               error(Efilename);
+               error(EINVAL, NULL);
 }
 
 struct dev *devbyname(char *name)