9ns: Fix issues with can_have_children
[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 int access_bits_to_omode(int access_bits)
103 {
104         int omode = 0;
105
106         if (R_OK)
107                 omode |= O_READ;
108         if (W_OK)
109                 omode |= O_WRITE;
110         if (X_OK)
111                 omode |= O_EXEC;
112         return omode;
113 }
114
115 /* TODO: This assumes UID isn't concurrently changed */
116 bool caller_is_username(char *uid)
117 {
118         struct username *current_user = current ? &current->user : &eve;
119
120         return strcmp(current_user->name, uid) == 0;
121 }
122
123 /* Checks if current->user has permissions for omode access on something with
124  * {owner_fileuid, perm} */
125 bool caller_has_perms(char *fileuid, uint32_t perm, int omode)
126 {
127         int rwx;
128
129         perm &= S_PMASK;        /* technically unnecessary; good for clarity */
130         /* select user, group, or other from the traditional rwxrwxrwx, shifting
131          * into the upper-most position */
132         if (caller_is_username(fileuid))
133                 perm <<= 0;
134         else if (iseve())
135                 perm <<= 3;
136         else
137                 perm <<= 6;
138         /* translate omode into things like S_IRUSR (just one set of rwx------).
139          * Plan 9 originally only returned 0400 0200 0600 and 0100 here; it didn't
140          * seem to handle O_EXEC being mixed readable or writable. */
141         rwx = omode_to_rwx(omode);
142         return (rwx & perm) == rwx;
143 }
144
145 bool caller_has_dir_perms(struct dir *dir, int omode)
146 {
147         return caller_has_perms(dir->uid, READ_ONCE(dir->mode), omode);
148 }
149
150 void dir_perm_check(struct dir *dir, int omode)
151 {
152         devpermcheck(dir->uid, READ_ONCE(dir->mode) & S_PMASK, omode);
153 }