|  | /* 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-2018 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 <net/ip.h> | 
|  |  | 
|  | /* It looks like the intent of this code is to check any stat that we, the | 
|  | * kernel or our userspace, send out. | 
|  | * | 
|  | * Throws on error. */ | 
|  | void statcheck(uint8_t *buf, size_t nbuf) | 
|  | { | 
|  | uint8_t *ebuf; | 
|  | int i; | 
|  |  | 
|  | ebuf = buf + nbuf; | 
|  |  | 
|  | if (nbuf < STAT_FIX_LEN_9P) | 
|  | error(EINVAL, "%s: legacy chunk too short (%lu < %u)", __func__, | 
|  | nbuf, STAT_FIX_LEN_9P); | 
|  | if (nbuf != BIT16SZ + GBIT16(buf)) | 
|  | error(EINVAL, "%s: size mismatch (%lu != %u)", __func__, nbuf, | 
|  | BIT16SZ + GBIT16(buf)); | 
|  |  | 
|  | buf += STAT_FIX_LEN_9P - STAT_NR_STRINGS_9P * BIT16SZ; | 
|  |  | 
|  | /* Check the legacy strings that all stats have. */ | 
|  | for (i = 0; i < STAT_NR_STRINGS_9P; i++) { | 
|  | if (buf + BIT16SZ > ebuf) | 
|  | error(EINVAL, "%s: string %d (legacy) out of range", | 
|  | __func__, i); | 
|  | buf += BIT16SZ + GBIT16(buf); | 
|  | } | 
|  | /* Legacy 9p stats are OK | 
|  | * TODO: consider removing this.  We get them from userspace, e.g. | 
|  | * mkdir. */ | 
|  | if (buf == ebuf) | 
|  | return; | 
|  |  | 
|  | for (i = STAT_NR_STRINGS_9P; i < STAT_NR_STRINGS_AK; i++) { | 
|  | if (buf + BIT16SZ > ebuf) | 
|  | error(EINVAL, "%s: string %d (akaros) out of range", | 
|  | __func__, i); | 
|  | buf += BIT16SZ + GBIT16(buf); | 
|  | } | 
|  |  | 
|  | if (buf + __STAT_FIX_LEN_AK_NONSTRING > ebuf) | 
|  | error(EINVAL, "%s: akaros chunk too short (%ld < %u)", __func__, | 
|  | ebuf - buf, __STAT_FIX_LEN_AK_NONSTRING); | 
|  | buf += __STAT_FIX_LEN_AK_NONSTRING; | 
|  | if (buf != ebuf) | 
|  | error(EINVAL, "%s: %lu extra bytes", __func__, ebuf - buf); | 
|  | } | 
|  |  | 
|  | static char nullstring[] = ""; | 
|  |  | 
|  | unsigned int | 
|  | convM2D(uint8_t * buf, unsigned int nbuf, struct dir *d, char *strs) | 
|  | { | 
|  | uint8_t *p, *ebuf; | 
|  | char *sv[STAT_NR_STRINGS_AK] = {nullstring}; | 
|  | int i, ns; | 
|  | bool good_stat = false; | 
|  | size_t msg_sz = 0; | 
|  |  | 
|  | if (nbuf < STAT_FIX_LEN_9P) | 
|  | return 0; | 
|  |  | 
|  | /* This M might not have all the fields we expect.  We'll ensure the | 
|  | * strings have the right values later.  We still need to initialize all | 
|  | * of the non-string extended fields. */ | 
|  | init_empty_dir(d); | 
|  |  | 
|  | p = buf; | 
|  | /* They might have given us more than one M, so we need to use the size | 
|  | * field to determine the real end of this M. */ | 
|  | msg_sz = GBIT16(p) + BIT16SZ; | 
|  | ebuf = buf + MIN(nbuf, msg_sz); | 
|  |  | 
|  | p += BIT16SZ;	/* jump over size */ | 
|  | d->type = GBIT16(p);            p += BIT16SZ; | 
|  | d->dev = GBIT32(p);             p += BIT32SZ; | 
|  | d->qid.type = GBIT8(p);         p += BIT8SZ; | 
|  | d->qid.vers = GBIT32(p);        p += BIT32SZ; | 
|  | d->qid.path = GBIT64(p);        p += BIT64SZ; | 
|  | d->mode = GBIT32(p);            p += BIT32SZ; | 
|  | /* Get a first attempt at atime/mtime.  Revisit this in 2038. */ | 
|  | d->atime.tv_sec = GBIT32(p);    p += BIT32SZ; | 
|  | d->mtime.tv_sec = GBIT32(p);    p += BIT32SZ; | 
|  | d->length = GBIT64(p);          p += BIT64SZ; | 
|  |  | 
|  | /* They might have asked for -1, meaning "don't touch".  Need to convert | 
|  | * that to our 64 bit times. */ | 
|  | if ((int32_t)d->atime.tv_sec == -1) | 
|  | d->atime.tv_sec = ~0; | 
|  | if ((int32_t)d->mtime.tv_sec == -1) | 
|  | d->mtime.tv_sec = ~0; | 
|  |  | 
|  | /* Anything beyond the legacy 9p strings might not be supported.  Though | 
|  | * if you have more, you probably have at least EVH's 9p2000.u | 
|  | * extensions.  Once we get all of the legacy strings, we have a good | 
|  | * stat. */ | 
|  | for (i = 0; i < STAT_NR_STRINGS_AK; i++) { | 
|  | if (i == STAT_NR_STRINGS_9P) | 
|  | good_stat = true; | 
|  | if (p + BIT16SZ > ebuf) | 
|  | goto out; | 
|  | ns = GBIT16(p);	p += BIT16SZ; | 
|  | if (p + ns > ebuf) | 
|  | goto out; | 
|  | if (strs) { | 
|  | sv[i] = strs; | 
|  | memmove(strs, p, ns); | 
|  | strs += ns; | 
|  | *strs++ = '\0'; | 
|  | } | 
|  | p += ns; | 
|  | } | 
|  |  | 
|  | /* Check for 9p2000.u */ | 
|  | if (p + 3 * BIT32SZ > ebuf) | 
|  | goto out; | 
|  | d->n_uid = GBIT32(p);           p += BIT32SZ; | 
|  | d->n_gid = GBIT32(p);           p += BIT32SZ; | 
|  | d->n_muid = GBIT32(p);          p += BIT32SZ; | 
|  |  | 
|  | /* Check for extended timespecs */ | 
|  | if (p + 4 * (2 * BIT64SZ) > ebuf) | 
|  | goto out; | 
|  | d->atime.tv_sec = GBIT64(p);    p += BIT64SZ; | 
|  | d->atime.tv_nsec = GBIT64(p);   p += BIT64SZ; | 
|  | d->btime.tv_sec = GBIT64(p);    p += BIT64SZ; | 
|  | d->btime.tv_nsec = GBIT64(p);   p += BIT64SZ; | 
|  | d->ctime.tv_sec = GBIT64(p);    p += BIT64SZ; | 
|  | d->ctime.tv_nsec = GBIT64(p);   p += BIT64SZ; | 
|  | d->mtime.tv_sec = GBIT64(p);    p += BIT64SZ; | 
|  | d->mtime.tv_nsec = GBIT64(p);   p += BIT64SZ; | 
|  |  | 
|  | /* Fall-through */ | 
|  | out: | 
|  | if (!good_stat) | 
|  | return 0; | 
|  | d->name = sv[0]; | 
|  | d->uid = sv[1]; | 
|  | d->gid = sv[2]; | 
|  | d->muid = sv[3]; | 
|  | d->ext = sv[4]; | 
|  | return p - buf; | 
|  | } |