|  | /* misc utilities for plan9 */ | 
|  |  | 
|  | #include <ns.h> | 
|  | #include <string.h> | 
|  | #include <err.h> | 
|  | #include <syscall.h> | 
|  | #include <smp.h> | 
|  |  | 
|  | /* Copies n bytes from mem + offset into buf, similar to a read() call. */ | 
|  | int readmem(unsigned long offset, char *buf, unsigned long n, | 
|  | const void *mem, size_t mem_len) | 
|  | { | 
|  | if (offset >= mem_len) | 
|  | return 0; | 
|  | if (offset + n > mem_len) | 
|  | n = mem_len - offset; | 
|  | memmove(buf, mem + offset, n); | 
|  | return n; | 
|  | } | 
|  |  | 
|  | /* Read a num/string to user mode, accounting for offset.  Not a huge fan of the | 
|  | * 'size' parameter (the old plan9 users just picked NUMSIZE (12), though they | 
|  | * seem to want to limit it).  */ | 
|  | static int __readnum(unsigned long off, char *buf, unsigned long n, | 
|  | unsigned long val, size_t size, const char *fmt) | 
|  | { | 
|  | char tmp[64]; | 
|  |  | 
|  | size = MIN(sizeof(tmp), size); | 
|  | /* we really need the %* format. */ | 
|  | size = snprintf(tmp, size, fmt, val); | 
|  | /* size is now strlen, so the rest of this is just like readstr. */ | 
|  | /* always include the \0 */ | 
|  | return readmem(off, buf, n, tmp, size + 1); | 
|  | } | 
|  |  | 
|  | int readnum(unsigned long off, char *buf, unsigned long n, unsigned long val, | 
|  | size_t size) | 
|  | { | 
|  | return __readnum(off, buf, n, val, size, "%lu"); | 
|  | } | 
|  |  | 
|  | int readnum_hex(unsigned long off, char *buf, unsigned long n, | 
|  | unsigned long val, size_t size) | 
|  | { | 
|  | return __readnum(off, buf, n, val, size, "0x%lx"); | 
|  | } | 
|  |  | 
|  | int readstr(unsigned long offset, char *buf, unsigned long n, const char *str) | 
|  | { | 
|  | return readmem(offset, buf, n, str, strlen(str)); | 
|  | } | 
|  |  | 
|  | /* Helper: extracts a long from a user buffer (in text). */ | 
|  | unsigned long strtoul_from_ubuf(void *ubuf, size_t count, int base) | 
|  | { | 
|  | char num64[NUMSIZE64]; | 
|  |  | 
|  | /* want to give strtoul a null-terminated buf (can't handle random | 
|  | * user strings) */ | 
|  | if (count > sizeof(num64)) { | 
|  | set_errno(EINVAL); | 
|  | error(EFAIL, "attempted to write %d chars, max %d", count, | 
|  | sizeof(num64)); | 
|  | } | 
|  | memcpy(num64, ubuf, count); | 
|  | num64[count] = 0;	/* enforce trailing 0 */ | 
|  | return strtoul(num64, 0, base); | 
|  | } | 
|  |  | 
|  | /* Converts open mode flags, e.g. O_RDWR, to a rwx------ value, e.g. S_IRUSR */ | 
|  | int omode_to_rwx(int open_flags) | 
|  | { | 
|  | static int rwx_opts[] = { [O_RDWR | O_EXEC] = 0700, | 
|  | [O_RDWR] = 0600, | 
|  | [O_READ | O_EXEC] = 0500, | 
|  | [O_READ] = 0400, | 
|  | [O_WRITE | O_EXEC] = 0300, | 
|  | [O_WRITE] = 0200, | 
|  | [O_EXEC] = 0100 }; | 
|  |  | 
|  | return rwx_opts[open_flags & O_ACCMODE]; | 
|  | } | 
|  |  | 
|  | /* Converts open mode flags related to permissions, e.g. O_RDWR, to 9p.  It's a | 
|  | * bit ugly, since 9p (according to http://man.cat-v.org/plan_9/5/open) seems to | 
|  | * require that O_EXEC is mutually exclusive with the others.  If someone on | 
|  | * Akaros wants EXEC, we'll just substitute READ. */ | 
|  | int omode_to_9p_accmode(int open_flags) | 
|  | { | 
|  | static int acc_opts[] = { [O_RDWR | O_EXEC] = 2, | 
|  | [O_WRITE | O_EXEC] = 2, | 
|  | [O_READ | O_EXEC] = 0, | 
|  | [O_EXEC] = 0, | 
|  | [O_RDWR] = 2, | 
|  | [O_WRITE] = 1, | 
|  | [O_READ] = 0, | 
|  | [0] = 0 /* we can't express no permissions */ | 
|  | }; | 
|  |  | 
|  | return acc_opts[open_flags & O_ACCMODE]; | 
|  | } | 
|  |  | 
|  | int access_bits_to_omode(int access_bits) | 
|  | { | 
|  | int omode = 0; | 
|  |  | 
|  | if (R_OK) | 
|  | omode |= O_READ; | 
|  | if (W_OK) | 
|  | omode |= O_WRITE; | 
|  | if (X_OK) | 
|  | omode |= O_EXEC; | 
|  | return omode; | 
|  | } | 
|  |  | 
|  | /* TODO: This assumes UID isn't concurrently changed */ | 
|  | bool caller_is_username(char *uid) | 
|  | { | 
|  | struct username *current_user = current ? ¤t->user : &eve; | 
|  |  | 
|  | return strcmp(current_user->name, uid) == 0; | 
|  | } | 
|  |  | 
|  | /* Checks if current->user has permissions for omode access on something with | 
|  | * {owner_fileuid, perm} */ | 
|  | bool caller_has_perms(char *fileuid, uint32_t perm, int omode) | 
|  | { | 
|  | int rwx; | 
|  |  | 
|  | perm &= S_PMASK;	/* technically unnecessary; good for clarity */ | 
|  | /* select user, group, or other from the traditional rwxrwxrwx, shifting | 
|  | * into the upper-most position */ | 
|  | if (caller_is_username(fileuid)) | 
|  | perm <<= 0; | 
|  | else if (iseve()) | 
|  | perm <<= 3; | 
|  | else | 
|  | perm <<= 6; | 
|  | /* translate omode into things like S_IRUSR (just one set of rwx------). | 
|  | * Plan 9 originally only returned 0400 0200 0600 and 0100 here; it | 
|  | * didn't seem to handle O_EXEC being mixed readable or writable. */ | 
|  | rwx = omode_to_rwx(omode); | 
|  | return (rwx & perm) == rwx; | 
|  | } | 
|  |  | 
|  | bool caller_has_dir_perms(struct dir *dir, int omode) | 
|  | { | 
|  | return caller_has_perms(dir->uid, READ_ONCE(dir->mode), omode); | 
|  | } | 
|  |  | 
|  | void dir_perm_check(struct dir *dir, int omode) | 
|  | { | 
|  | devpermcheck(dir->uid, READ_ONCE(dir->mode) & S_PMASK, omode); | 
|  | } |