Remove uses of errno_to_string()
[akaros.git] / kern / src / ns / chan.c
index 6060e8e..c488d1a 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>
@@ -89,16 +116,20 @@ void chandevreset(void)
 {
        int i;
 
-       for (i = 0; &devtab[i] < __devtabend; i++)
-               devtab[i].reset();
+       for (i = 0; &devtab[i] < __devtabend; i++) {
+               if (devtab[i].reset)
+                       devtab[i].reset();
+       }
 }
 
 void chandevinit(void)
 {
        int i;
 
-       for (i = 0; &devtab[i] < __devtabend; i++)
-               devtab[i].init();
+       for (i = 0; &devtab[i] < __devtabend; i++) {
+               if (devtab[i].init)
+                       devtab[i].init();
+       }
 }
 
 void chandevshutdown(void)
@@ -107,8 +138,10 @@ void chandevshutdown(void)
 
        /* shutdown in reverse order */
        for (i = 0; &devtab[i] < __devtabend; i++) ;
-       for (i--; i >= 0; i--)
-               devtab[i].shutdown();
+       for (i--; i >= 0; i--) {
+               if (devtab[i].shutdown)
+                       devtab[i].shutdown();
+       }
 }
 
 static void chan_release(struct kref *kref)
@@ -169,6 +202,8 @@ struct chan *newchan(void)
        c->mqid.vers = 0;
        c->mqid.type = 0;
        c->name = 0;
+       c->buf = NULL;
+       c->mountpoint = NULL;
        return c;
 }
 
@@ -258,6 +293,11 @@ void chanfree(struct chan *c)
        }
 
        cnameclose(c->name);
+       if (c->buf)
+               kfree(c->buf);
+       c->buf = NULL;
+       c->bufused = 0;
+       c->ateof = 0;
 
        spin_lock(&(&chanalloc)->lock);
        c->next = chanalloc.free;
@@ -276,6 +316,13 @@ void cclose(struct chan *c)
        kref_put(&c->ref);
 }
 
+/* convenience wrapper for interposition.  if you do use this, don't forget
+ * about the kref_get_not_zero in plan9setup() */
+void chan_incref(struct chan *c)
+{
+       kref_get(&c->ref, 1);
+}
+
 /*
  * Make sure we have the only copy of c.  (Copy on write.)
  */
@@ -336,8 +383,9 @@ struct mhead *newmhead(struct chan *from)
 
        mh = kzmalloc(sizeof(struct mhead), 0);
        kref_init(&mh->ref, mh_release, 1);
+       rwinit(&mh->lock);
        mh->from = from;
-       kref_get(&from->ref, 1);
+       chan_incref(from);
 
 /*
        n = from->name->len;
@@ -358,7 +406,7 @@ int cmount(struct chan *new, struct chan *old, int flag, char *spec)
        struct mount *nm, *f, *um, **h;
 
        if (QTDIR & (old->qid.type ^ new->qid.type))
-               error(Emount);
+               error(EINVAL, ERROR_FIXME);
 
        if (old->umh)
                printd("cmount old extra umh\n");
@@ -366,7 +414,7 @@ int cmount(struct chan *new, struct chan *old, int flag, char *spec)
        order = flag & MORDER;
 
        if ((old->qid.type & QTDIR) == 0 && order != MREPL)
-               error(Emount);
+               error(EINVAL, ERROR_FIXME);
 
        mh = new->umh;
 
@@ -382,13 +430,13 @@ int cmount(struct chan *new, struct chan *old, int flag, char *spec)
         * work.  The check of mount->mflag catches things like
         *  mount fd /root
         *  bind -c /root /
-        * 
+        *
         * This is far more complicated than it should be, but I don't
         * see an easier way at the moment.     -rsc
         */
        if ((flag & MCREATE) && mh && mh->mount
                && (mh->mount->next || !(mh->mount->mflag & MCREATE)))
-               error(Emount);
+               error(EEXIST, ERROR_FIXME);
 
        pg = current->pgrp;
        wlock(&pg->ns);
