akaros/kern/src/ns/util.c
<<
>>
Prefs
   1/* misc utilities for plan9 */
   2
   3#include <ns.h>
   4#include <string.h>
   5#include <err.h>
   6#include <syscall.h>
   7#include <smp.h>
   8
   9/* Copies n bytes from mem + offset into buf, similar to a read() call. */
  10int readmem(unsigned long offset, char *buf, unsigned long n,
  11                        const void *mem, size_t mem_len)
  12{
  13        if (offset >= mem_len)
  14                return 0;
  15        if (offset + n > mem_len)
  16                n = mem_len - offset;
  17        memmove(buf, mem + offset, n);
  18        return n;
  19}
  20
  21/* Read a num/string to user mode, accounting for offset.  Not a huge fan of the
  22 * 'size' parameter (the old plan9 users just picked NUMSIZE (12), though they
  23 * seem to want to limit it).  */
  24static int __readnum(unsigned long off, char *buf, unsigned long n,
  25                     unsigned long val, size_t size, const char *fmt)
  26{
  27        char tmp[64];
  28
  29        size = MIN(sizeof(tmp), size);
  30        /* we really need the %* format. */
  31        size = snprintf(tmp, size, fmt, val);
  32        /* size is now strlen, so the rest of this is just like readstr. */
  33        /* always include the \0 */
  34        return readmem(off, buf, n, tmp, size + 1);
  35}
  36
  37int readnum(unsigned long off, char *buf, unsigned long n, unsigned long val,
  38            size_t size)
  39{
  40        return __readnum(off, buf, n, val, size, "%lu");
  41}
  42
  43int readnum_hex(unsigned long off, char *buf, unsigned long n,
  44                unsigned long val, size_t size)
  45{
  46        return __readnum(off, buf, n, val, size, "0x%lx");
  47}
  48
  49int readstr(unsigned long offset, char *buf, unsigned long n, const char *str)
  50{
  51        return readmem(offset, buf, n, str, strlen(str));
  52}
  53
  54/* Helper: extracts a long from a user buffer (in text). */
  55unsigned long strtoul_from_ubuf(void *ubuf, size_t count, int base)
  56{
  57        char num64[NUMSIZE64];
  58
  59        /* want to give strtoul a null-terminated buf (can't handle random
  60         * user strings) */
  61        if (count > sizeof(num64)) {
  62                set_errno(EINVAL);
  63                error(EFAIL, "attempted to write %d chars, max %d", count,
  64                          sizeof(num64));
  65        }
  66        memcpy(num64, ubuf, count);
  67        num64[count] = 0;       /* enforce trailing 0 */
  68        return strtoul(num64, 0, base);
  69}
  70
  71/* Converts open mode flags, e.g. O_RDWR, to a rwx------ value, e.g. S_IRUSR */
  72int omode_to_rwx(int open_flags)
  73{
  74        static int rwx_opts[] = { [O_RDWR | O_EXEC] = 0700,
  75                                  [O_RDWR] = 0600,
  76                                  [O_READ | O_EXEC] = 0500,
  77                                  [O_READ] = 0400,
  78                                  [O_WRITE | O_EXEC] = 0300,
  79                                  [O_WRITE] = 0200,
  80                                  [O_EXEC] = 0100 };
  81
  82        return rwx_opts[open_flags & O_ACCMODE];
  83}
  84
  85/* Converts open mode flags related to permissions, e.g. O_RDWR, to 9p.  It's a
  86 * bit ugly, since 9p (according to http://man.cat-v.org/plan_9/5/open) seems to
  87 * require that O_EXEC is mutually exclusive with the others.  If someone on
  88 * Akaros wants EXEC, we'll just substitute READ. */
  89int omode_to_9p_accmode(int open_flags)
  90{
  91        static int acc_opts[] = { [O_RDWR | O_EXEC] = 2,
  92                                  [O_WRITE | O_EXEC] = 2,
  93                                  [O_READ | O_EXEC] = 0,
  94                                  [O_EXEC] = 0,
  95                                  [O_RDWR] = 2,
  96                                  [O_WRITE] = 1,
  97                                  [O_READ] = 0,
  98                                  [0] = 0 /* we can't express no permissions */
  99                                  };
 100
 101        return acc_opts[open_flags & O_ACCMODE];
 102}
 103
 104int access_bits_to_omode(int access_bits)
 105{
 106        int omode = 0;
 107
 108        if (R_OK)
 109                omode |= O_READ;
 110        if (W_OK)
 111                omode |= O_WRITE;
 112        if (X_OK)
 113                omode |= O_EXEC;
 114        return omode;
 115}
 116
 117/* TODO: This assumes UID isn't concurrently changed */
 118bool caller_is_username(char *uid)
 119{
 120        struct username *current_user = current ? &current->user : &eve;
 121
 122        return strcmp(current_user->name, uid) == 0;
 123}
 124
 125/* Checks if current->user has permissions for omode access on something with
 126 * {owner_fileuid, perm} */
 127bool caller_has_perms(char *fileuid, uint32_t perm, int omode)
 128{
 129        int rwx;
 130
 131        perm &= S_PMASK;        /* technically unnecessary; good for clarity */
 132        /* select user, group, or other from the traditional rwxrwxrwx, shifting
 133         * into the upper-most position */
 134        if (caller_is_username(fileuid))
 135                perm <<= 0;
 136        else if (iseve())
 137                perm <<= 3;
 138        else
 139                perm <<= 6;
 140        /* translate omode into things like S_IRUSR (just one set of rwx------).
 141         * Plan 9 originally only returned 0400 0200 0600 and 0100 here; it
 142         * didn't seem to handle O_EXEC being mixed readable or writable. */
 143        rwx = omode_to_rwx(omode);
 144        return (rwx & perm) == rwx;
 145}
 146
 147bool caller_has_dir_perms(struct dir *dir, int omode)
 148{
 149        return caller_has_perms(dir->uid, READ_ONCE(dir->mode), omode);
 150}
 151
 152void dir_perm_check(struct dir *dir, int omode)
 153{
 154        devpermcheck(dir->uid, READ_ONCE(dir->mode) & S_PMASK, omode);
 155}
 156