akaros/kern/src/ns/dev.c
<<
>>
Prefs
   1/* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
   2 * Portions Copyright © 1997-1999 Vita Nuova Limited
   3 * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
   4 *                                (www.vitanuova.com)
   5 * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
   6 *
   7 * Modified for the Akaros operating system:
   8 * Copyright (c) 2013-2014 The Regents of the University of California
   9 * Copyright (c) 2013-2015 Google Inc.
  10 *
  11 * Permission is hereby granted, free of charge, to any person obtaining a copy
  12 * of this software and associated documentation files (the "Software"), to deal
  13 * in the Software without restriction, including without limitation the rights
  14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15 * copies of the Software, and to permit persons to whom the Software is
  16 * furnished to do so, subject to the following conditions:
  17 *
  18 * The above copyright notice and this permission notice shall be included in
  19 * all copies or substantial portions of the Software.
  20 *
  21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
  24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  27 * SOFTWARE. */
  28
  29#include <slab.h>
  30#include <kmalloc.h>
  31#include <kref.h>
  32#include <string.h>
  33#include <stdio.h>
  34#include <assert.h>
  35#include <error.h>
  36#include <cpio.h>
  37#include <pmap.h>
  38#include <smp.h>
  39#include <net/ip.h>
  40
  41extern uint32_t kerndate;
  42extern struct username eve;
  43
  44void mkqid(struct qid *q, int64_t path, uint32_t vers, int type)
  45{
  46        q->type = type;
  47        q->vers = vers;
  48        q->path = path;
  49}
  50
  51int devno(const char *name, int user)
  52{
  53        int i;
  54
  55        for (i = 0; &devtab[i] < __devtabend; i++) {
  56                if (!strcmp(devtab[i].name, name))
  57                        return i;
  58        }
  59        if (user == 0)
  60                panic("Lookup of dev :%s: failed", name);
  61
  62        return -1;
  63}
  64
  65void
  66devdir(struct chan *c, struct qid qid, char *n,
  67           int64_t length, char *user, long perm, struct dir *db)
  68{
  69        struct timespec now = nsec2timespec(epoch_nsec());
  70
  71        db->name = n;
  72        if (c->flag & CMSG)
  73                qid.type |= QTMOUNT;
  74        db->qid = qid;
  75        db->type = c->type;     /* used to use the dev's dc here */
  76        db->dev = c->dev;
  77        db->mode = perm;
  78        db->mode |= qid.type << 24;
  79        db->length = length;
  80        db->uid = user;
  81        db->gid = eve.name;
  82        db->muid = user;
  83        db->ext = NULL;
  84        /* TODO: once we figure out what to do for uid/gid, then we can try to
  85         * tie that to the n_uid.  Or just ignore it, and only use that as a
  86         * pass-through for 9p2000.u. */
  87        db->n_uid = 0;
  88        db->n_gid = 0;
  89        db->n_muid = 0;
  90        /* TODO: what does devdir really want? */
  91        db->atime = now;
  92        db->btime = now;
  93        db->ctime = now;
  94        db->mtime = now;
  95}
  96
  97/*
  98 * The zeroth element of the table MUST be the directory itself, or '.' (dot),
  99 * for processing '..' (dot-dot). Specifically, if i==DEVDOTDOT, we call devdir
 100 * on the *directory* (that is, dot), as opposed to children of the directory.
 101 * The rest of the system assumes that the first entry in the table refers to
 102 * the directory, and by convention this is named '.' (dot). This is confusing.
 103 *
 104 * Any entry with qid verion of -1 will return 0, indicating that the value is
 105 * valid but there is nothing there, so continue walking.
 106 *
 107 * TODO(cross): Document devgen and clean this mess up. Devgen should probably
 108 * be removed and replaced with a smarter data structure.
 109 *
 110 * Keep in mind that the expected behavior of gen functions that interoperate
 111 * with dev functions (e.g. devdirread()) is that files are directly genned, but
 112 * not directories.  Directories will fail to gen, and devstat() just makes
 113 * something up.  See also:
 114 * https://github.com/brho/plan9/blob/89d43d2262ad43eb4b26c2a8d6a27cfeddb33828/nix/sys/src/nix/port/dev.c#L74
 115 *
 116 * The comment about genning a file's siblings needs a grain of salt too.  Look
 117 * through ipgen().  I think it's what I call "direct genning." */
 118int devgen(struct chan *c, char *unused_name, struct dirtab *tab, int ntab,
 119           int i, struct dir *dp)
 120{
 121        if (tab == NULL)
 122                return -1;
 123        if (i != DEVDOTDOT) {
 124                /* Skip over the first element, that for the directory itself */
 125                i++;
 126                if (i < 0 || ntab <= i)
 127                        return -1;
 128                tab += i;
 129        }
 130        if (tab->qid.vers == -1)
 131                return 0;
 132        devdir(c, tab->qid, tab->name, tab->length, eve.name, tab->perm, dp);
 133        return 1;
 134}
 135
 136void devreset(void)
 137{
 138}
 139
 140void devinit(void)
 141{
 142}
 143
 144void devshutdown(void)
 145{
 146}
 147
 148struct chan *devattach(const char *name, char *spec)
 149{
 150        struct chan *c;
 151        char *buf;
 152        size_t buflen;
 153
 154        c = newchan();
 155        mkqid(&c->qid, 0, 0, QTDIR);
 156        c->type = devno(name, 0);
 157        if (spec == NULL)
 158                spec = "";
 159        /* 1 for #, 1 for ., 1 for \0 */
 160        buflen = strlen(name) + strlen(spec) + 3;
 161        buf = kzmalloc(buflen, MEM_WAIT);
 162        snprintf(buf, sizeof(buf), "#%s.%s", name, spec);
 163        c->name = newcname(buf);
 164        kfree(buf);
 165        return c;
 166}
 167
 168struct chan *devclone(struct chan *c)
 169{
 170        struct chan *nc;
 171
 172        /* In plan 9, you couldn't clone an open chan.  We're allowing it,
 173         * possibly foolishly.  The new chan is a non-open, "kernel internal"
 174         * chan.  Note that c->flag isn't set, for instance.  c->mode is, which
 175         * might be a problem.  The newchan should eventually have a device's
 176         * open called on it, at which point it upgrades from a kernel internal
 177         * chan to one that can refer to an object in the device (e.g. grab a
 178         * refcnt on a conversation in #ip).
 179         *
 180         * Either we allow devclones of open chans, or O_PATH walks do not open
 181         * a file.  It's nice to allow the device to do something for O_PATH,
 182         * but perhaps that is not critical.  However, if we can't clone an
 183         * opened chan, then we can *only* openat from an FD that is O_PATH,
 184         * which is not the spec (and not as useful). */
 185        if ((c->flag & COPEN) && !(c->flag & O_PATH))
 186                panic("clone of non-O_PATH open file type %s\n",
 187                      devtab[c->type].name);
 188
 189        nc = newchan();
 190        nc->type = c->type;
 191        nc->dev = c->dev;
 192        nc->mode = c->mode;
 193        nc->qid = c->qid;
 194        nc->offset = c->offset;
 195        nc->umh = NULL;
 196        nc->mountid = c->mountid;
 197        nc->aux = c->aux;
 198        nc->mqid = c->mqid;
 199        nc->mcp = c->mcp;
 200        return nc;
 201}
 202
 203struct walkqid *devwalk(struct chan *c, struct chan *nc, char **name, int nname,
 204                        struct dirtab *tab, int ntab, Devgen * gen)
 205{
 206        ERRSTACK(1);
 207        int i, j;
 208        volatile int alloc;     /* to keep waserror from optimizing this out */
 209        struct walkqid *wq;
 210        char *n;
 211        struct dir dir;
 212
 213        if (nname > 0)
 214                isdir(c);
 215
 216        alloc = 0;
 217        wq = kzmalloc(sizeof(struct walkqid) + nname * sizeof(struct qid),
 218                                  MEM_WAIT);
 219        if (waserror()) {
 220                if (alloc && wq->clone != NULL)
 221                        cclose(wq->clone);
 222                kfree(wq);
 223                poperror();
 224                return NULL;
 225        }
 226        if (nc == NULL) {
 227                nc = devclone(c);
 228                /* inferno was setting this to 0, assuming it was devroot.
 229                 * lining up with chanrelease and newchan */
 230                nc->type = -1;  /* device doesn't know about this channel yet */
 231                alloc = 1;
 232        }
 233        wq->clone = nc;
 234
 235        dir.qid.path = 0;
 236        for (j = 0; j < nname; j++) {
 237                if (!(nc->qid.type & QTDIR)) {
 238                        if (j == 0)
 239                                error(ENOTDIR, ERROR_FIXME);
 240                        goto Done;
 241                }
 242                n = name[j];
 243                if (strcmp(n, ".") == 0) {
 244Accept:
 245                        wq->qid[wq->nqid++] = nc->qid;
 246                        continue;
 247                }
 248                if (strcmp(n, "..") == 0) {
 249                        (*gen) (nc, NULL, tab, ntab, DEVDOTDOT, &dir);
 250                        nc->qid = dir.qid;
 251                        goto Accept;
 252                }
 253                /*
 254                 * Ugly problem: If we're using devgen, make sure we're
 255                 * walking the directory itself, represented by the first
 256                 * entry in the table, and not trying to step into a sub-
 257                 * directory of the table, e.g. /net/net. Devgen itself
 258                 * should take care of the problem, but it doesn't have
 259                 * the necessary information (that we're doing a walk).
 260                 */
 261                if (gen == devgen && nc->qid.path != tab[0].qid.path)
 262                        goto Notfound;
 263                dir.qid.path = 0;
 264                for (i = 0;; i++) {
 265                        switch ((*gen) (nc, n, tab, ntab, i, &dir)) {
 266                        case -1:
 267                                printd("DEVWALK -1, i was %d, want path %p\n",
 268                                       i, c->qid.path);
 269Notfound:
 270                                set_error(ENOENT,
 271                                          "could not find name %s, dev %s", n,
 272                                          c->type == -1 ? "no dev" :
 273                                          devtab[c->type].name);
 274                                if (j == 0)
 275                                        error_jmp();
 276                                goto Done;
 277                        case 0:
 278                                printd("DEVWALK continue, i was %d\n", i);
 279                                continue;
 280                        case 1:
 281                                printd("DEVWALK gen returns path %p name %s, want path %p\n",
 282                                       dir.qid.path, dir.name, c->qid.path);
 283                                if (strcmp(n, dir.name) == 0) {
 284                                        nc->qid = dir.qid;
 285                                        goto Accept;
 286                                }
 287                                continue;
 288                        }
 289                }
 290        }
 291        /*
 292         * We processed at least one name, so will return some data.
 293         * If we didn't process all nname entries succesfully, we drop
 294         * the cloned channel and return just the Qids of the walks.
 295         */
 296Done:
 297        poperror();
 298        if (wq->nqid < nname) {
 299                if (alloc)
 300                        cclose(wq->clone);
 301                wq->clone = NULL;
 302        } else if (wq->clone) {
 303                /* attach cloned channel to same device */
 304                wq->clone->type = c->type;
 305        } else {
 306                /* Not sure this is possible, would like to know. */
 307                warn_once("had enough names, but still no wq->clone");
 308        }
 309        return wq;
 310}
 311
 312/* Helper, makes a stat in @dp, given @n bytes, from chan @c's contents in @dir.
 313 * Throws on error, returns the size used on success. */
 314size_t dev_make_stat(struct chan *c, struct dir *dir, uint8_t *dp, size_t n)
 315{
 316        if (c->flag & CMSG)
 317                dir->mode |= DMMOUNT;
 318        n = convD2M(dir, dp, n);
 319        if (n == 0)
 320                error(EINVAL, ERROR_FIXME);
 321        return n;
 322}
 323
 324size_t devstat(struct chan *c, uint8_t *db, size_t n, struct dirtab *tab,
 325               int ntab, Devgen *gen)
 326{
 327        int i;
 328        struct dir dir;
 329        char *p, *elem;
 330
 331        dir.qid.path = 0;
 332        for (i = 0;; i++)
 333                switch ((*gen) (c, NULL, tab, ntab, i, &dir)) {
 334                case -1:
 335                        if (c->qid.type & QTDIR) {
 336                                printd("DEVSTAT got a dir: %llu\n",
 337                                       c->qid.path);
 338                                if (c->name == NULL)
 339                                        elem = "???";
 340                                else if (strcmp(c->name->s, "/") == 0)
 341                                        elem = "/";
 342                                else
 343                                        for (elem = p = c->name->s; *p; p++)
 344                                                if (*p == '/')
 345                                                        elem = p + 1;
 346                                devdir(c, c->qid, elem, 0, eve.name, DMDIR |
 347                                       0555, &dir);
 348                                n = convD2M(&dir, db, n);
 349                                if (n == 0)
 350                                        error(EINVAL, ERROR_FIXME);
 351                                return n;
 352                        }
 353                        printd("DEVSTAT fails:%s %llu\n", devtab[c->type].name,
 354                                   c->qid.path);
 355                        error(ENOENT, ERROR_FIXME);
 356                case 0:
 357                        printd("DEVSTAT got 0\n");
 358                        break;
 359                case 1:
 360                        printd("DEVSTAT gen returns path %p name %s, want path %p\n",
 361                               dir.qid.path, dir.name, c->qid.path);
 362                        if (c->qid.path == dir.qid.path)
 363                                return dev_make_stat(c, &dir, db, n);
 364                        break;
 365                }
 366}
 367
 368long devdirread(struct chan *c, char *d, long n, struct dirtab *tab, int ntab,
 369                Devgen * gen)
 370{
 371        long m, dsz;
 372        /* this is gross. Make it 2 so we have room at the end for
 373         * bad things.
 374         */
 375        struct dir dir[4];
 376
 377        dir[0].qid.path = 0;
 378        for (m = 0; m < n; c->dri++) {
 379                switch ((*gen) (c, NULL, tab, ntab, c->dri, &dir[0])) {
 380                case -1:
 381                        printd("DEVDIRREAD got -1, asked for s = %d\n", c->dri);
 382                        return m;
 383
 384                case 0:
 385                        printd("DEVDIRREAD got 0, asked for s = %d\n", c->dri);
 386                        break;
 387
 388                case 1:
 389                        printd("DEVDIRREAD got 1, asked for s = %d\n", c->dri);
 390                        dsz = convD2M(&dir[0], (uint8_t *) d, n - m);
 391                        /* <= not < because this isn't stat; read is stuck */
 392                        if (dsz <= BIT16SZ) {
 393                                if (m == 0)
 394                                        error(ENODATA, ERROR_FIXME);
 395                                return m;
 396                        }
 397                        m += dsz;
 398                        d += dsz;
 399                        break;
 400                }
 401        }
 402
 403        return m;
 404}
 405
 406/*
 407 * Throws an error if open permission not granted for current->user.name
 408 */
 409void devpermcheck(char *fileuid, uint32_t perm, int omode)
 410{
 411        if (!caller_has_perms(fileuid, perm, omode))
 412                error(EPERM, "permcheck(user: %s, rwx: 0%o, omode 0%o) failed",
 413                      fileuid, perm, omode);
 414}
 415
 416struct chan *devopen(struct chan *c, int omode, struct dirtab *tab, int ntab,
 417                                         Devgen * gen)
 418{
 419        int i;
 420        struct dir dir;
 421
 422        dir.qid.path = 0;
 423        for (i = 0;; i++) {
 424                switch ((*gen) (c, NULL, tab, ntab, i, &dir)) {
 425                case -1:
 426                        goto Return;
 427                case 0:
 428                        break;
 429                case 1:
 430                        if (c->qid.path == dir.qid.path) {
 431                                devpermcheck(dir.uid, dir.mode, omode);
 432                                goto Return;
 433                        }
 434                        break;
 435                }
 436        }
 437Return:
 438        c->offset = 0;
 439        if ((c->qid.type & QTDIR) && (omode & O_WRITE))
 440                error(EACCES, "Tried opening dir with non-read-only mode %o",
 441                      omode);
 442        c->mode = openmode(omode);
 443        c->flag |= COPEN;
 444        return c;
 445}
 446
 447void devcreate(struct chan *c, char *unused_char_p_t, int unused_int,
 448               uint32_t u, char *ext)
 449{
 450        error(EPERM, ERROR_FIXME);
 451}
 452
 453struct block *devbread(struct chan *c, size_t n, off64_t offset)
 454{
 455        ERRSTACK(1);
 456        struct block *bp;
 457
 458        bp = block_alloc(n, MEM_WAIT);
 459        if (bp == 0)
 460                error(ENOMEM, ERROR_FIXME);
 461        if (waserror()) {
 462                freeb(bp);
 463                nexterror();
 464        }
 465        bp->wp += devtab[c->type].read(c, bp->wp, n, offset);
 466        poperror();
 467        return bp;
 468}
 469
 470size_t devbwrite(struct chan *c, struct block *bp, off64_t offset)
 471{
 472        ERRSTACK(1);
 473        long n;
 474
 475        if (waserror()) {
 476                freeb(bp);
 477                nexterror();
 478        }
 479        n = devtab[c->type].write(c, bp->rp, BLEN(bp), offset);
 480        poperror();
 481        freeb(bp);
 482
 483        return n;
 484}
 485
 486void devremove(struct chan *c)
 487{
 488        error(EPERM, ERROR_FIXME);
 489}
 490
 491size_t devwstat(struct chan *c, uint8_t *unused_uint8_p_t, size_t i)
 492{
 493        error(EPERM, ERROR_FIXME);
 494        return 0;
 495}
 496
 497void devpower(int i)
 498{
 499        error(EPERM, ERROR_FIXME);
 500}
 501
 502#if 0
 503int devconfig(int unused_int, char *c, DevConf *)
 504{
 505        error(EPERM, ERROR_FIXME);
 506        return 0;
 507}
 508#endif
 509
 510char *devchaninfo(struct chan *chan, char *ret, size_t ret_l)
 511{
 512        snprintf(ret, ret_l, "qid.path: %p, qid.type: %02x", chan->qid.path,
 513                         chan->qid.type);
 514        return ret;
 515}
 516
 517/*
 518 * check that the name in a wstat is plausible
 519 */
 520void validwstatname(char *name)
 521{
 522        validname(name, 0);
 523        if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
 524                error(EINVAL, ERROR_FIXME);
 525}
 526
 527struct dev *devbyname(char *name)
 528{
 529        int i;
 530
 531        for (i = 0; &devtab[i] < __devtabend; i++)
 532                if (strcmp(devtab[i].name, name) == 0)
 533                        return &devtab[i];
 534        return NULL;
 535}
 536