@@ -471,7 +519,7 @@ void cunmount(struct chan *mnt, struct chan *mounted)
                printd("cunmount newp extra umh %p has %p\n", mnt, mnt->umh);
 
        /*
-        * It _can_ happen that mounted->umh is non-NULL, 
+        * It _can_ happen that mounted->umh is non-NULL,
         * because mounted is the result of namec(Aopen)
         * (see sysfile.c:/^sysunmount).
         * If we open a union directory, it will have a umh.
@@ -491,7 +539,7 @@ void cunmount(struct chan *mnt, struct chan *mounted)
 
        if (m == 0) {
                wunlock(&pg->ns);
-               error(Eunmount);
+               error(ENOENT, ERROR_FIXME);
        }
 
        wlock(&m->lock);
@@ -530,7 +578,7 @@ void cunmount(struct chan *mnt, struct chan *mounted)
        }
        wunlock(&m->lock);
        wunlock(&pg->ns);
-       error(Eunion);
+       error(ENOENT, ERROR_FIXME);
 }
 
 struct chan *cclone(struct chan *c)
@@ -540,7 +588,7 @@ struct chan *cclone(struct chan *c)
 
        wq = devtab[c->type].walk(c, NULL, NULL, 0);
        if (wq == NULL)
-               error("clone failed");
+               error(EFAIL, "clone failed");
        nc = wq->clone;
        kfree(wq);
        nc->name = c->name;
@@ -575,7 +623,7 @@ findmount(struct chan **cp,
                        }
                        if (*cp != NULL)
                                cclose(*cp);
-                       kref_get(&m->mount->to->ref, 1);
+                       chan_incref(m->mount->to);
                        *cp = m->mount->to;
                        runlock(&m->lock);
                        return 1;
@@ -623,7 +671,7 @@ struct chan *undomount(struct chan *c, struct cname *name)
                                        if (strcmp(t->head->from->name->s, name->s) != 0)
                                                continue;
                                        nc = t->head->from;
-                                       kref_get(&nc->ref, 1);
+                                       chan_incref(nc);
                                        cclose(c);
                                        c = nc;
                                        break;
@@ -640,31 +688,30 @@ struct chan *undomount(struct chan *c, struct cname *name)
  * Either walks all the way or not at all.  No partial results in *cp.
  * *nerror is the number of names to display in an error message.
  */
-static char Edoesnotexist[] = "does not exist";
-int walk(struct chan **cp, char **names, int nnames, int nomount, int *nerror)
+int walk(struct chan **cp, char **names, int nnames, bool can_mount, int *nerror)
 {
        int dev, dotdot, i, n, nhave, ntry, type;
-       struct chan *c, *nc;
+       struct chan *c, *nc, *lastmountpoint = NULL;
        struct cname *cname;
        struct mount *f;
        struct mhead *mh, *nmh;
        struct walkqid *wq;
 
        c = *cp;
-       kref_get(&c->ref, 1);
+       chan_incref(c);
        cname = c->name;
        kref_get(&cname->ref, 1);
        mh = NULL;
 
        /*
         * While we haven't gotten all the way down the path:
-        *    1. step through a mount po int unused_int, if any
+        *    1. step through a mount point, if any
         *    2. send a walk request for initial dotdot or initial prefix without dotdot
         *    3. move to the first mountpoint along the way.
         *    4. repeat.
         *
         * An invariant is that each time through the loop, c is on the undomount
-        * side of the mount po int unused_int, and c's name is cname.
+        * side of the mount point, and c's name is cname.
         */
        for (nhave = 0; nhave < nnames; nhave += n) {
                if ((c->qid.type & QTDIR) == 0) {
@@ -672,7 +719,7 @@ int walk(struct chan **cp, char **names, int nnames, int nomount, int *nerror)
                                *nerror = nhave;
                        cnameclose(cname);
                        cclose(c);
-                       set_errstr(Enotdir);
+                       set_error(ENOTDIR, ERROR_FIXME);
                        if (mh != NULL)
                                putmhead(mh);
                        return -1;
@@ -692,7 +739,7 @@ int walk(struct chan **cp, char **names, int nnames, int nomount, int *nerror)
                        }
                }
 
-               if (!dotdot && !nomount)
+               if (!dotdot && can_mount)
                        domount(&c, &mh);
 
                type = c->type;
@@ -700,7 +747,7 @@ int walk(struct chan **cp, char **names, int nnames, int nomount, int *nerror)
 
                if ((wq = devtab[type].walk(c, NULL, names + nhave, ntry)) == NULL) {
                        /* try a union mount, if any */
-                       if (mh && !nomount) {
+                       if (mh && can_mount) {
                                /*
                                 * mh->mount == c, so start at mh->mount->next
                                 */
@@ -737,7 +784,7 @@ int walk(struct chan **cp, char **names, int nnames, int nomount, int *nerror)
                        n = 1;
                } else {
                        nc = NULL;
-                       if (!nomount)
+                       if (can_mount)
                                for (i = 0; i < wq->nqid && i < ntry - 1; i++)
                                        if (findmount(&nc, &nmh, type, dev, wq->qid[i]))
                                                break;
@@ -748,11 +795,11 @@ int walk(struct chan **cp, char **names, int nnames, int nomount, int *nerror)
                                        if (wq->nqid == 0 || (wq->qid[wq->nqid - 1].type & QTDIR)) {
                                                if (nerror)
                                                        *nerror = nhave + wq->nqid + 1;
-                                               set_errstr(Edoesnotexist);
+                                               set_error(ENOENT, "walk failed");
                                        } else {
                                                if (nerror)
                                                        *nerror = nhave + wq->nqid;
-                                               set_errstr(Enotdir);
+                                               set_error(ENOTDIR, "walk failed");
                                        }
                                        kfree(wq);
                                        if (mh != NULL)
@@ -766,6 +813,7 @@ int walk(struct chan **cp, char **names, int nnames, int nomount, int *nerror)
                                        cclose(wq->clone);
                                        wq->clone = NULL;
                                }
+                               lastmountpoint = nc;
                                n = i + 1;
                        }
                        for (i = 0; i < n; i++)
@@ -790,6 +838,7 @@ int walk(struct chan **cp, char **names, int nnames, int nomount, int *nerror)
 
        cnameclose(c->name);
        c->name = cname;
+       c->mountpoint = lastmountpoint;
 
        cclose(*cp);
        *cp = c;
@@ -821,7 +870,7 @@ struct chan *createdir(struct chan *c, struct mhead *m)
                        return nc;
                }
        }
