1
2 #include "fsdriver.h"
3
4 /*
5 * Check whether the given node may be accessed as directory.
6 * Return OK or an appropriate error code.
7 */
8 static int
access_as_dir(struct fsdriver_node * __restrict node,vfs_ucred_t * __restrict ucred)9 access_as_dir(struct fsdriver_node * __restrict node,
10 vfs_ucred_t * __restrict ucred)
11 {
12 mode_t mask;
13 int i;
14
15 /* The file must be a directory to begin with. */
16 if (!S_ISDIR(node->fn_mode)) return ENOTDIR;
17
18 /* The root user may access anything at all. */
19 if (ucred->vu_uid == ROOT_UID) return OK;
20
21 /* Otherwise, the caller must have search access to the directory. */
22 if (ucred->vu_uid == node->fn_uid) mask = S_IXUSR;
23 else if (ucred->vu_gid == node->fn_gid) mask = S_IXGRP;
24 else {
25 mask = S_IXOTH;
26
27 for (i = 0; i < ucred->vu_ngroups; i++) {
28 if (ucred->vu_sgroups[i] == node->fn_gid) {
29 mask = S_IXGRP;
30
31 break;
32 }
33 }
34 }
35
36 return (node->fn_mode & mask) ? OK : EACCES;
37 }
38
39 /*
40 * Get the next path component from a path. Return the start and end of the
41 * component into the path, and store its name in a null-terminated buffer.
42 */
43 static int
next_name(char ** ptr,char ** start,char * __restrict name,size_t namesize)44 next_name(char ** ptr, char ** start, char * __restrict name, size_t namesize)
45 {
46 char *p;
47 unsigned int i;
48
49 /* Skip one or more path separator characters; they have no effect. */
50 for (p = *ptr; *p == '/'; p++);
51
52 *start = p;
53
54 if (*p) {
55 /*
56 * Copy as much of the name as possible, up to the next path
57 * separator. Return an error if the name does not fit.
58 */
59 for (i = 0; *p && *p != '/' && i < namesize; p++, i++)
60 name[i] = *p;
61
62 if (i >= namesize)
63 return ENAMETOOLONG;
64
65 name[i] = 0;
66 } else
67 /* An empty path component implies the current directory. */
68 strlcpy(name, ".", namesize);
69
70 /*
71 * Return a pointer to the first character not part of this component.
72 * This would typically be either the path separator or a null.
73 */
74 *ptr = p;
75 return OK;
76 }
77
78 /*
79 * Given a symbolic link, resolve and return the contents of the link, followed
80 * by the remaining part of the path that has not yet been resolved (the tail).
81 * Note that the tail points into the given destination buffer.
82 */
83 static int
resolve_link(const struct fsdriver * __restrict fdp,ino_t ino_nr,char * pptr,size_t size,char * tail)84 resolve_link(const struct fsdriver * __restrict fdp, ino_t ino_nr, char * pptr,
85 size_t size, char * tail)
86 {
87 struct fsdriver_data data;
88 char path[PATH_MAX];
89 ssize_t r;
90
91 data.endpt = SELF;
92 data.ptr = path;
93 data.size = sizeof(path) - 1;
94
95 /*
96 * Let the file system the symbolic link. Note that the resulting path
97 * is not null-terminated.
98 */
99 if ((r = fdp->fdr_rdlink(ino_nr, &data, data.size)) < 0)
100 return r;
101
102 /* Append the remaining part of the original path to be resolved. */
103 if (r + strlen(tail) >= sizeof(path))
104 return ENAMETOOLONG;
105
106 strlcpy(&path[r], tail, sizeof(path) - r);
107
108 /* Copy back the result to the original buffer. */
109 strlcpy(pptr, path, size);
110
111 return OK;
112 }
113
114 /*
115 * Process a LOOKUP request from VFS.
116 */
117 int
fsdriver_lookup(const struct fsdriver * __restrict fdp,const message * __restrict m_in,message * __restrict m_out)118 fsdriver_lookup(const struct fsdriver * __restrict fdp,
119 const message * __restrict m_in, message * __restrict m_out)
120 {
121 ino_t dir_ino_nr, root_ino_nr;
122 struct fsdriver_node cur_node, next_node;
123 char path[PATH_MAX], name[NAME_MAX+1];
124 char *ptr, *last;
125 cp_grant_id_t path_grant;
126 vfs_ucred_t ucred;
127 unsigned int flags;
128 size_t path_len, path_size;
129 int r, r2, going_up, is_mountpt, symloop;
130
131 if (fdp->fdr_lookup == NULL)
132 return ENOSYS;
133
134 dir_ino_nr = m_in->m_vfs_fs_lookup.dir_ino;
135 root_ino_nr = m_in->m_vfs_fs_lookup.root_ino;
136 path_grant = m_in->m_vfs_fs_lookup.grant_path;
137 path_size = m_in->m_vfs_fs_lookup.path_size;
138 path_len = m_in->m_vfs_fs_lookup.path_len;
139 flags = m_in->m_vfs_fs_lookup.flags;
140
141 /* Fetch the path name. */
142 if ((r = fsdriver_getname(m_in->m_source, path_grant, path_len, path,
143 sizeof(path), FALSE /*not_empty*/)) != OK)
144 return r;
145
146 /* Fetch the caller's credentials. */
147 if (flags & PATH_GET_UCRED) {
148 if (m_in->m_vfs_fs_lookup.ucred_size != sizeof(ucred)) {
149 printf("fsdriver: bad credential structure\n");
150
151 return EINVAL;
152 }
153
154 if ((r = sys_safecopyfrom(m_in->m_source,
155 m_in->m_vfs_fs_lookup.grant_ucred, 0, (vir_bytes)&ucred,
156 (phys_bytes)m_in->m_vfs_fs_lookup.ucred_size)) != OK)
157 return r;
158 } else {
159 ucred.vu_uid = m_in->m_vfs_fs_lookup.uid;
160 ucred.vu_gid = m_in->m_vfs_fs_lookup.gid;
161 ucred.vu_ngroups = 0;
162 }
163
164 /* Start the actual lookup by referencing the starting inode. */
165 strlcpy(name, ".", sizeof(name)); /* allow a non-const argument */
166
167 r = fdp->fdr_lookup(dir_ino_nr, name, &cur_node, &is_mountpt);
168 if (r != OK)
169 return r;
170
171 symloop = 0;
172
173 /* Whenever we leave this loop, 'cur_node' holds a referenced inode. */
174 for (ptr = last = path; *ptr != 0; ) {
175 /*
176 * Get the next path component. The result is a non-empty
177 * string.
178 */
179 if ((r = next_name(&ptr, &last, name, sizeof(name))) != OK)
180 break;
181
182 if (is_mountpt) {
183 /*
184 * If we start off from a mount point, the next path
185 * component *must* cause us to go up. Anything else
186 * is a protocol violation.
187 */
188 if (strcmp(name, "..")) {
189 r = EINVAL;
190 break;
191 }
192 } else {
193 /*
194 * There is more path to process. That means that the
195 * current file is now being accessed as a directory.
196 * Check type and permissions.
197 */
198 if ((r = access_as_dir(&cur_node, &ucred)) != OK)
199 break;
200 }
201
202 /* A single-dot component resolves to the current directory. */
203 if (!strcmp(name, "."))
204 continue;
205
206 /* A dot-dot component resolves to the parent directory. */
207 going_up = !strcmp(name, "..");
208
209 if (going_up) {
210 /*
211 * The parent of the process's root directory is the
212 * same root directory. All processes have a root
213 * directory, so this check also covers the case of
214 * going up from the global system root directory.
215 */
216 if (cur_node.fn_ino_nr == root_ino_nr)
217 continue;
218
219 /*
220 * Going up from the file system's root directory means
221 * crossing mount points. As indicated, the root file
222 * system is already covered by the check above.
223 */
224 if (cur_node.fn_ino_nr == fsdriver_root) {
225 ptr = last;
226
227 r = ELEAVEMOUNT;
228 break;
229 }
230 }
231
232 /*
233 * Descend into a child node or go up to a parent node, by
234 * asking the actual file system to perform a one-step
235 * resolution. The result, if successful, is an open
236 * (referenced) inode.
237 */
238 if ((r = fdp->fdr_lookup(cur_node.fn_ino_nr, name, &next_node,
239 &is_mountpt)) != OK)
240 break;
241
242 /* Sanity check: a parent node must always be a directory. */
243 if (going_up && !S_ISDIR(next_node.fn_mode))
244 panic("fsdriver: ascending into nondirectory");
245
246 /*
247 * Perform symlink resolution, unless the symlink is the last
248 * path component and VFS is asking us not to resolve it.
249 */
250 if (S_ISLNK(next_node.fn_mode) &&
251 (*ptr || !(flags & PATH_RET_SYMLINK))) {
252 /*
253 * Resolve the symlink, and append the remaining
254 * unresolved part of the path.
255 */
256 if (++symloop < _POSIX_SYMLOOP_MAX)
257 r = resolve_link(fdp, next_node.fn_ino_nr,
258 path, sizeof(path), ptr);
259 else
260 r = ELOOP;
261
262 if (fdp->fdr_putnode != NULL)
263 fdp->fdr_putnode(next_node.fn_ino_nr, 1);
264
265 if (r != OK)
266 break;
267
268 ptr = path;
269
270 /* If the symlink is absolute, return it to VFS. */
271 if (path[0] == '/') {
272 r = ESYMLINK;
273 break;
274 }
275
276 continue;
277 }
278
279 /* We have found a new node. Continue from this node. */
280 if (fdp->fdr_putnode != NULL)
281 fdp->fdr_putnode(cur_node.fn_ino_nr, 1);
282
283 cur_node = next_node;
284
285 /*
286 * If the new node is a mount point, yield to another file
287 * system.
288 */
289 if (is_mountpt) {
290 r = EENTERMOUNT;
291 break;
292 }
293 }
294
295 /* For special redirection errors, we need to return extra details. */
296 if (r == EENTERMOUNT || r == ELEAVEMOUNT || r == ESYMLINK) {
297 /* Copy back the path if we resolved at least one symlink. */
298 if (symloop > 0) {
299 if ((path_len = strlen(path) + 1) > path_size)
300 return ENAMETOOLONG;
301
302 r2 = sys_safecopyto(m_in->m_source, path_grant, 0,
303 (vir_bytes)path, (phys_bytes)path_len);
304 } else
305 r2 = OK;
306
307 if (r2 == OK) {
308 m_out->m_fs_vfs_lookup.offset = (int)(ptr - path);
309 m_out->m_fs_vfs_lookup.symloop = symloop;
310
311 if (r == EENTERMOUNT)
312 m_out->m_fs_vfs_lookup.inode =
313 cur_node.fn_ino_nr;
314 } else
315 r = r2;
316 }
317
318 /*
319 * On success, leave the resulting file open and return its details.
320 * If an error occurred, close the file and return error information.
321 */
322 if (r == OK) {
323 m_out->m_fs_vfs_lookup.inode = cur_node.fn_ino_nr;
324 m_out->m_fs_vfs_lookup.mode = cur_node.fn_mode;
325 m_out->m_fs_vfs_lookup.file_size = cur_node.fn_size;
326 m_out->m_fs_vfs_lookup.uid = cur_node.fn_uid;
327 m_out->m_fs_vfs_lookup.gid = cur_node.fn_gid;
328 m_out->m_fs_vfs_lookup.device = cur_node.fn_dev;
329 } else if (fdp->fdr_putnode != NULL)
330 fdp->fdr_putnode(cur_node.fn_ino_nr, 1);
331
332 return r;
333 }
334