blob: 682ebd6fcba0c171ba2265b1161d9de6d2514892 [file] [log] [blame]
/* 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 ? &current->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);
}