-       error(Enocreate);
+       error(EPERM, ERROR_FIXME);
        poperror();
        return 0;
 }
@@ -937,85 +986,26 @@ void *memrchr(void *va, int c, long n)
  * correct Chan* but with an incorrect struct cname attached.
  * Since the functions that open Aaccess (sysstat, syswstat, sys_stat)
  * do not use the struct cname*, this avoids an unnecessary clone.
- */
-struct chan *namec(char *aname, int amode, int omode, uint32_t perm)
+ *
+ * Acreatechan will never open. It will do all the tests and return a chan
+ * for the directory where an open will succeed.
+ *
+ * The classic namec() is broken into a front end to get the starting point and
+ * a __namec_from, which does the guts of the lookup.  */
+static struct chan *__namec_from(struct chan *c, char *aname, int amode,
+                                 int omode, uint32_t perm, bool can_mount)
 {
        ERRSTACK(2);
-       int n, prefix, len, t, nomount, npath;
-       struct chan *c, *cnew;
+       int len, npath;
+       struct chan *cnew;
        struct cname *cname;
        Elemlist e;
        struct mhead *m;
        char tmperrbuf[ERRMAX];
        int saved_errno;
-       char *name;
        // Rune r;
 
-       name = aname;
-       if (name[0] == '\0')
-               error("empty file name");
-       validname(name, 1);
-
-       /*
-        * Find the starting off point (the current slash, the root of
-        * a device tree, or the current dot) as well as the name to
-        * evaluate starting there.
-        */
-       nomount = 0;
-       switch (name[0]) {
-               case '/':
-                       c = current->slash;
-                       if (!c)
-                               panic("no slash!");
-                       kref_get(&c->ref, 1);
-                       break;
-
-               case '#':
-                       nomount = 1;
-                       get_cur_genbuf()[0] = '\0';
-                       n = 0;
-                       while (*name != '\0' && (*name != '/' || n < 2)) {
-                               if (n >= sizeof(get_cur_genbuf()) - 1)
-                                       error(Efilename);
-                               get_cur_genbuf()[n++] = *name++;
-                       }
-                       get_cur_genbuf()[n] = '\0';
-#if 0
-                       n = chartorune(&r, get_cur_genbuf() + 1) + 1;
-                       if (r == 'M')
-                               error(Enoattach);
-#endif
-                       if (get_cur_genbuf()[1] == 'M')
-                               error(Enoattach);
-                       /*
-                        *  the nodevs exceptions are
-                        *  |  it only gives access to pipes you create
-                        *  e  this process's environment
-                        *  s  private file2chan creation space
-                        *  D private secure sockets name space
-                        *  a private TLS name space
-                        */
-                       if (current->pgrp->nodevs &&
-                               //          (utfrune("|esDa", r) == NULL
-                               ((strchr("|esDa", get_cur_genbuf()[1]) == NULL)
-                                || (get_cur_genbuf()[1] == 's' // || r == 's'
-                                        && get_cur_genbuf()[n] != '\0')))
-                               error(Enoattach);
-                       t = devno( /*r */ get_cur_genbuf()[1], 1);
-                       if (t == -1)
-                               error(Ebadsharp);
-                       /* genbuf + 2: skips the # and X, just the spec left */
-                       c = devtab[t].attach(get_cur_genbuf() + 2);
-                       break;
-
-               default:
-                       c = current->dot;
-                       if (!c)
-                               panic("no dot!");
-                       kref_get(&c->ref, 1);
-                       break;
-       }
-       prefix = name - aname;
+       static_assert(!(CINTERNAL_FLAGS & CEXTERNAL_FLAGS));
 
        e.name = NULL;
        e.elems = NULL;
@@ -1026,49 +1016,49 @@ struct chan *namec(char *aname, int amode, int omode, uint32_t perm)
                kfree(e.name);
                kfree(e.elems);
                kfree(e.off);
-//dumpmount();
+               //dumpmount();
                nexterror();
        }
 
        /*
         * Build a list of elements in the path.
         */
