9ns: Fix devtab function pointer signatures
[akaros.git] / kern / src / ns / util.c
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. */
10 int 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).  */
24 static 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         size = MIN(sizeof(tmp), size);
29         /* we really need the %* format. */
30         size = snprintf(tmp, size, fmt, val);
31         /* size is now strlen, so the rest of this is just like readstr. */
32         /* always include the \0 */
33         return readmem(off, buf, n, tmp, size + 1);
34 }
35
36 int readnum(unsigned long off, char *buf, unsigned long n, unsigned long val,
37             size_t size)
38 {
39         return __readnum(off, buf, n, val, size, "%lu");
40 }
41
42 int readnum_hex(unsigned long off, char *buf, unsigned long n,
43                 unsigned long val, size_t size)
44 {
45         return __readnum(off, buf, n, val, size, "0x%lx");
46 }
47
48 int readstr(unsigned long offset, char *buf, unsigned long n, const char *str)
49 {
50         /* always include the \0 */
51         return readmem(offset, buf, n, str, strlen(str) + 1);
52 }
53
54 /* Helper: extracts a long from a user buffer (in text). */
55 unsigned 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 */
72 int 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         return rwx_opts[open_flags & O_ACCMODE];
82 }
83
84 /* Converts open mode flags related to permissions, e.g. O_RDWR, to 9p.  It's a
85  * bit ugly, since 9p (according to http://man.cat-v.org/plan_9/5/open) seems to
86  * require that O_EXEC is mutually exclusive with the others.  If someone on
87  * Akaros wants EXEC, we'll just substitute READ. */
88 int omode_to_9p_accmode(int open_flags)
89 {
90         static int acc_opts[] = { [O_RDWR | O_EXEC] = 2,
91                                   [O_WRITE | O_EXEC] = 2,
92                                   [O_READ | O_EXEC] = 0,
93                                   [O_EXEC] = 0,
94                                   [O_RDWR] = 2,
95                                   [O_WRITE] = 1,
96                                   [O_READ] = 0,
97                                   [0] = 0 /* we can't express no permissions */
98                                   };
99         return acc_opts[open_flags & O_ACCMODE];
100 }
101
102 /* TODO: This assumes UID isn't concurrently changed */
103 bool caller_is_username(char *uid)
104 {
105         struct username *current_user = current ? &current->user : &eve;
106
107         return strcmp(current_user->name, uid) == 0;
108 }
109
110 /* Checks if current->user has permissions for omode access on something with
111  * {owner_fileuid, perm} */
112 bool caller_has_perms(char *fileuid, uint32_t perm, int omode)
113 {
114         int rwx;
115
116         perm &= S_PMASK;        /* technically unnecessary; good for clarity */
117         /* select user, group, or other from the traditional rwxrwxrwx, shifting
118          * into the upper-most position */
119         if (caller_is_username(fileuid))
120                 perm <<= 0;
121         else if (iseve())
122                 perm <<= 3;
123         else
124                 perm <<= 6;
125         /* translate omode into things like S_IRUSR (just one set of rwx------).
126          * Plan 9 originally only returned 0400 0200 0600 and 0100 here; it didn't
127          * seem to handle O_EXEC being mixed readable or writable. */
128         rwx = omode_to_rwx(omode);
129         return (rwx & perm) == rwx;
130 }
131
132 bool caller_has_dir_perms(struct dir *dir, int omode)
133 {
134         return caller_has_perms(dir->uid, READ_ONCE(dir->mode), omode);
135 }
136
137 void dir_perm_check(struct dir *dir, int omode)
138 {
139         devpermcheck(dir->uid, READ_ONCE(dir->mode) & S_PMASK, omode);
140 }