tests/linux: use Akaros's CFLAGS
[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
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
37 int 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
43 int 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
49 int 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). */
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
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. */
89 int 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
104 int 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 */
118 bool 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} */
127 bool 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
147 bool caller_has_dir_perms(struct dir *dir, int omode)
148 {
149         return caller_has_perms(dir->uid, READ_ONCE(dir->mode), omode);
150 }
151
152 void dir_perm_check(struct dir *dir, int omode)
153 {
154         devpermcheck(dir->uid, READ_ONCE(dir->mode) & S_PMASK, omode);
155 }