Added set_error() API to have a single point of entry for setting for errno and errstr.
[akaros.git] / kern / src / ns / dev.c
index ec2a883..a2db8bf 100644 (file)
@@ -23,16 +23,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 +45,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 +59,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 +76,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 +94,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 +118,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 +185,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 +216,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(ENODEV, NULL);
+                                       set_error(ENOENT, NULL);
                                        goto Done;
                                case 0:
                                        printd("DEVWALK continue, i was %d\n", i);
@@ -260,13 +278,13 @@ 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(ENODEV, NULL);
                        case 0:
                                printd("DEVSTAT got 0\n");
                                break;
@@ -278,7 +296,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,10 +329,9 @@ 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;
                                }
-                               assert(dsz >= MIN_M_BUF_SZ);
                                m += dsz;
                                d += dsz;
                                break;
@@ -325,24 +342,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,
@@ -368,8 +386,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;
@@ -378,7 +396,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)
@@ -388,7 +406,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();
@@ -416,31 +434,31 @@ 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
 
 char *devchaninfo(struct chan *chan, char *ret, size_t ret_l)
 {
-       snprintf(ret, ret_l, "qid.path: %p, qid.type: %02x\n", chan->qid.path,
+       snprintf(ret, ret_l, "qid.path: %p, qid.type: %02x", chan->qid.path,
                         chan->qid.type);
        return ret;
 }
@@ -452,7 +470,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)