xref: /minix/minix/lib/libvtreefs/path.c (revision 433d6423)
1 /* VTreeFS - path.c - by Alen Stojanov and David van Moolenbroek */
2 
3 #include "inc.h"
4 
5 /*===========================================================================*
6  *				access_as_dir				     *
7  *===========================================================================*/
8 static int access_as_dir(struct inode *node, vfs_ucred_t *ucred)
9 {
10 	/* Check whether the given inode may be accessed as directory.
11 	 * Return OK or an appropriate error code.
12 	 */
13 	mode_t mask;
14 	int i;
15 
16 	/* The inode must be a directory to begin with. */
17 	if (!S_ISDIR(node->i_stat.mode)) return ENOTDIR;
18 
19 	/* The caller must have search access to the directory.
20 	 * Root always does.
21 	 */
22 	if (ucred->vu_uid == SUPER_USER) return OK;
23 
24 	if (ucred->vu_uid == node->i_stat.uid) mask = S_IXUSR;
25 	else if (ucred->vu_gid == node->i_stat.gid) mask = S_IXGRP;
26 	else {
27 		mask = S_IXOTH;
28 
29 		for (i = 0; i < ucred->vu_ngroups; i++) {
30 			if (ucred->vu_sgroups[i] == node->i_stat.gid) {
31 				mask = S_IXGRP;
32 
33 				break;
34 			}
35 		}
36 	}
37 
38 	return (node->i_stat.mode & mask) ? OK : EACCES;
39 }
40 
41 /*===========================================================================*
42  *				next_name				     *
43  *===========================================================================*/
44 static int next_name(char **ptr, char **start, char name[PNAME_MAX+1])
45 {
46 	/* Get the next path component from a path.
47 	 */
48 	char *p;
49 	int i;
50 
51 	for (p = *ptr; *p == '/'; p++);
52 
53 	*start = p;
54 
55 	if (*p) {
56 		for (i = 0; *p && *p != '/' && i <= PNAME_MAX; p++, i++)
57 			name[i] = *p;
58 
59 		if (i > PNAME_MAX)
60 			return ENAMETOOLONG;
61 
62 		name[i] = 0;
63 	} else {
64 		strcpy(name, ".");
65 	}
66 
67 	*ptr = p;
68 	return OK;
69 }
70 
71 /*===========================================================================*
72  *				go_up					     *
73  *===========================================================================*/
74 static int go_up(struct inode *node, struct inode **parent)
75 {
76 	/* Given a directory inode, progress into the parent directory.
77 	 */
78 
79 	*parent = get_parent_inode(node);
80 
81 	/* Trapped in a deleted directory? Should not be possible. */
82 	if (*parent == NULL)
83 		return ENOENT;
84 
85 	ref_inode(*parent);
86 
87 	return OK;
88 }
89 
90 /*===========================================================================*
91  *				go_down					     *
92  *===========================================================================*/
93 static int go_down(struct inode *parent, char *name, struct inode **child)
94 {
95 	/* Given a directory inode and a name, progress into a directory entry.
96 	 */
97 	int r;
98 
99 	/* Call the lookup hook, if present, before doing the actual lookup. */
100 	if (!is_inode_deleted(parent) && vtreefs_hooks->lookup_hook != NULL) {
101 		r = vtreefs_hooks->lookup_hook(parent, name,
102 			get_inode_cbdata(parent));
103 		if (r != OK) return r;
104 	}
105 
106 	if ((*child = get_inode_by_name(parent, name)) == NULL)
107 		return ENOENT;
108 
109 	ref_inode(*child);
110 
111 	return OK;
112 }
113 
114 /*===========================================================================*
115  *				resolve_link				     *
116  *===========================================================================*/
117 static int resolve_link(struct inode *node, char pptr[PATH_MAX], char *tail)
118 {
119 	/* Given a symbolic link, resolve and return the contents of the link.
120 	 */
121 	char path[PATH_MAX];
122 	size_t len;
123 	int r;
124 
125 	assert(vtreefs_hooks->rdlink_hook != NULL);
126 	assert(!is_inode_deleted(node));
127 
128 	r = vtreefs_hooks->rdlink_hook(node, path, sizeof(path),
129 		get_inode_cbdata(node));
130 	if (r != OK) return r;
131 
132 	len = strlen(path);
133 	assert(len > 0 && len < sizeof(path));
134 
135 	if (len + strlen(tail) >= sizeof(path))
136 		return ENAMETOOLONG;
137 
138 	strlcat(path, tail, sizeof(path));
139 
140 	strlcpy(pptr, path, PATH_MAX);
141 
142 	return OK;
143 }
144 
145 /*===========================================================================*
146  *				fs_lookup				     *
147  *===========================================================================*/
148 int fs_lookup(void)
149 {
150 	/* Resolve a path string to an inode.
151 	 */
152 	ino_t dir_ino_nr, root_ino_nr;
153 	struct inode *cur_ino, *next_ino, *root_ino;
154 	char path[PATH_MAX], name[PNAME_MAX+1];
155 	char *ptr, *last;
156 	vfs_ucred_t ucred;
157 	size_t len;
158 	int r, r2, symloop;
159 
160 	dir_ino_nr = fs_m_in.m_vfs_fs_lookup.dir_ino;
161 	root_ino_nr = fs_m_in.m_vfs_fs_lookup.root_ino;
162 	len = fs_m_in.m_vfs_fs_lookup.path_len;
163 
164 	/* Fetch the path name. */
165 	if (len < 1 || len > PATH_MAX)
166 		return EINVAL;
167 
168 	r = sys_safecopyfrom(fs_m_in.m_source,
169 		fs_m_in.m_vfs_fs_lookup.grant_path, 0, (vir_bytes) path,
170 		(phys_bytes) len);
171 	if (r != OK) return r;
172 
173 	if (path[len-1] != 0) return EINVAL;
174 
175 	/* Fetch the caller's credentials. */
176 	if (fs_m_in.m_vfs_fs_lookup.flags & PATH_GET_UCRED) {
177 		assert(fs_m_in.m_vfs_fs_lookup.ucred_size == sizeof(ucred));
178 
179 		r = sys_safecopyfrom(fs_m_in.m_source,
180 			fs_m_in.m_vfs_fs_lookup.grant_ucred, 0,
181 			(vir_bytes) &ucred, fs_m_in.m_vfs_fs_lookup.ucred_size);
182 
183 		if (r != OK)
184 			return r;
185 	}
186 	else {
187 		ucred.vu_uid = fs_m_in.m_vfs_fs_lookup.uid;
188 		ucred.vu_gid = fs_m_in.m_vfs_fs_lookup.gid;
189 		ucred.vu_ngroups = 0;
190 	}
191 
192 	/* Start the actual lookup. */
193 	if ((cur_ino = get_inode(dir_ino_nr)) == NULL)
194 		return EINVAL;
195 
196 	/* Chroot'ed environment? */
197 	if (root_ino_nr > 0)
198 		root_ino = find_inode(root_ino_nr);
199 	else
200 		root_ino = NULL;
201 
202 	symloop = 0;
203 
204 	for (ptr = last = path; ptr[0] != 0; ) {
205 		/* There is more path to process. That means that the current
206 		 * file is now being accessed as a directory. Check type and
207 		 * permissions.
208 		 */
209 		if ((r = access_as_dir(cur_ino, &ucred)) != OK)
210 			break;
211 
212 		/* Get the next path component. The result is a non-empty
213 		 * string.
214 		 */
215 		if ((r = next_name(&ptr, &last, name)) != OK)
216 			break;
217 
218 		if (!strcmp(name, ".") ||
219 				(cur_ino == root_ino && !strcmp(name, "..")))
220 			continue;
221 
222 		if (!strcmp(name, "..")) {
223 			if (cur_ino == get_root_inode())
224 				r = ELEAVEMOUNT;
225 			else
226 				r = go_up(cur_ino, &next_ino);
227 		} else {
228 			r = go_down(cur_ino, name, &next_ino);
229 
230 			/* Perform symlink resolution if we have to. */
231 			if (r == OK && S_ISLNK(next_ino->i_stat.mode) &&
232 				(ptr[0] != '\0' ||
233 				!(fs_m_in.m_vfs_fs_lookup.flags & PATH_RET_SYMLINK))) {
234 
235 				if (++symloop == _POSIX_SYMLOOP_MAX) {
236 					put_inode(next_ino);
237 
238 					r = ELOOP;
239 
240 					break;
241 				}
242 
243 				/* Resolve the symlink, and append the
244 				 * remaining unresolved part of the path.
245 				 */
246 				r = resolve_link(next_ino, path, ptr);
247 
248 				put_inode(next_ino);
249 
250 				if (r != OK)
251 					break;
252 
253 				/* If the symlink is absolute, return it to
254 				 * VFS.
255 				 */
256 				if (path[0] == '/') {
257 					r = ESYMLINK;
258 					last = path;
259 
260 					break;
261 				}
262 
263 				ptr = path;
264 				continue;
265 			}
266 		}
267 
268 		if (r != OK)
269 			break;
270 
271 		/* We have found a new file. Continue from this file. */
272 		assert(next_ino != NULL);
273 
274 		put_inode(cur_ino);
275 
276 		cur_ino = next_ino;
277 	}
278 
279 	/* If an error occurred, close the file and return error information.
280 	 */
281 	if (r != OK) {
282 		put_inode(cur_ino);
283 
284 		/* We'd need support for this here. */
285 		assert(r != EENTERMOUNT);
286 
287 		/* Copy back the path if we resolved at least one symlink. */
288 		if (symloop > 0 && (r == ELEAVEMOUNT || r == ESYMLINK)) {
289 			r2 = sys_safecopyto(fs_m_in.m_source,
290 				fs_m_in.m_vfs_fs_lookup.grant_path, 0,
291 				(vir_bytes) path, strlen(path) + 1);
292 
293 			if (r2 != OK)
294 				r = r2;
295 		}
296 
297 		if (r == ELEAVEMOUNT || r == ESYMLINK) {
298 			fs_m_out.m_fs_vfs_lookup.offset = (int) (last - path);
299 			fs_m_out.m_fs_vfs_lookup.symloop = symloop;
300 		}
301 
302 		return r;
303 	}
304 
305 	/* On success, leave the resulting file open and return its details. */
306 	fs_m_out.m_fs_vfs_lookup.inode = get_inode_number(cur_ino);
307 	fs_m_out.m_fs_vfs_lookup.mode = cur_ino->i_stat.mode;
308 	fs_m_out.m_fs_vfs_lookup.file_size = cur_ino->i_stat.size;
309 	fs_m_out.m_fs_vfs_lookup.uid = cur_ino->i_stat.uid;
310 	fs_m_out.m_fs_vfs_lookup.gid = cur_ino->i_stat.gid;
311 	fs_m_out.m_fs_vfs_lookup.device = cur_ino->i_stat.dev;
312 
313 	return OK;
314 }
315