-       parsename(name, &e);
+       parsename(aname, &e);
 
        /*
         * On create, ....
         */
-       if (amode == Acreate) {
+       if ((amode == Acreate) || (amode == Acreatechan)) {
                /* perm must have DMDIR if last element is / or /. */
                if (e.mustbedir && !(perm & DMDIR)) {
                        npath = e.ARRAY_SIZEs;
-                       strncpy(tmperrbuf, "create without DMDIR", sizeof(tmperrbuf));
+                       strlcpy(tmperrbuf, "create without DMDIR", sizeof(tmperrbuf));
                        goto NameError;
                }
 
                /* don't try to walk the last path element just yet. */
                if (e.ARRAY_SIZEs == 0)
-                       error(Eexist);
+                       error(EEXIST, ERROR_FIXME);
                e.ARRAY_SIZEs--;
        }
 
-       if (walk(&c, e.elems, e.ARRAY_SIZEs, nomount, &npath) < 0) {
+       if (walk(&c, e.elems, e.ARRAY_SIZEs, can_mount, &npath) < 0) {
                if (npath < 0 || npath > e.ARRAY_SIZEs) {
                        printd("namec %s walk error npath=%d\n", aname, npath);
-                       error("walk failed");
+                       error(EFAIL, "walk failed");
                }
 NameError:
                if (current_errstr()[0]) {
                        /* errstr is set, we'll just stick with it and error out */
                        longjmp(&get_cur_errbuf()->jmpbuf, 1);
                } else {
-                       error("Name to chan lookup failed");
+                       error(EFAIL, "Name to chan lookup failed");
                }
                /* brho: skipping the namec custom error string business, since it hides
                 * the underlying failure.  implement this if you want the old stuff. */
 #if 0
-               strncpy(tmperrbuf, current->errstr, sizeof(tmperrbuf));
-               len = prefix + e.off[npath];
+               strlcpy(tmperrbuf, current->errstr, sizeof(tmperrbuf));
+               len = prefix + e.off[npath]; // prefix was name - aname, the start pt
                if (len < ERRMAX / 3 || (name = memrchr(aname, '/', len)) == NULL
                        || name == aname)
                        snprintf(get_cur_genbuf(), sizeof current->genbuf, "%.*s", len,
@@ -1083,24 +1073,24 @@ NameError:
 
        if (e.mustbedir && !(c->qid.type & QTDIR)) {
                npath = e.ARRAY_SIZEs;
-               strncpy(tmperrbuf, "not a directory", sizeof(tmperrbuf));
+               strlcpy(tmperrbuf, "not a directory", sizeof(tmperrbuf));
                goto NameError;
        }
 
-       if (amode == Aopen && (omode & 3) == OEXEC && (c->qid.type & QTDIR)) {
+       if ((amode == Aopen) && (omode & O_EXEC) && (c->qid.type & QTDIR)) {
                npath = e.ARRAY_SIZEs;
-               error("cannot exec directory");
+               error(EFAIL, "cannot exec directory");
        }
 
        switch (amode) {
                case Aaccess:
-                       if (!nomount)
+                       if (can_mount)
                                domount(&c, NULL);
                        break;
 
                case Abind:
                        m = NULL;
-                       if (!nomount)
+                       if (can_mount)
                                domount(&c, &m);
                        if (c->umh != NULL)
                                putmhead(c->umh);
@@ -1114,7 +1104,7 @@ Open:
                        cname = c->name;
                        kref_get(&cname->ref, 1);
                        m = NULL;
-                       if (!nomount)
+                       if (can_mount)
                                domount(&c, &m);
 
                        /* our own copy to open or remove */
@@ -1142,30 +1132,31 @@ Open:
                                                c->umh = m;
                                        else
                                                putmhead(m);
-
-                                       if (omode == OEXEC)
+                                       if (omode == O_EXEC)
                                                c->flag &= ~CCACHE;
-
-                                       c = devtab[c->type].open(c, omode & ~OCEXEC);
-                                       /* here is where convert omode/vfs flags to c->flags */
-                                       if (omode & O_APPEND)
-                                               c->flag |= CAPPEND;
-                                       // CEXEC should be in the FD, not the chan, right?
-                                       if (omode & OCEXEC)
-                                               c->flag |= CCEXEC;
-                                       if (omode & ORCLOSE)
-                                               c->flag |= CRCLOSE;
+                                       /* here is where convert omode/vfs flags to c->flags.
+                                        * careful, O_CLOEXEC and O_REMCLO are in there.  might need
+                                        * to change that. */
+                                       c->flag |= omode & CEXTERNAL_FLAGS;
+                                       c = devtab[c->type].open(c,
+                                                                omode & ~O_CLOEXEC);
+                                       /* if you get this from a dev, in the dev's open, you are
+                                        * probably saving mode directly, without passing it through
+                                        * openmode. */
+                                       if (c->mode & O_TRUNC)
+                                               error(EFAIL, "Device %s open failed to clear O_TRUNC",
+                                                     devtab[c->type].name);
                                        break;
                        }
                        break;
 
                case Atodir:
                        /*
-                        * Directories (e.g. for cd) are left before the mount po int unused_int,
+                        * Directories (e.g. for cd) are left before the mount point,
                         * so one may mount on / or . and see the effect.
                         */
                        if (!(c->qid.type & QTDIR))
-                               error(Enotdir);
+                               error(ENOTDIR, ERROR_FIXME);
                        break;
 
                case Amount:
@@ -1176,6 +1167,13 @@ Open:
                         */
                        break;
 
+               case Acreatechan:
+                       /*
+                        * We've walked to the place where it *could* be created.
+                        * Return that chan.
+                        */
+                       break;
+
                case Acreate:
                        /*
                         * We've already walked all but the last element.
@@ -1183,10 +1181,10 @@ Open:
                         * If omode&OEXCL is set, just give up.
                         */
                        e.ARRAY_SIZEs++;
-                       if (walk(&c, e.elems + e.ARRAY_SIZEs - 1, 1, nomount, NULL) == 0) {
-                               if (omode & OEXCL)
-                                       error(Eexist);
-                               omode |= OTRUNC;
+                       if (walk(&c, e.elems + e.ARRAY_SIZEs - 1, 1, can_mount, NULL) == 0) {
+                               if (omode & O_EXCL)
+                                       error(EEXIST, ERROR_FIXME);
+                               omode |= O_TRUNC;
                                goto Open;
                        }
 
@@ -1194,11 +1192,11 @@ Open:
                         * The semantics of the create(2) system call are that if the
                         * file exists and can be written, it is to be opened with truncation.
                         * On the other hand, the create(5) message fails if the file exists.
-                        * If we get two create(2) calls happening simultaneously, 
-                        * they might both get here and send create(5) messages, but only 
+                        * If we get two create(2) calls happening simultaneously,
+                        * they might both get here and send create(5) messages, but only
                         * one of the messages will succeed.  To provide the expected create(2)
                         * semantics, the call with the failed message needs to try the above
-                        * walk again, opening for truncation.  This correctly solves the 
+                        * walk again, opening for truncation.  This correctly solves the
                         * create/create race, in the sense that any observable outcome can
                         * be explained as one happening before the other.
                         * The create/create race is quite common.  For example, it happens
@@ -1208,7 +1206,7 @@ Open:
                         * The implementation still admits a create/create/remove race:
                         * (A) walk to file, fails
                         * (B) walk to file, fails
-                        * (A) create file, succeeds, returns 
+                        * (A) create file, succeeds, returns
                         * (B) create file, fails
                         * (A) remove file, succeeds, returns
                         * (B) walk to file, return failure.
@@ -1234,11 +1232,11 @@ Open:
                        cnew = NULL;    /* is this assignment necessary? */
                        /* discard error */
                        if (!waserror()) {      /* try create */
-                               if (!nomount && findmount(&cnew, &m, c->type, c->dev, c->qid))
+                               if (can_mount && findmount(&cnew, &m, c->type, c->dev, c->qid))
                                        cnew = createdir(cnew, m);
                                else {
                                        cnew = c;
-                                       kref_get(&cnew->ref, 1);
+                                       chan_incref(cnew);
                                }
 
                                /*
@@ -1253,14 +1251,15 @@ Open:
                                kref_get(&cnew->name->ref, 1);
 
                                devtab[cnew->type].create(cnew, e.elems[e.ARRAY_SIZEs - 1],
-                                                                                 omode & ~(OEXCL | OCEXEC), perm);
+                                                                                 omode & ~(O_EXCL | O_CLOEXEC),
+                                                         perm);
                                poperror();
                                if (omode & O_APPEND)
-                                       cnew->flag |= CAPPEND;
-                               if (omode & OCEXEC)
-                                       cnew->flag |= CCEXEC;
-                               if (omode & ORCLOSE)
-                                       cnew->flag |= CRCLOSE;
+                                       cnew->flag |= O_APPEND;
+                               if (omode & O_CLOEXEC)
+                                       cnew->flag |= O_CLOEXEC;
+                               if (omode & O_REMCLO)
+                                       cnew->flag |= O_REMCLO;
                                if (m)
                                        putmhead(m);
                                cclose(c);
@@ -1273,20 +1272,21 @@ Open:
                        cclose(cnew);
                        if (m)
                                putmhead(m);
-                       if (omode & OEXCL)
+                       if (omode & O_EXCL)
                                nexterror();    /* safe since we're in a waserror() */
                        poperror();     /* matching the if(!waserror) */
 
                        /* save error, so walk doesn't clobber our existing errstr */
-                       strncpy(tmperrbuf, current_errstr(), MAX_ERRSTR_LEN);
+                       strlcpy(tmperrbuf, current_errstr(), sizeof(tmperrbuf));
                        saved_errno = get_errno();
                        /* note: we depend that walk does not error */
-                       if (walk(&c, e.elems + e.ARRAY_SIZEs - 1, 1, nomount, NULL) < 0) {
+                       if (walk(&c, e.elems + e.ARRAY_SIZEs - 1, 1, can_mount, NULL) < 0) {
                                set_errno(saved_errno);
-                               error(tmperrbuf);       /* report the error we had originally */
+                               /* Report the error we had originally */
+                               error(EFAIL, tmperrbuf);
                        }
-                       strncpy(current_errstr(), tmperrbuf, MAX_ERRSTR_LEN);
-                       omode |= OTRUNC;
+                       strlcpy(current_errstr(), tmperrbuf, MAX_ERRSTR_LEN);
+                       omode |= O_TRUNC;
                        goto Open;
 
                default:
@@ -1296,9 +1296,9 @@ Open:
        poperror();
 
        if (e.ARRAY_SIZEs > 0)
-               strncpy(get_cur_genbuf(), e.elems[e.ARRAY_SIZEs - 1], GENBUF_SZ);
+               strlcpy(get_cur_genbuf(), e.elems[e.ARRAY_SIZEs - 1], GENBUF_SZ);
        else
-               strncpy(get_cur_genbuf(), ".", GENBUF_SZ);
+               strlcpy(get_cur_genbuf(), ".", GENBUF_SZ);
 
        kfree(e.name);
        kfree(e.elems);
@@ -1307,6 +1307,97 @@ Open:
        return c;
 }
 
+struct chan *namec(char *name, int amode, int omode, uint32_t perm)
+{
+       bool can_mount = TRUE;
+       struct chan *c;
+       char *devname, *devspec;
+       int n, devtype;
+
+       if (name[0] == '\0')
+               error(EFAIL, "empty file name");
+       validname(name, 1);
+       /*
+        * Find the starting off point (the current slash, the root of
+        * a device tree, or the current dot) as well as the name to
+        * evaluate starting there.
+        */
+       switch (name[0]) {
+               case '/':
+                       c = current->slash;
+                       if (!c)
+                               panic("no slash!");
+                       chan_incref(c);
+                       break;
+
+               case '#':
+                       can_mount = FALSE;
+                       devname = get_cur_genbuf();
+                       devname[0] = '\0';
+                       n = 0;
+                       name++; /* drop the # */
+                       while ((*name != '\0') && (*name != '/')) {
+                               if (n >= GENBUF_SZ - 1)
+                                       error(ENAMETOOLONG, ERROR_FIXME);
+                               devname[n++] = *name++;
+                       }
+                       devname[n] = '\0';
+                       /* for a name #foo.spec, devname = foo\0, devspec = spec\0.
+                        * genbuf contains foo\0spec\0.  for no spec, devspec = \0 */
+                       devspec = strchr(devname, '.');
+                       if (devspec) {
+                               *devspec = '\0';
+                               devspec++;
+                       } else {
+                               devspec = &devname[n];
+                       }
+                       if (!strcmp(devname, "mnt"))
+                               error(EINVAL, ERROR_FIXME);
+                       /* TODO: deal with this "nodevs" business. */
+                       #if 0
+                       /*
+                        *  the nodevs exceptions are
+                        *  |  it only gives access to pipes you create
+                        *  e  this process's environment
+                        *  s  private file2chan creation space
+                        *  D private secure sockets name space
+                        *  a private TLS name space
+                        */
+                       if (current->pgrp->nodevs &&
+                               //          (utfrune("|esDa", r) == NULL
+                               ((strchr("|esDa", get_cur_genbuf()[1]) == NULL)
+                                || (get_cur_genbuf()[1] == 's' // || r == 's'
+                                        && get_cur_genbuf()[n] != '\0')))
+                               error(EINVAL, ERROR_FIXME);
+                       #endif
+                       devtype = devno(devname, 1);
+                       if (devtype == -1)
+                               error(EFAIL, "Unknown #device %s (spec %s)", devname, devspec);
+                       c = devtab[devtype].attach(devspec);
+                       break;
+               default:
+                       /* this case also covers \0 */
+                       c = current->dot;
+                       if (!c)
+                               panic("no dot!");
+                       chan_incref(c);
+                       break;
+       }
+       return __namec_from(c, name, amode, omode, perm, can_mount);
+}
+
+struct chan *namec_from(struct chan *c, char *name, int amode, int omode,
+                        uint32_t perm)
+{
+       if (name[0] == '\0') {
+               /* Our responsibility to cclose 'c' on our error */
+               cclose(c);
+               error(EFAIL, "empty file name");
+       }
+       validname(name, 1);
+       return __namec_from(c, name, amode, omode, perm, TRUE);
+}
+
 /*
  * name is valid. skip leading / and ./ as much as possible
  */
@@ -1346,7 +1437,7 @@ void validname(char *aname, int slashok)
        ename = memchr(name, 0, (1 << 16));
 
        if (ename == NULL || ename - name >= (1 << 16))
-               error("name too long");
+               error(EINVAL, "Name too long");
 
        while (*name) {
                /* all characters above '~' are ok */
@@ -1356,11 +1447,11 @@ void validname(char *aname, int slashok)
                        name += chartorune(&r, name);
 #endif
                if (c >= 0x7f) {
-                       error("Akaros doesn't do UTF-8");
+                       error(EFAIL, "Akaros doesn't do UTF-8");
                } else {
                        if (isfrog[c])
                                if (!slashok || c != '/') {
-                                       error("%s: %q", Ebadchar, aname);
+                                       error(EINVAL, "%s (%p), at char %c", aname, aname, c);
                                }
                        name++;
                }
@@ -1371,7 +1462,7 @@ void isdir(struct chan *c)
 {
        if (c->qid.type & QTDIR)
                return;
-       error(Enotdir);
+       error(ENOTDIR, ERROR_FIXME);
 }
 
 /*
@@ -1388,7 +1479,7 @@ void isdir(struct chan *c)
  * The mount list is deleted when we cunmount.
  * The RWlock ensures that nothing is using the mount list at that time.
  *
- * It is okay to replace c->mh with whatever you want as 
+ * It is okay to replace c->mh with whatever you want as
  * long as you are sure you have a unique reference to it.
  *
  * This comment might belong somewhere else.