etc/services - sync with NetBSD-8
[minix.git] / minix / lib / libfsdriver / lookup.c
blobb8e2ae08044aa77338bcadf0cc6543eed7316241
2 #include "fsdriver.h"
4 /*
5 * Check whether the given node may be accessed as directory.
6 * Return OK or an appropriate error code.
7 */
8 static int
9 access_as_dir(struct fsdriver_node * __restrict node,
10 vfs_ucred_t * __restrict ucred)
12 mode_t mask;
13 int i;
15 /* The file must be a directory to begin with. */
16 if (!S_ISDIR(node->fn_mode)) return ENOTDIR;
18 /* The root user may access anything at all. */
19 if (ucred->vu_uid == ROOT_UID) return OK;
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;
27 for (i = 0; i < ucred->vu_ngroups; i++) {
28 if (ucred->vu_sgroups[i] == node->fn_gid) {
29 mask = S_IXGRP;
31 break;
36 return (node->fn_mode & mask) ? OK : EACCES;
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.
43 static int
44 next_name(char ** ptr, char ** start, char * __restrict name, size_t namesize)
46 char *p;
47 unsigned int i;
49 /* Skip one or more path separator characters; they have no effect. */
50 for (p = *ptr; *p == '/'; p++);
52 *start = p;
54 if (*p) {
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.
59 for (i = 0; *p && *p != '/' && i < namesize; p++, i++)
60 name[i] = *p;
62 if (i >= namesize)
63 return ENAMETOOLONG;
65 name[i] = 0;
66 } else
67 /* An empty path component implies the current directory. */
68 strlcpy(name, ".", namesize);
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.
74 *ptr = p;
75 return OK;
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.
83 static int
84 resolve_link(const struct fsdriver * __restrict fdp, ino_t ino_nr, char * pptr,
85 size_t size, char * tail)
87 struct fsdriver_data data;
88 char path[PATH_MAX];
89 ssize_t r;
91 data.endpt = SELF;
92 data.ptr = path;
93 data.size = sizeof(path) - 1;
96 * Let the file system the symbolic link. Note that the resulting path
97 * is not null-terminated.
99 if ((r = fdp->fdr_rdlink(ino_nr, &data, data.size)) < 0)
100 return r;
102 /* Append the remaining part of the original path to be resolved. */
103 if (r + strlen(tail) >= sizeof(path))
104 return ENAMETOOLONG;
106 strlcpy(&path[r], tail, sizeof(path) - r);
108 /* Copy back the result to the original buffer. */
109 strlcpy(pptr, path, size);
111 return OK;
115 * Process a LOOKUP request from VFS.
118 fsdriver_lookup(const struct fsdriver * __restrict fdp,
119 const message * __restrict m_in, message * __restrict m_out)
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;
131 if (fdp->fdr_lookup == NULL)
132 return ENOSYS;
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;
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;
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");
151 return EINVAL;
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;
164 /* Start the actual lookup by referencing the starting inode. */
165 strlcpy(name, ".", sizeof(name)); /* allow a non-const argument */
167 r = fdp->fdr_lookup(dir_ino_nr, name, &cur_node, &is_mountpt);
168 if (r != OK)
169 return r;
171 symloop = 0;
173 /* Whenever we leave this loop, 'cur_node' holds a referenced inode. */
174 for (ptr = last = path; *ptr != 0; ) {
176 * Get the next path component. The result is a non-empty
177 * string.
179 if ((r = next_name(&ptr, &last, name, sizeof(name))) != OK)
180 break;
182 if (is_mountpt) {
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.
188 if (strcmp(name, "..")) {
189 r = EINVAL;
190 break;
192 } else {
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.
198 if ((r = access_as_dir(&cur_node, &ucred)) != OK)
199 break;
202 /* A single-dot component resolves to the current directory. */
203 if (!strcmp(name, "."))
204 continue;
206 /* A dot-dot component resolves to the parent directory. */
207 going_up = !strcmp(name, "..");
209 if (going_up) {
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.
216 if (cur_node.fn_ino_nr == root_ino_nr)
217 continue;
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.
224 if (cur_node.fn_ino_nr == fsdriver_root) {
225 ptr = last;
227 r = ELEAVEMOUNT;
228 break;
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.
238 if ((r = fdp->fdr_lookup(cur_node.fn_ino_nr, name, &next_node,
239 &is_mountpt)) != OK)
240 break;
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");
247 * Perform symlink resolution, unless the symlink is the last
248 * path component and VFS is asking us not to resolve it.
250 if (S_ISLNK(next_node.fn_mode) &&
251 (*ptr || !(flags & PATH_RET_SYMLINK))) {
253 * Resolve the symlink, and append the remaining
254 * unresolved part of the path.
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;
262 if (fdp->fdr_putnode != NULL)
263 fdp->fdr_putnode(next_node.fn_ino_nr, 1);
265 if (r != OK)
266 break;
268 ptr = path;
270 /* If the symlink is absolute, return it to VFS. */
271 if (path[0] == '/') {
272 r = ESYMLINK;
273 break;
276 continue;
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);
283 cur_node = next_node;
286 * If the new node is a mount point, yield to another file
287 * system.
289 if (is_mountpt) {
290 r = EENTERMOUNT;
291 break;
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;
302 r2 = sys_safecopyto(m_in->m_source, path_grant, 0,
303 (vir_bytes)path, (phys_bytes)path_len);
304 } else
305 r2 = OK;
307 if (r2 == OK) {
308 m_out->m_fs_vfs_lookup.offset = (int)(ptr - path);
309 m_out->m_fs_vfs_lookup.symloop = symloop;
311 if (r == EENTERMOUNT)
312 m_out->m_fs_vfs_lookup.inode =
313 cur_node.fn_ino_nr;
314 } else
315 r = r2;
319 * On success, leave the resulting file open and return its details.
320 * If an error occurred, close the file and return error information.
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);
332 return r;