|  | /* 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 <slab.h> | 
|  | #include <kmalloc.h> | 
|  | #include <kref.h> | 
|  | #include <string.h> | 
|  | #include <stdio.h> | 
|  | #include <assert.h> | 
|  | #include <error.h> | 
|  | #include <cpio.h> | 
|  | #include <pmap.h> | 
|  | #include <smp.h> | 
|  | #include <syscall.h> | 
|  |  | 
|  | struct chan *kern_slash; | 
|  |  | 
|  | char *channame(struct chan *c) | 
|  | {	/* DEBUGGING */ | 
|  | if (c == NULL) | 
|  | return "<NULL chan>"; | 
|  | if (c->name == NULL) | 
|  | return "<NULL name>"; | 
|  | if (c->name->s == NULL) | 
|  | return "<NULL name.s>"; | 
|  | return c->name->s; | 
|  | } | 
|  |  | 
|  | enum { | 
|  | CNAMESLOP = 20 | 
|  | }; | 
|  |  | 
|  | struct { | 
|  | spinlock_t lock; | 
|  | int fid; | 
|  | struct chan *free; | 
|  | struct chan *list; | 
|  | } chanalloc; | 
|  |  | 
|  | typedef struct Elemlist Elemlist; | 
|  |  | 
|  | struct Elemlist { | 
|  | char *name;					/* copy of name, so '/' can be overwritten */ | 
|  | int ARRAY_SIZEs; | 
|  | char **elems; | 
|  | int *off; | 
|  | int mustbedir; | 
|  | }; | 
|  |  | 
|  | struct walk_helper { | 
|  | bool can_mount; | 
|  | bool no_follow; | 
|  | unsigned int nr_loops; | 
|  | }; | 
|  | #define WALK_MAX_NR_LOOPS 8 | 
|  |  | 
|  | static struct chan *walk_symlink(struct chan *symlink, struct walk_helper *wh, | 
|  | unsigned int nr_names_left); | 
|  |  | 
|  | #define SEP(c) ((c) == 0 || (c) == '/') | 
|  | void cleancname(struct cname *); | 
|  |  | 
|  | int isdotdot(char *p) | 
|  | { | 
|  | return p[0] == '.' && p[1] == '.' && p[2] == '\0'; | 
|  | } | 
|  |  | 
|  | int emptystr(char *s) | 
|  | { | 
|  | if (s == NULL) | 
|  | return 1; | 
|  | if (s[0] == '\0') | 
|  | return 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Atomically replace *p with copy of s | 
|  | */ | 
|  | void kstrdup(char **p, char *s) | 
|  | { | 
|  | int n; | 
|  | char *t, *prev; | 
|  |  | 
|  | n = strlen(s) + 1; | 
|  | /* if it's a user, we can wait for memory; if not, something's very wrong */ | 
|  | if (current) { | 
|  | t = kzmalloc(n, 0); | 
|  | } else { | 
|  | t = kzmalloc(n, 0); | 
|  | if (t == NULL) | 
|  | panic("kstrdup: no memory"); | 
|  | } | 
|  | memmove(t, s, n); | 
|  | prev = *p; | 
|  | *p = t; | 
|  | kfree(prev); | 
|  | } | 
|  |  | 
|  | void chandevreset(void) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | 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++) { | 
|  | if (devtab[i].init) | 
|  | devtab[i].init(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void chandevshutdown(void) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | /* shutdown in reverse order */ | 
|  | for (i = 0; &devtab[i] < __devtabend; i++) ; | 
|  | for (i--; i >= 0; i--) { | 
|  | if (devtab[i].shutdown) | 
|  | devtab[i].shutdown(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void chan_release(struct kref *kref) | 
|  | { | 
|  | struct chan *c = container_of(kref, struct chan, ref); | 
|  | ERRSTACK(1); | 
|  | /* this style discards the error from close().  picture it as | 
|  | * if (waserror()) { } else { close(); } chanfree_no_matter_what();  */ | 
|  | if (!waserror()) { | 
|  | printd("releasing chan %p, type %d\n", c, c->type); | 
|  | /* -1 means there is no dev yet.  wants a noop for close() */ | 
|  | if (c->type != -1) | 
|  | devtab[c->type].close(c); | 
|  | } | 
|  | /* need to poperror regardless of whether we error'd or not */ | 
|  | poperror(); | 
|  | /* and chan free no matter what */ | 
|  | chanfree(c); | 
|  | } | 
|  |  | 
|  | struct chan *newchan(void) | 
|  | { | 
|  | struct chan *c; | 
|  |  | 
|  | spin_lock(&(&chanalloc)->lock); | 
|  | c = chanalloc.free; | 
|  | if (c != 0) | 
|  | chanalloc.free = c->next; | 
|  | spin_unlock(&(&chanalloc)->lock); | 
|  |  | 
|  | if (c == NULL) { | 
|  | c = kzmalloc(sizeof(struct chan), 0); | 
|  | spin_lock(&(&chanalloc)->lock); | 
|  | c->fid = ++chanalloc.fid; | 
|  | c->link = chanalloc.list; | 
|  | chanalloc.list = c; | 
|  | spin_unlock(&(&chanalloc)->lock); | 
|  | spinlock_init(&c->lock); | 
|  | qlock_init(&c->umqlock); | 
|  | } | 
|  |  | 
|  | /* if you get an error before associating with a dev, cclose skips calling | 
|  | * the dev's close */ | 
|  | c->type = -1; | 
|  | c->flag = 0; | 
|  | kref_init(&c->ref, chan_release, 1); | 
|  | c->dev = 0; | 
|  | c->offset = 0; | 
|  | c->iounit = 0; | 
|  | c->umh = 0; | 
|  | c->uri = 0; | 
|  | c->dri = 0; | 
|  | c->aux = 0; | 
|  | c->mchan = 0; | 
|  | c->mcp = 0; | 
|  | c->mux = 0; | 
|  | c->mqid.path = 0; | 
|  | c->mqid.vers = 0; | 
|  | c->mqid.type = 0; | 
|  | c->name = 0; | 
|  | c->buf = NULL; | 
|  | c->mountpoint = NULL; | 
|  | return c; | 
|  | } | 
|  |  | 
|  | static void __cname_release(struct kref *kref) | 
|  | { | 
|  | struct cname *n = container_of(kref, struct cname, ref); | 
|  | kfree(n->s); | 
|  | kfree(n); | 
|  | } | 
|  |  | 
|  | struct cname *newcname(char *s) | 
|  | { | 
|  | struct cname *n; | 
|  | int i; | 
|  |  | 
|  | n = kzmalloc(sizeof(*n), 0); | 
|  | i = strlen(s); | 
|  | n->len = i; | 
|  | n->alen = i + CNAMESLOP; | 
|  | n->s = kzmalloc(n->alen, 0); | 
|  | memmove(n->s, s, i + 1); | 
|  | kref_init(&n->ref, __cname_release, 1); | 
|  | return n; | 
|  | } | 
|  |  | 
|  | void cnameclose(struct cname *n) | 
|  | { | 
|  | if (n == NULL) | 
|  | return; | 
|  | kref_put(&n->ref); | 
|  | } | 
|  |  | 
|  | struct cname *addelem(struct cname *n, char *s) | 
|  | { | 
|  | int i, a; | 
|  | char *t; | 
|  | struct cname *new; | 
|  |  | 
|  | if (s[0] == '.' && s[1] == '\0') | 
|  | return n; | 
|  |  | 
|  | if (kref_refcnt(&n->ref) > 1) { | 
|  | /* copy on write */ | 
|  | new = newcname(n->s); | 
|  | cnameclose(n); | 
|  | n = new; | 
|  | } | 
|  |  | 
|  | i = strlen(s); | 
|  | if (n->len + 1 + i + 1 > n->alen) { | 
|  | a = n->len + 1 + i + 1 + CNAMESLOP; | 
|  | t = kzmalloc(a, 0); | 
|  | memmove(t, n->s, n->len + 1); | 
|  | kfree(n->s); | 
|  | n->s = t; | 
|  | n->alen = a; | 
|  | } | 
|  | if (n->len > 0 && n->s[n->len - 1] != '/' && s[0] != '/')	/* don't insert extra slash if one is present */ | 
|  | n->s[n->len++] = '/'; | 
|  | memmove(n->s + n->len, s, i + 1); | 
|  | n->len += i; | 
|  | if (isdotdot(s)) | 
|  | cleancname(n); | 
|  | return n; | 
|  | } | 
|  |  | 
|  | void chanfree(struct chan *c) | 
|  | { | 
|  | c->flag = CFREE; | 
|  |  | 
|  | if (c->umh != NULL) { | 
|  | putmhead(c->umh); | 
|  | c->umh = NULL; | 
|  | } | 
|  | if (c->umc != NULL) { | 
|  | cclose(c->umc); | 
|  | c->umc = NULL; | 
|  | } | 
|  | if (c->mux != NULL) { | 
|  | // | 
|  | muxclose(c->mux); | 
|  | c->mux = NULL; | 
|  | } | 
|  | if (c->mchan != NULL) { | 
|  | cclose(c->mchan); | 
|  | c->mchan = NULL; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | chanalloc.free = c; | 
|  | spin_unlock(&(&chanalloc)->lock); | 
|  | } | 
|  |  | 
|  | void cclose(struct chan *c) | 
|  | { | 
|  | if (c == 0) | 
|  | return; | 
|  |  | 
|  | if (c->flag & CFREE) | 
|  | panic("cclose %p", getcallerpc(&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.) | 
|  | */ | 
|  | struct chan *cunique(struct chan *c) | 
|  | { | 
|  | struct chan *nc; | 
|  |  | 
|  | if (kref_refcnt(&c->ref) != 1) { | 
|  | nc = cclone(c); | 
|  | cclose(c); | 
|  | c = nc; | 
|  | } | 
|  |  | 
|  | return c; | 
|  | } | 
|  |  | 
|  | int eqqid(struct qid a, struct qid b) | 
|  | { | 
|  | return a.path == b.path && a.vers == b.vers; | 
|  | } | 
|  |  | 
|  | int eqchan(struct chan *a, struct chan *b, int pathonly) | 
|  | { | 
|  | if (a->qid.path != b->qid.path) | 
|  | return 0; | 
|  | if (!pathonly && a->qid.vers != b->qid.vers) | 
|  | return 0; | 
|  | if (a->type != b->type) | 
|  | return 0; | 
|  | if (a->dev != b->dev) | 
|  | return 0; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | int eqchantdqid(struct chan *a, int type, int dev, struct qid qid, int pathonly) | 
|  | { | 
|  | if (a->qid.path != qid.path) | 
|  | return 0; | 
|  | if (!pathonly && a->qid.vers != qid.vers) | 
|  | return 0; | 
|  | if (a->type != type) | 
|  | return 0; | 
|  | if (a->dev != dev) | 
|  | return 0; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static void mh_release(struct kref *kref) | 
|  | { | 
|  | struct mhead *mh = container_of(kref, struct mhead, ref); | 
|  | mh->mount = (struct mount *)0xCafeBeef; | 
|  | kfree(mh); | 
|  | } | 
|  |  | 
|  | struct mhead *newmhead(struct chan *from) | 
|  | { | 
|  | struct mhead *mh; | 
|  |  | 
|  | mh = kzmalloc(sizeof(struct mhead), 0); | 
|  | kref_init(&mh->ref, mh_release, 1); | 
|  | rwinit(&mh->lock); | 
|  | mh->from = from; | 
|  | chan_incref(from); | 
|  |  | 
|  | /* | 
|  | n = from->name->len; | 
|  | if(n >= sizeof(mh->fromname)) | 
|  | n = sizeof(mh->fromname)-1; | 
|  | memmove(mh->fromname, from->name->s, n); | 
|  | mh->fromname[n] = 0; | 
|  | */ | 
|  | return mh; | 
|  | } | 
|  |  | 
|  | int cmount(struct chan *new, struct chan *old, int flag, char *spec) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | struct pgrp *pg; | 
|  | int order, flg; | 
|  | struct mhead *m, **l, *mh; | 
|  | struct mount *nm, *f, *um, **h; | 
|  |  | 
|  | /* Can bind anything onto a symlink's name.  Otherwise, both the old and the | 
|  | * new must agree on whether or not it is a directory. */ | 
|  | if (!(old->qid.type & QTSYMLINK) && | 
|  | (QTDIR & (old->qid.type ^ new->qid.type))) | 
|  | error(EINVAL, ERROR_FIXME); | 
|  |  | 
|  | if (old->umh) | 
|  | printd("cmount old extra umh\n"); | 
|  |  | 
|  | order = flag & MORDER; | 
|  |  | 
|  | if ((old->qid.type & QTDIR) == 0 && order != MREPL) | 
|  | error(EINVAL, ERROR_FIXME); | 
|  |  | 
|  | mh = new->umh; | 
|  |  | 
|  | /* | 
|  | * Not allowed to bind when the old directory | 
|  | * is itself a union.  (Maybe it should be allowed, but I don't see | 
|  | * what the semantics would be.) | 
|  | * | 
|  | * We need to check mh->mount->next to tell unions apart from | 
|  | * simple mount points, so that things like | 
|  | *  mount -c fd /root | 
|  | *  bind -c /root / | 
|  | * 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(EEXIST, ERROR_FIXME); | 
|  |  | 
|  | pg = current->pgrp; | 
|  | wlock(&pg->ns); | 
|  |  | 
|  | l = &MOUNTH(pg, old->qid); | 
|  | for (m = *l; m; m = m->hash) { | 
|  | if (eqchan(m->from, old, 1)) | 
|  | break; | 
|  | l = &m->hash; | 
|  | } | 
|  |  | 
|  | if (m == NULL) { | 
|  | /* | 
|  | *  nothing mounted here yet.  create a mount | 
|  | *  head and add to the hash table. | 
|  | */ | 
|  | m = newmhead(old); | 
|  | *l = m; | 
|  |  | 
|  | /* | 
|  | *  if this is a union mount, add the old | 
|  | *  node to the mount chain. | 
|  | */ | 
|  | if (order != MREPL) | 
|  | m->mount = newmount(m, old, 0, 0); | 
|  | } | 
|  | wlock(&m->lock); | 
|  | if (waserror()) { | 
|  | wunlock(&m->lock); | 
|  | nexterror(); | 
|  | } | 
|  | wunlock(&pg->ns); | 
|  |  | 
|  | nm = newmount(m, new, flag, spec); | 
|  | if (mh != NULL && mh->mount != NULL) { | 
|  | /* | 
|  | *  copy a union when binding it onto a directory | 
|  | */ | 
|  | flg = order; | 
|  | if (order == MREPL) | 
|  | flg = MAFTER; | 
|  | h = &nm->next; | 
|  | um = mh->mount; | 
|  | for (um = um->next; um; um = um->next) { | 
|  | f = newmount(m, um->to, flg, um->spec); | 
|  | *h = f; | 
|  | h = &f->next; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (m->mount && order == MREPL) { | 
|  | mountfree(m->mount); | 
|  | m->mount = 0; | 
|  | } | 
|  |  | 
|  | if (flag & MCREATE) | 
|  | nm->mflag |= MCREATE; | 
|  |  | 
|  | if (m->mount && order == MAFTER) { | 
|  | for (f = m->mount; f->next; f = f->next) ; | 
|  | f->next = nm; | 
|  | } else { | 
|  | for (f = nm; f->next; f = f->next) ; | 
|  | f->next = m->mount; | 
|  | m->mount = nm; | 
|  | } | 
|  |  | 
|  | wunlock(&m->lock); | 
|  | poperror(); | 
|  | return nm->mountid; | 
|  | } | 
|  |  | 
|  | void cunmount(struct chan *mnt, struct chan *mounted) | 
|  | { | 
|  | struct pgrp *pg; | 
|  | struct mhead *m, **l; | 
|  | struct mount *f, **p; | 
|  |  | 
|  | if (mnt->umh)	/* should not happen */ | 
|  | printd("cunmount newp extra umh %p has %p\n", mnt, mnt->umh); | 
|  |  | 
|  | /* | 
|  | * 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. | 
|  | * Although surprising, this is okay, since the | 
|  | * cclose will take care of freeing the umh. | 
|  | */ | 
|  |  | 
|  | pg = current->pgrp; | 
|  | wlock(&pg->ns); | 
|  |  | 
|  | l = &MOUNTH(pg, mnt->qid); | 
|  | for (m = *l; m; m = m->hash) { | 
|  | if (eqchan(m->from, mnt, 1)) | 
|  | break; | 
|  | l = &m->hash; | 
|  | } | 
|  |  | 
|  | if (m == 0) { | 
|  | wunlock(&pg->ns); | 
|  | error(ENOENT, ERROR_FIXME); | 
|  | } | 
|  |  | 
|  | wlock(&m->lock); | 
|  | if (mounted == 0) { | 
|  | *l = m->hash; | 
|  | wunlock(&pg->ns); | 
|  | mountfree(m->mount); | 
|  | m->mount = NULL; | 
|  | cclose(m->from); | 
|  | wunlock(&m->lock); | 
|  | putmhead(m); | 
|  | return; | 
|  | } | 
|  |  | 
|  | p = &m->mount; | 
|  | for (f = *p; f; f = f->next) { | 
|  | /* BUG: Needs to be 2 pass */ | 
|  | if (eqchan(f->to, mounted, 1) || | 
|  | (f->to->mchan && eqchan(f->to->mchan, mounted, 1))) { | 
|  | *p = f->next; | 
|  | f->next = 0; | 
|  | mountfree(f); | 
|  | if (m->mount == NULL) { | 
|  | *l = m->hash; | 
|  | cclose(m->from); | 
|  | wunlock(&m->lock); | 
|  | wunlock(&pg->ns); | 
|  | putmhead(m); | 
|  | return; | 
|  | } | 
|  | wunlock(&m->lock); | 
|  | wunlock(&pg->ns); | 
|  | return; | 
|  | } | 
|  | p = &f->next; | 
|  | } | 
|  | wunlock(&m->lock); | 
|  | wunlock(&pg->ns); | 
|  | error(ENOENT, ERROR_FIXME); | 
|  | } | 
|  |  | 
|  | struct chan *cclone(struct chan *c) | 
|  | { | 
|  | struct chan *nc; | 
|  | struct walkqid *wq; | 
|  |  | 
|  | wq = devtab[c->type].walk(c, NULL, NULL, 0); | 
|  | if (wq == NULL) | 
|  | error(EFAIL, "clone failed"); | 
|  | nc = wq->clone; | 
|  | kfree(wq); | 
|  | nc->name = c->name; | 
|  | if (c->name) | 
|  | kref_get(&c->name->ref, 1); | 
|  | return nc; | 
|  | } | 
|  |  | 
|  | /* Helper: is something mounted on the chan? */ | 
|  | static bool is_mount_point(struct chan *c) | 
|  | { | 
|  | struct pgrp *pg; | 
|  | struct mhead *m; | 
|  | int type = c->type; | 
|  | int dev = c->dev; | 
|  | struct qid qid = c->qid; | 
|  |  | 
|  | if (!current) | 
|  | return false; | 
|  | pg = current->pgrp; | 
|  | rlock(&pg->ns); | 
|  | for (m = MOUNTH(pg, qid); m; m = m->hash) { | 
|  | rlock(&m->lock); | 
|  | if (!m->from) { | 
|  | runlock(&m->lock); | 
|  | continue; | 
|  | } | 
|  | if (eqchantdqid(m->from, type, dev, qid, 1)) { | 
|  | runlock(&m->lock); | 
|  | runlock(&pg->ns); | 
|  | return true; | 
|  | } | 
|  | runlock(&m->lock); | 
|  | } | 
|  | runlock(&pg->ns); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | int | 
|  | findmount(struct chan **cp, | 
|  | struct mhead **mp, int type, int dev, struct qid qid) | 
|  | { | 
|  | struct pgrp *pg; | 
|  | struct mhead *m; | 
|  |  | 
|  | if (!current) | 
|  | return 0; | 
|  | pg = current->pgrp; | 
|  | rlock(&pg->ns); | 
|  | for (m = MOUNTH(pg, qid); m; m = m->hash) { | 
|  | rlock(&m->lock); | 
|  | if (m->from == NULL) { | 
|  | printd("m %p m->from 0\n", m); | 
|  | runlock(&m->lock); | 
|  | continue; | 
|  | } | 
|  | if (eqchantdqid(m->from, type, dev, qid, 1)) { | 
|  | runlock(&pg->ns); | 
|  | if (mp != NULL) { | 
|  | kref_get(&m->ref, 1); | 
|  | if (*mp != NULL) | 
|  | putmhead(*mp); | 
|  | *mp = m; | 
|  | } | 
|  | if (*cp != NULL) | 
|  | cclose(*cp); | 
|  | chan_incref(m->mount->to); | 
|  | *cp = m->mount->to; | 
|  | runlock(&m->lock); | 
|  | return 1; | 
|  | } | 
|  | runlock(&m->lock); | 
|  | } | 
|  |  | 
|  | runlock(&pg->ns); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int domount(struct chan **cp, struct mhead **mp) | 
|  | { | 
|  | return findmount(cp, mp, (*cp)->type, (*cp)->dev, (*cp)->qid); | 
|  | } | 
|  |  | 
|  | struct chan *undomount(struct chan *c, struct cname *name) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | struct chan *nc; | 
|  | struct pgrp *pg; | 
|  | struct mount *t; | 
|  | struct mhead **h, **he, *f; | 
|  |  | 
|  | if (!current) | 
|  | return c; | 
|  | pg = current->pgrp; | 
|  | rlock(&pg->ns); | 
|  | if (waserror()) { | 
|  | runlock(&pg->ns); | 
|  | nexterror(); | 
|  | } | 
|  |  | 
|  | he = &pg->mnthash[MNTHASH]; | 
|  | for (h = pg->mnthash; h < he; h++) { | 
|  | for (f = *h; f; f = f->hash) { | 
|  | if (strcmp(f->from->name->s, name->s) != 0) | 
|  | continue; | 
|  | for (t = f->mount; t; t = t->next) { | 
|  | if (eqchan(c, t->to, 1)) { | 
|  | /* | 
|  | * We want to come out on the left hand side of the mount | 
|  | * point using the element of the union that we entered on. | 
|  | * To do this, find the element that has a from name of | 
|  | * c->name->s. | 
|  | */ | 
|  | if (strcmp(t->head->from->name->s, name->s) != 0) | 
|  | continue; | 
|  | nc = t->head->from; | 
|  | chan_incref(nc); | 
|  | cclose(c); | 
|  | c = nc; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | poperror(); | 
|  | runlock(&pg->ns); | 
|  | return c; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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. | 
|  | */ | 
|  | int walk(struct chan **cp, char **names, int nnames, struct walk_helper *wh, | 
|  | int *nerror) | 
|  | { | 
|  | int dev, dotdot, i, n, nhave, ntry, type; | 
|  | struct chan *c, *nc, *lastmountpoint = NULL; | 
|  | struct cname *cname; | 
|  | struct mount *f; | 
|  | struct mhead *mh, *nmh; | 
|  | struct walkqid *wq; | 
|  |  | 
|  | c = *cp; | 
|  | 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 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 point, and c's name is cname. | 
|  | */ | 
|  | for (nhave = 0; nhave < nnames; nhave += n) { | 
|  | /* We only allow symlink when they are first and it's .. (see below) */ | 
|  | if ((c->qid.type & (QTDIR | QTSYMLINK)) == 0) { | 
|  | if (nerror) | 
|  | *nerror = nhave; | 
|  | cnameclose(cname); | 
|  | cclose(c); | 
|  | set_error(ENOTDIR, ERROR_FIXME); | 
|  | if (mh != NULL) | 
|  | putmhead(mh); | 
|  | return -1; | 
|  | } | 
|  | ntry = nnames - nhave; | 
|  | if (ntry > MAXWELEM) | 
|  | ntry = MAXWELEM; | 
|  | dotdot = 0; | 
|  | for (i = 0; i < ntry; i++) { | 
|  | if (isdotdot(names[nhave + i])) { | 
|  | if (i == 0) { | 
|  | dotdot = 1; | 
|  | ntry = 1; | 
|  | } else | 
|  | ntry = i; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!dotdot && wh->can_mount) | 
|  | domount(&c, &mh); | 
|  | /* Bug - the only time we walk from a symlink should be during | 
|  | * walk_symlink, which should have given us a dotdot. */ | 
|  | if ((c->qid.type & QTSYMLINK) && !dotdot) | 
|  | panic("Got a walk from a symlink that wasn't ..!"); | 
|  |  | 
|  | type = c->type; | 
|  | dev = c->dev; | 
|  |  | 
|  | if ((wq = devtab[type].walk(c, NULL, names + nhave, ntry)) == NULL) { | 
|  | /* try a union mount, if any */ | 
|  | if (mh && wh->can_mount) { | 
|  | /* | 
|  | * mh->mount == c, so start at mh->mount->next | 
|  | */ | 
|  | rlock(&mh->lock); | 
|  | for (f = mh->mount->next; f; f = f->next) | 
|  | if ((wq = | 
|  | devtab[f->to->type].walk(f->to, NULL, names + nhave, | 
|  | ntry)) != NULL) | 
|  | break; | 
|  | runlock(&mh->lock); | 
|  | if (f != NULL) { | 
|  | type = f->to->type; | 
|  | dev = f->to->dev; | 
|  | } | 
|  | } | 
|  | if (wq == NULL) { | 
|  | cclose(c); | 
|  | cnameclose(cname); | 
|  | if (nerror) | 
|  | *nerror = nhave + 1; | 
|  | if (mh != NULL) | 
|  | putmhead(mh); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | nmh = NULL; | 
|  | if (dotdot) { | 
|  | assert(wq->nqid == 1); | 
|  | assert(wq->clone != NULL); | 
|  |  | 
|  | cname = addelem(cname, ".."); | 
|  | nc = undomount(wq->clone, cname); | 
|  | n = 1; | 
|  | } else { | 
|  | nc = NULL; | 
|  | if (wh->can_mount) | 
|  | for (i = 0; i < wq->nqid && i < ntry - 1; i++) | 
|  | if (findmount(&nc, &nmh, type, dev, wq->qid[i])) | 
|  | break; | 
|  | if (nc == NULL) {	/* no mount points along path */ | 
|  | if (wq->clone == NULL) { | 
|  | cclose(c); | 
|  | cnameclose(cname); | 
|  | if (wq->nqid == 0 || (wq->qid[wq->nqid - 1].type & QTDIR)) { | 
|  | if (nerror) | 
|  | *nerror = nhave + wq->nqid + 1; | 
|  | set_error(ENOENT, "walk failed"); | 
|  | } else { | 
|  | if (nerror) | 
|  | *nerror = nhave + wq->nqid; | 
|  | set_error(ENOTDIR, "walk failed"); | 
|  | } | 
|  | kfree(wq); | 
|  | if (mh != NULL) | 
|  | putmhead(mh); | 
|  | return -1; | 
|  | } | 
|  | n = wq->nqid; | 
|  | if (wq->clone->qid.type & QTSYMLINK) { | 
|  | nc = walk_symlink(wq->clone, wh, nnames - nhave - n); | 
|  | if (!nc) { | 
|  | /* walk_symlink() set error.  This seems to be the | 
|  | * standard walk() error-cleanup. */ | 
|  | if (nerror) | 
|  | *nerror = nhave + wq->nqid; | 
|  | cclose(c); | 
|  | cclose(wq->clone); | 
|  | cnameclose(cname); | 
|  | kfree(wq); | 
|  | if (mh != NULL) | 
|  | putmhead(mh); | 
|  | return -1; | 
|  | } | 
|  | } else { | 
|  | nc = wq->clone; | 
|  | } | 
|  | } else {	/* stopped early, at a mount point */ | 
|  | if (wq->clone != NULL) { | 
|  | cclose(wq->clone); | 
|  | wq->clone = NULL; | 
|  | } | 
|  | lastmountpoint = nc; | 
|  | n = i + 1; | 
|  | } | 
|  | for (i = 0; i < n; i++) | 
|  | cname = addelem(cname, names[nhave + i]); | 
|  | } | 
|  | cclose(c); | 
|  | c = nc; | 
|  | putmhead(mh); | 
|  | mh = nmh; | 
|  | kfree(wq); | 
|  | } | 
|  |  | 
|  | putmhead(mh); | 
|  |  | 
|  | c = cunique(c); | 
|  |  | 
|  | if (c->umh != NULL) {	//BUG | 
|  | printd("walk umh\n"); | 
|  | putmhead(c->umh); | 
|  | c->umh = NULL; | 
|  | } | 
|  |  | 
|  | cnameclose(c->name); | 
|  | c->name = cname; | 
|  | c->mountpoint = lastmountpoint; | 
|  |  | 
|  | cclose(*cp); | 
|  | *cp = c; | 
|  | if (nerror) | 
|  | *nerror = 0; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * c is a mounted non-creatable directory.  find a creatable one. | 
|  | */ | 
|  | struct chan *createdir(struct chan *c, struct mhead *m) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | struct chan *nc; | 
|  | struct mount *f; | 
|  |  | 
|  | rlock(&m->lock); | 
|  | if (waserror()) { | 
|  | runlock(&m->lock); | 
|  | nexterror(); | 
|  | } | 
|  | for (f = m->mount; f; f = f->next) { | 
|  | if (f->mflag & MCREATE) { | 
|  | nc = cclone(f->to); | 
|  | runlock(&m->lock); | 
|  | poperror(); | 
|  | cclose(c); | 
|  | return nc; | 
|  | } | 
|  | } | 
|  | error(EPERM, ERROR_FIXME); | 
|  | poperror(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * In place, rewrite name to compress multiple /, eliminate ., and process .. | 
|  | */ | 
|  | void cleancname(struct cname *n) | 
|  | { | 
|  | char *p; | 
|  |  | 
|  | if (n->s[0] == '#') { | 
|  | p = strchr(n->s, '/'); | 
|  | if (p == NULL) | 
|  | return; | 
|  | cleanname(p); | 
|  |  | 
|  | /* | 
|  | * The correct name is #i rather than #i/, | 
|  | * but the correct name of #/ is #/. | 
|  | */ | 
|  | if (strcmp(p, "/") == 0 && n->s[1] != '/') | 
|  | *p = '\0'; | 
|  | } else | 
|  | cleanname(n->s); | 
|  | n->len = strlen(n->s); | 
|  | } | 
|  |  | 
|  | static void growparse(Elemlist * e) | 
|  | { | 
|  | char **new; | 
|  | int *inew; | 
|  | enum { Delta = 8 }; | 
|  |  | 
|  | if (e->ARRAY_SIZEs % Delta == 0) { | 
|  | new = kzmalloc((e->ARRAY_SIZEs + Delta) * sizeof(char *), 0); | 
|  | memmove(new, e->elems, e->ARRAY_SIZEs * sizeof(char *)); | 
|  | kfree(e->elems); | 
|  | e->elems = new; | 
|  | inew = kzmalloc((e->ARRAY_SIZEs + Delta + 1) * sizeof(int), 0); | 
|  | memmove(inew, e->off, e->ARRAY_SIZEs * sizeof(int)); | 
|  | kfree(e->off); | 
|  | e->off = inew; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * The name is known to be valid. | 
|  | * Copy the name so slashes can be overwritten. | 
|  | * An empty string will set ARRAY_SIZE=0. | 
|  | * A path ending in / or /. or /.//./ etc. will have | 
|  | * e.mustbedir = 1, so that we correctly | 
|  | * reject, e.g., "/adm/users/." when /adm/users is a file | 
|  | * rather than a directory. | 
|  | */ | 
|  | static void parsename(char *name, Elemlist * e) | 
|  | { | 
|  | char *slash; | 
|  |  | 
|  | kstrdup(&e->name, name); | 
|  | name = e->name; | 
|  | e->ARRAY_SIZEs = 0; | 
|  | e->elems = NULL; | 
|  | e->off = kzmalloc(sizeof(int), 0); | 
|  | e->off[0] = skipslash(name) - name; | 
|  | for (;;) { | 
|  | name = skipslash(name); | 
|  | if (*name == '\0') { | 
|  | e->mustbedir = 1; | 
|  | break; | 
|  | } | 
|  | growparse(e); | 
|  |  | 
|  | e->elems[e->ARRAY_SIZEs++] = name; | 
|  | /* we may want to do this again some day | 
|  | slash = utfrune(name, '/'); | 
|  | */ | 
|  | slash = strchr(name, '/'); | 
|  | if (slash == NULL) { | 
|  | e->off[e->ARRAY_SIZEs] = name + strlen(name) - e->name; | 
|  | e->mustbedir = 0; | 
|  | break; | 
|  | } | 
|  | e->off[e->ARRAY_SIZEs] = slash - e->name; | 
|  | *slash++ = '\0'; | 
|  | name = slash; | 
|  | } | 
|  | } | 
|  |  | 
|  | void *memrchr(void *va, int c, long n) | 
|  | { | 
|  | uint8_t *a, *e; | 
|  |  | 
|  | a = va; | 
|  | for (e = a + n - 1; e > a; e--) | 
|  | if (*e == c) | 
|  | return e; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Turn a name into a channel. | 
|  | * &name[0] is known to be a valid address.  It may be a kernel address. | 
|  | * | 
|  | * Opening with amode Aopen, Acreate, or Aremove guarantees | 
|  | * that the result will be the only reference to that particular fid. | 
|  | * This is necessary since we might pass the result to | 
|  | * devtab[].remove(). | 
|  | * | 
|  | * Opening Atodir, Amount, or Aaccess does not guarantee this. | 
|  | * | 
|  | * Opening Aaccess can, under certain conditions, return a | 
|  | * 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. | 
|  | * | 
|  | * 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, | 
|  | struct walk_helper *wh, void *ext) | 
|  | { | 
|  | ERRSTACK(2); | 
|  | int len, npath; | 
|  | struct chan *cnew, *renamee; | 
|  | struct cname *cname; | 
|  | Elemlist e; | 
|  | struct mhead *m; | 
|  | char tmperrbuf[ERRMAX]; | 
|  | int saved_errno; | 
|  | // Rune r; | 
|  |  | 
|  | static_assert(!(CINTERNAL_FLAGS & CEXTERNAL_FLAGS)); | 
|  |  | 
|  | e.name = NULL; | 
|  | e.elems = NULL; | 
|  | e.off = NULL; | 
|  | e.ARRAY_SIZEs = 0; | 
|  | if (waserror()) { | 
|  | cclose(c); | 
|  | kfree(e.name); | 
|  | kfree(e.elems); | 
|  | kfree(e.off); | 
|  | //dumpmount(); | 
|  | nexterror(); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Build a list of elements in the path. | 
|  | */ | 
|  | parsename(aname, &e); | 
|  |  | 
|  | if (e.mustbedir) | 
|  | omode &= ~O_NOFOLLOW; | 
|  |  | 
|  | switch (amode) { | 
|  | case Acreate: | 
|  | /* perm must have DMDIR if last element is / or /. */ | 
|  | if (e.mustbedir && !(perm & DMDIR)) { | 
|  | npath = e.ARRAY_SIZEs; | 
|  | error(EINVAL, "create without DMDIR"); | 
|  | } | 
|  | /* don't try to walk the last path element just yet. */ | 
|  | if (e.ARRAY_SIZEs == 0) | 
|  | error(EEXIST, ERROR_FIXME); | 
|  | e.ARRAY_SIZEs--; | 
|  | /* We're dropping the last element, which O_NOFOLLOW applied to.  Not | 
|  | * sure if there are any legit reasons to have O_NOFOLLOW with create.*/ | 
|  | omode &= ~O_NOFOLLOW; | 
|  | break; | 
|  | case Arename: | 
|  | if (e.ARRAY_SIZEs == 0) | 
|  | error(EINVAL, "rename needs at least one name"); | 
|  | e.ARRAY_SIZEs--; | 
|  | omode &= ~O_NOFOLLOW; | 
|  | break; | 
|  | /* the difference for stat and lstat (Aaccess) are handled in sysfile.c */ | 
|  | case Abind: | 
|  | case Amount: | 
|  | case Aremove: | 
|  | omode |= O_NOFOLLOW; | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (omode & O_NOFOLLOW) | 
|  | wh->no_follow = true; | 
|  |  | 
|  | if (walk(&c, e.elems, e.ARRAY_SIZEs, wh, &npath) < 0) { | 
|  | if (npath < 0 || npath > e.ARRAY_SIZEs) { | 
|  | printd("namec %s walk error npath=%d\n", aname, npath); | 
|  | error(EFAIL, "walk failed"); | 
|  | } | 
|  | /* Old plan 9 errors would jump here for the magic error parsing. */ | 
|  | NameError: | 
|  | if (current_errstr()[0]) { | 
|  | /* errstr is set, we'll just stick with it and error out */ | 
|  | error_jmp(); | 
|  | } else { | 
|  | 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 | 
|  | 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, | 
|  | aname); | 
|  | else | 
|  | snprintf(get_cur_genbuf(), sizeof current->genbuf, "...%.*s", | 
|  | (int)(len - (name - aname)), name); | 
|  | snprintf(current->errstr, ERRMAX, "%#q %s", get_cur_genbuf(), | 
|  | tmperrbuf); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | if (e.mustbedir && !(c->qid.type & QTDIR)) { | 
|  | npath = e.ARRAY_SIZEs; | 
|  | error(ENOTDIR, "not a dir, but mustbedir.  trailing slash?"); | 
|  | } | 
|  |  | 
|  | if ((amode == Aopen) && (omode & O_EXEC) && (c->qid.type & QTDIR)) { | 
|  | npath = e.ARRAY_SIZEs; | 
|  | error(EFAIL, "cannot exec directory"); | 
|  | } | 
|  |  | 
|  | switch (amode) { | 
|  | case Aaccess: | 
|  | if (wh->can_mount) | 
|  | domount(&c, NULL); | 
|  | break; | 
|  |  | 
|  | case Abind: | 
|  | m = NULL; | 
|  | if (wh->can_mount) | 
|  | domount(&c, &m); | 
|  | if (c->umh != NULL) | 
|  | putmhead(c->umh); | 
|  | c->umh = m; | 
|  | break; | 
|  |  | 
|  | case Aremove: | 
|  | case Aopen: | 
|  | Open: | 
|  | /* save the name; domount might change c */ | 
|  | cname = c->name; | 
|  | kref_get(&cname->ref, 1); | 
|  | m = NULL; | 
|  | if (wh->can_mount) | 
|  | domount(&c, &m); | 
|  |  | 
|  | /* our own copy to open or remove */ | 
|  | c = cunique(c); | 
|  |  | 
|  | /* now it's our copy anyway, we can put the name back */ | 
|  | cnameclose(c->name); | 
|  | c->name = cname; | 
|  |  | 
|  | switch (amode) { | 
|  | case Aremove: | 
|  | putmhead(m); | 
|  | break; | 
|  |  | 
|  | case Aopen: | 
|  | case Acreate: | 
|  | if (c->umh != NULL) { | 
|  | printd("cunique umh\n"); | 
|  | putmhead(c->umh); | 
|  | c->umh = NULL; | 
|  | } | 
|  |  | 
|  | /* only save the mount head if it's a multiple element union */ | 
|  | if (m && m->mount && m->mount->next) | 
|  | c->umh = m; | 
|  | else | 
|  | putmhead(m); | 
|  | /* 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 point, | 
|  | * so one may mount on / or . and see the effect. | 
|  | */ | 
|  | if (!(c->qid.type & QTDIR)) | 
|  | error(ENOTDIR, ERROR_FIXME); | 
|  | break; | 
|  |  | 
|  | case Amount: | 
|  | /* | 
|  | * When mounting on an already mounted upon directory, | 
|  | * one wants subsequent mounts to be attached to the | 
|  | * original directory, not the replacement.  Don't domount. | 
|  | */ | 
|  | break; | 
|  |  | 
|  | case Arename: | 
|  | /* We already walked to the parent of new_path, which is in c. | 
|  | * We're a lot like create here - need to find mounts, etc.  On the | 
|  | * way out, we putmhead if we have an m, and clean up our chans.  On | 
|  | * success, c becomes cnew (thus close the old c).  On failure, we | 
|  | * just close cnew. */ | 
|  | e.ARRAY_SIZEs++; | 
|  | m = NULL; | 
|  | cnew = NULL; | 
|  | if (waserror()) { | 
|  | /* rename or createdir failed */ | 
|  | cclose(cnew); | 
|  | if (m) | 
|  | putmhead(m); | 
|  | nexterror();	/* safe since we're in a waserror() */ | 
|  | } | 
|  | if (wh->can_mount && findmount(&cnew, &m, c->type, c->dev, | 
|  | c->qid)) { | 
|  | cnew = createdir(cnew, m); | 
|  | } else { | 
|  | cnew = c; | 
|  | chan_incref(cnew); | 
|  | } | 
|  | cnew = cunique(cnew); | 
|  | cnameclose(cnew->name); | 
|  | cnew->name = c->name; | 
|  | kref_get(&cnew->name->ref, 1); | 
|  | /* At this point, we have our new_path parent chan (cnew) and the | 
|  | * renamee chan */ | 
|  | renamee = ext; | 
|  | if (cnew->type != renamee->type) | 
|  | error(EXDEV, "can't rename across device types"); | 
|  |  | 
|  | devtab[cnew->type].rename(renamee, cnew, | 
|  | e.elems[e.ARRAY_SIZEs - 1], 0); | 
|  | poperror(); | 
|  |  | 
|  | if (m) | 
|  | putmhead(m); | 
|  | cclose(c); | 
|  | c = cnew; | 
|  | c->name = addelem(c->name, e.elems[e.ARRAY_SIZEs - 1]); | 
|  | break; | 
|  |  | 
|  | case Acreate: | 
|  | /* | 
|  | * We've already walked all but the last element. | 
|  | * If the last exists, try to open it OTRUNC. | 
|  | * If omode&OEXCL is set, just give up. | 
|  | */ | 
|  | e.ARRAY_SIZEs++; | 
|  | if (walk(&c, e.elems + e.ARRAY_SIZEs - 1, 1, wh, NULL) == 0) { | 
|  | if (omode & O_EXCL) | 
|  | error(EEXIST, ERROR_FIXME); | 
|  | omode |= O_TRUNC; | 
|  | goto 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 | 
|  | * 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 | 
|  | * 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 | 
|  | * when two rc subshells simultaneously update the same | 
|  | * environment variable. | 
|  | * | 
|  | * The implementation still admits a create/create/remove race: | 
|  | * (A) walk to file, fails | 
|  | * (B) walk to file, fails | 
|  | * (A) create file, succeeds, returns | 
|  | * (B) create file, fails | 
|  | * (A) remove file, succeeds, returns | 
|  | * (B) walk to file, return failure. | 
|  | * | 
|  | * This is hardly as common as the create/create race, and is really | 
|  | * not too much worse than what might happen if (B) got a hold of a | 
|  | * file descriptor and then the file was removed -- either way (B) can't do | 
|  | * anything with the result of the create call.  So we don't care about this race. | 
|  | * | 
|  | * Applications that care about more fine-grained decision of the races | 
|  | * can use the OEXCL flag to get at the underlying create(5) semantics; | 
|  | * by default we provide the common case. | 
|  | * | 
|  | * We need to stay behind the mount point in case we | 
|  | * need to do the first walk again (should the create fail). | 
|  | * | 
|  | * We also need to cross the mount point and find the directory | 
|  | * in the union in which we should be creating. | 
|  | * | 
|  | * The channel staying behind is c, the one moving forward is cnew. | 
|  | */ | 
|  | m = NULL; | 
|  | cnew = NULL;	/* is this assignment necessary? */ | 
|  | /* discard error */ | 
|  | if (!waserror()) {	/* try create */ | 
|  | if (wh->can_mount && findmount(&cnew, &m, c->type, c->dev, | 
|  | c->qid)) | 
|  | cnew = createdir(cnew, m); | 
|  | else { | 
|  | cnew = c; | 
|  | chan_incref(cnew); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * We need our own copy of the Chan because we're | 
|  | * about to send a create, which will move it.  Once we have | 
|  | * our own copy, we can fix the name, which might be wrong | 
|  | * if findmount gave us a new Chan. | 
|  | */ | 
|  | cnew = cunique(cnew); | 
|  | cnameclose(cnew->name); | 
|  | cnew->name = c->name; | 
|  | kref_get(&cnew->name->ref, 1); | 
|  |  | 
|  | cnew->flag |= omode & CEXTERNAL_FLAGS; | 
|  | devtab[cnew->type].create(cnew, e.elems[e.ARRAY_SIZEs - 1], | 
|  | omode & ~(O_EXCL | O_CLOEXEC), | 
|  | perm, ext); | 
|  | poperror(); | 
|  |  | 
|  | if (m) | 
|  | putmhead(m); | 
|  | cclose(c); | 
|  | c = cnew; | 
|  | c->name = addelem(c->name, e.elems[e.ARRAY_SIZEs - 1]); | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* create failed */ | 
|  | cclose(cnew); | 
|  | if (m) | 
|  | putmhead(m); | 
|  | 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 */ | 
|  | 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, wh, NULL) < 0) { | 
|  | set_errno(saved_errno); | 
|  | /* Report the error we had originally */ | 
|  | error(EFAIL, tmperrbuf); | 
|  | } | 
|  | strlcpy(current_errstr(), tmperrbuf, MAX_ERRSTR_LEN); | 
|  | omode |= O_TRUNC; | 
|  | goto Open; | 
|  |  | 
|  | default: | 
|  | panic("unknown namec access %d\n", amode); | 
|  | } | 
|  |  | 
|  | poperror(); | 
|  |  | 
|  | if (e.ARRAY_SIZEs > 0) | 
|  | strlcpy(get_cur_genbuf(), e.elems[e.ARRAY_SIZEs - 1], GENBUF_SZ); | 
|  | else | 
|  | strlcpy(get_cur_genbuf(), ".", GENBUF_SZ); | 
|  |  | 
|  | kfree(e.name); | 
|  | kfree(e.elems); | 
|  | kfree(e.off); | 
|  |  | 
|  | return c; | 
|  | } | 
|  |  | 
|  | struct chan *namec(char *name, int amode, int omode, uint32_t perm, void *ext) | 
|  | { | 
|  | struct walk_helper wh = {.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 '/': | 
|  | if (current) | 
|  | c = current->slash; | 
|  | else | 
|  | c = kern_slash; | 
|  | chan_incref(c); | 
|  | break; | 
|  |  | 
|  | case '#': | 
|  | wh.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]; | 
|  | } | 
|  | /* These devices have special attach functions that treat the char * | 
|  | * as a blob pointer */ | 
|  | if (!strcmp(devname, "mnt")) | 
|  | error(EINVAL, "can't namec-attach #mnt"); | 
|  | if (!strcmp(devname, "gtfs")) | 
|  | error(EINVAL, "can't namec-attach #gtfs"); | 
|  | /* 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, &wh, ext); | 
|  | } | 
|  |  | 
|  | struct chan *namec_from(struct chan *c, char *name, int amode, int omode, | 
|  | uint32_t perm, void *ext) | 
|  | { | 
|  | struct walk_helper wh = {.can_mount = true}; | 
|  |  | 
|  | 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, &wh, ext); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * name is valid. skip leading / and ./ as much as possible | 
|  | */ | 
|  | char *skipslash(char *name) | 
|  | { | 
|  | while (name[0] == '/' | 
|  | || (name[0] == '.' && (name[1] == 0 || name[1] == '/'))) | 
|  | name++; | 
|  | return name; | 
|  | } | 
|  |  | 
|  | char isfrog[256] = { | 
|  | /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1, | 
|  | /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1, | 
|  | /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1, | 
|  | /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1, | 
|  | ['/'] 1, | 
|  | [0x7f] 1, | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * Check that the name | 
|  | *  a) is in valid memory. | 
|  | *  b) is shorter than 2^16 bytes, so it can fit in a 9P string field. | 
|  | *  c) contains no frogs. | 
|  | * The first byte is known to be addressible by the requester, so the | 
|  | * routine works for kernel and user memory both. | 
|  | * The parameter slashok flags whether a slash character is an error | 
|  | * or a valid character. | 
|  | */ | 
|  | void validname(char *aname, int slashok) | 
|  | { | 
|  | char *ename, *name; | 
|  | int c; | 
|  |  | 
|  | name = aname; | 
|  | ename = memchr(name, 0, (1 << 16)); | 
|  |  | 
|  | if (ename == NULL || ename - name >= (1 << 16)) | 
|  | error(EINVAL, "Name too long"); | 
|  |  | 
|  | while (*name) { | 
|  | /* all characters above '~' are ok */ | 
|  | c = *(uint8_t *) name; | 
|  | #if 0 | 
|  | if (c >= Runeself) | 
|  | name += chartorune(&r, name); | 
|  | #endif | 
|  | if (c >= 0x7f) { | 
|  | error(EFAIL, "Akaros doesn't do UTF-8"); | 
|  | } else { | 
|  | if (isfrog[c]) | 
|  | if (!slashok || c != '/') { | 
|  | error(EINVAL, "%s (%p), at char %c", aname, aname, c); | 
|  | } | 
|  | name++; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void isdir(struct chan *c) | 
|  | { | 
|  | if (c->qid.type & QTDIR) | 
|  | return; | 
|  | error(ENOTDIR, ERROR_FIXME); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * This is necessary because there are many | 
|  | * pointers to the top of a given mount list: | 
|  | * | 
|  | *	- the mhead in the namespace hash table | 
|  | *	- the mhead in chans returned from findmount: | 
|  | *	  used in namec and then by unionread. | 
|  | *	- the mhead in chans returned from createdir: | 
|  | *	  used in the open/create race protect, which is gone. | 
|  | * | 
|  | * The RWlock in the Mhead protects the mount list it contains. | 
|  | * 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 | 
|  | * long as you are sure you have a unique reference to it. | 
|  | * | 
|  | * This comment might belong somewhere else. | 
|  | */ | 
|  | void putmhead(struct mhead *m) | 
|  | { | 
|  | if (m) | 
|  | kref_put(&m->ref); | 
|  | } | 
|  |  | 
|  | /* Given s, make a copy of a string with padding bytes in front.  Returns a | 
|  | * pointer to the start of the string and the memory to free in str_store. | 
|  | * | 
|  | * Free str_store with kfree. */ | 
|  | static char *pad_and_strdup(char *s, int padding, char **str_store) | 
|  | { | 
|  | char *store = kzmalloc(strlen(s) + 1 + padding, MEM_WAIT); | 
|  |  | 
|  | strlcpy(store + padding, s, strlen(s) + 1); | 
|  | *str_store = store; | 
|  | return store + padding; | 
|  | } | 
|  |  | 
|  | /* Walks a symlink c.  Returns the target chan, which could be the symlink | 
|  | * itself, if we're NO_FOLLOW.  On success, we'll decref the symlink and give | 
|  | * you a ref counted result. | 
|  | * | 
|  | * Returns NULL on error, and does not close the symlink.  Like regular walk, it | 
|  | * is all or nothing. */ | 
|  | static struct chan *walk_symlink(struct chan *symlink, struct walk_helper *wh, | 
|  | unsigned int nr_names_left) | 
|  | { | 
|  | struct dir *dir; | 
|  | char *link_name, *link_store; | 
|  | struct chan *from; | 
|  | Elemlist e = {0}; | 
|  |  | 
|  | /* mildly expensive: need to rlock the namespace */ | 
|  | if (is_mount_point(symlink)) | 
|  | return symlink; | 
|  | if (!nr_names_left && wh->no_follow) | 
|  | return symlink; | 
|  | if (wh->nr_loops >= WALK_MAX_NR_LOOPS) { | 
|  | set_error(ELOOP, "too many nested symlinks in walk"); | 
|  | return NULL; | 
|  | } | 
|  | dir = chandirstat(symlink); | 
|  | if (!dir) { | 
|  | /* Should propagate the error from dev.stat() */ | 
|  | return NULL; | 
|  | } | 
|  | if (!(dir->mode & DMSYMLINK)) { | 
|  | set_error(ELOOP, "symlink isn't a symlink!"); | 
|  | kfree(dir); | 
|  | return NULL; | 
|  | } | 
|  | link_name = pad_and_strdup(dir->ext, 3, &link_store); | 
|  | kfree(dir); | 
|  |  | 
|  | if (link_name[0] == '/') { | 
|  | if (current) | 
|  | from = current->slash; | 
|  | else | 
|  | from = kern_slash; | 
|  | } else { | 
|  | from = symlink; | 
|  | link_name -= 3; | 
|  | strncpy(link_name, "../", 3); | 
|  | if (!from->name) | 
|  | from->name = newcname(""); | 
|  | } | 
|  | /* we close this ref on failure or it gets walked to the result. */ | 
|  | chan_incref(from); | 
|  |  | 
|  | parsename(link_name, &e); | 
|  | kfree(link_store); | 
|  |  | 
|  | wh->nr_loops++; | 
|  | if (walk(&from, e.elems, e.ARRAY_SIZEs, wh, NULL) < 0) { | 
|  | cclose(from); | 
|  | from = NULL; | 
|  | } else { | 
|  | cclose(symlink); | 
|  | if (from->qid.type & QTSYMLINK) { | 
|  | symlink = from; | 
|  | from = walk_symlink(symlink, wh, nr_names_left); | 
|  | if (!from) | 
|  | cclose(symlink); | 
|  | } | 
|  | } | 
|  | wh->nr_loops--; | 
|  |  | 
|  | kfree(e.name); | 
|  | kfree(e.elems); | 
|  | kfree(e.off); | 
|  | return from; | 
|  | } |