xref: /minix/minix/lib/libsffs/lookup.c (revision 7f5f010b)
1 /* This file provides path-to-inode lookup functionality.
2  *
3  * The entry points into this file are:
4  *   do_lookup		perform the LOOKUP file system call
5  *
6  * Created:
7  *   April 2009 (D.C. van Moolenbroek)
8  */
9 
10 #include "inc.h"
11 
12 static int get_mask(vfs_ucred_t *ucred);
13 
14 static int access_as_dir(struct inode *ino, struct sffs_attr *attr,
15 	int uid, int mask);
16 
17 static int next_name(char **ptr, char **start, char name[NAME_MAX+1]);
18 
19 static int go_up(char path[PATH_MAX], struct inode *ino,
20 	struct inode **res_ino, struct sffs_attr *attr);
21 
22 static int go_down(char path[PATH_MAX], struct inode *ino, char *name,
23 	struct inode **res_ino, struct sffs_attr *attr);
24 
25 /*===========================================================================*
26  *				get_mask				     *
27  *===========================================================================*/
28 static int get_mask(
29 	vfs_ucred_t *ucred	/* credentials of the caller */
30 )
31 {
32   /* Given the caller's credentials, precompute a search access mask to test
33    * against directory modes.
34    */
35   int i;
36 
37   if (ucred->vu_uid == sffs_params->p_uid) return S_IXUSR;
38 
39   if (ucred->vu_gid == sffs_params->p_gid) return S_IXGRP;
40 
41   for (i = 0; i < ucred->vu_ngroups; i++)
42 	if (ucred->vu_sgroups[i] == sffs_params->p_gid) return S_IXGRP;
43 
44   return S_IXOTH;
45 }
46 
47 /*===========================================================================*
48  *				access_as_dir				     *
49  *===========================================================================*/
50 static int access_as_dir(
51 	struct inode *ino,      /* the inode to test */
52 	struct sffs_attr *attr, /* attributes of the inode */
53 	int uid,                /* UID of the caller */
54 	int mask                /* search access mask of the caller */
55 )
56 {
57 /* Check whether the given inode may be accessed as directory.
58  * Return OK or an appropriate error code.
59  */
60   mode_t mode;
61 
62   assert(attr->a_mask & SFFS_ATTR_MODE);
63 
64   /* The inode must be a directory to begin with. */
65   if (!IS_DIR(ino)) return ENOTDIR;
66 
67   /* The caller must have search access to the directory. Root always does. */
68   if (uid == 0) return OK;
69 
70   mode = get_mode(ino, attr->a_mode);
71 
72   return (mode & mask) ? OK : EACCES;
73 }
74 
75 /*===========================================================================*
76  *				next_name				     *
77  *===========================================================================*/
78 static int next_name(
79 	char **ptr,            /* cursor pointer into path (in, out) */
80 	char **start,          /* place to store start of name */
81 	char name[NAME_MAX+1]  /* place to store name */
82 )
83 {
84 /* Get the next path component from a path.
85  */
86   char *p;
87   int i;
88 
89   for (p = *ptr; *p == '/'; p++);
90 
91   *start = p;
92 
93   if (*p) {
94 	for (i = 0; *p && *p != '/' && i <= NAME_MAX; p++, i++)
95 		name[i] = *p;
96 
97 	if (i > NAME_MAX)
98 		return ENAMETOOLONG;
99 
100 	name[i] = 0;
101   } else {
102 	strcpy(name, ".");
103   }
104 
105   *ptr = p;
106   return OK;
107 }
108 
109 /*===========================================================================*
110  *				go_up					     *
111  *===========================================================================*/
112 static int go_up(
113 	char path[PATH_MAX],    /* path to take the last part from */
114 	struct inode *ino,      /* inode of the current directory */
115 	struct inode **res_ino, /* place to store resulting inode */
116 	struct sffs_attr *attr  /* place to store inode attributes */
117 )
118 {
119 /* Given an inode, progress into the parent directory.
120  */
121   struct inode *parent;
122   int r;
123 
124   pop_path(path);
125 
126   parent = ino->i_parent;
127   assert(parent != NULL);
128 
129   if ((r = verify_path(path, parent, attr, NULL)) != OK)
130 	return r;
131 
132   get_inode(parent);
133 
134   *res_ino = parent;
135 
136   return r;
137 }
138 
139 /*===========================================================================*
140  *				go_down					     *
141  *===========================================================================*/
142 static int go_down(
143 	char path[PATH_MAX],    /* path to add the name to */
144 	struct inode *parent,   /* inode of the current directory */
145 	char *name,             /* name of the directory entry */
146 	struct inode **res_ino, /* place to store resulting inode */
147 	struct sffs_attr *attr  /* place to store inode attributes */
148 )
149 {
150 /* Given a directory inode and a name, progress into a directory entry.
151  */
152   struct inode *ino;
153   int r, stale = 0;
154 
155   if ((r = push_path(path, name)) != OK)
156 	return r;
157 
158   dprintf(("%s: go_down: name '%s', path now '%s'\n", sffs_name, name, path));
159 
160   ino = lookup_dentry(parent, name);
161 
162   dprintf(("%s: lookup_dentry('%s') returned %p\n", sffs_name, name, ino));
163 
164   if (ino != NULL)
165 	r = verify_path(path, ino, attr, &stale);
166   else
167 	r = sffs_table->t_getattr(path, attr);
168 
169   dprintf(("%s: path query returned %d\n", sffs_name, r));
170 
171   if (r != OK) {
172 	if (ino != NULL) {
173 		put_inode(ino);
174 
175 		ino = NULL;
176 	}
177 
178 	if (!stale)
179 		return r;
180   }
181 
182   dprintf(("%s: name '%s'\n", sffs_name, name));
183 
184   if (ino == NULL) {
185 	if ((ino = get_free_inode()) == NULL)
186 		return ENFILE;
187 
188 	dprintf(("%s: inode %p ref %d\n", sffs_name, ino, ino->i_ref));
189 
190 	ino->i_flags = MODE_TO_DIRFLAG(attr->a_mode);
191 
192 	add_dentry(parent, name, ino);
193   }
194 
195   *res_ino = ino;
196   return OK;
197 }
198 
199 /*===========================================================================*
200  *				do_lookup				     *
201  *===========================================================================*/
202 int do_lookup(void)
203 {
204 /* Resolve a path string to an inode.
205  */
206   ino_t dir_ino_nr, root_ino_nr;
207   struct inode *cur_ino, *root_ino;
208   struct inode *next_ino = NULL;
209   struct sffs_attr attr;
210   char buf[PATH_MAX], path[PATH_MAX];
211   char name[NAME_MAX+1];
212   char *ptr, *last;
213   vfs_ucred_t ucred;
214   mode_t mask;
215   size_t len;
216   int r;
217 
218   dir_ino_nr = m_in.m_vfs_fs_lookup.dir_ino;
219   root_ino_nr = m_in.m_vfs_fs_lookup.root_ino;
220   len = m_in.m_vfs_fs_lookup.path_len;
221 
222   /* Fetch the path name. */
223   if (len < 1 || len > PATH_MAX)
224 	return EINVAL;
225 
226   r = sys_safecopyfrom(m_in.m_source, m_in.m_vfs_fs_lookup.grant_path, 0,
227 	(vir_bytes) buf, len);
228 
229   if (r != OK)
230 	return r;
231 
232   if (buf[len-1] != 0) {
233 	printf("%s: VFS did not zero-terminate path!\n", sffs_name);
234 
235 	return EINVAL;
236   }
237 
238   /* Fetch the credentials, and generate a search access mask to test against
239    * directory modes.
240    */
241   if (m_in.m_vfs_fs_lookup.flags & PATH_GET_UCRED) {
242 	if (m_in.m_vfs_fs_lookup.ucred_size != sizeof(ucred)) {
243 		printf("%s: bad credential structure size\n", sffs_name);
244 
245 		return EINVAL;
246 	}
247 
248 	r = sys_safecopyfrom(m_in.m_source, m_in.m_vfs_fs_lookup.grant_ucred, 0,
249 		(vir_bytes) &ucred, m_in.m_vfs_fs_lookup.ucred_size);
250 
251 	if (r != OK)
252 		return r;
253   }
254   else {
255 	ucred.vu_uid = m_in.m_vfs_fs_lookup.uid;
256 	ucred.vu_gid = m_in.m_vfs_fs_lookup.gid;
257 	ucred.vu_ngroups = 0;
258   }
259 
260   mask = get_mask(&ucred);
261 
262   /* Start the actual lookup. */
263   dprintf(("%s: lookup: got query '%s'\n", sffs_name, buf));
264 
265   if ((cur_ino = find_inode(dir_ino_nr)) == NULL)
266 	return EINVAL;
267 
268   attr.a_mask = SFFS_ATTR_MODE | SFFS_ATTR_SIZE;
269 
270   if ((r = verify_inode(cur_ino, path, &attr)) != OK)
271 	return r;
272 
273   get_inode(cur_ino);
274 
275   if (root_ino_nr > 0)
276 	root_ino = find_inode(root_ino_nr);
277   else
278 	root_ino = NULL;
279 
280   /* One possible optimization would be to check a path only right before the
281    * first ".." in a row, and at the very end (if still necessary). This would
282    * have consequences for inode validation, though.
283    */
284   for (ptr = last = buf; *ptr != 0; ) {
285 	if ((r = access_as_dir(cur_ino, &attr, ucred.vu_uid, mask)) != OK)
286 		break;
287 
288 	if ((r = next_name(&ptr, &last, name)) != OK)
289 		break;
290 
291 	dprintf(("%s: lookup: next name '%s'\n", sffs_name, name));
292 
293 	if (!strcmp(name, ".") ||
294 			(cur_ino == root_ino && !strcmp(name, "..")))
295 		continue;
296 
297 	if (!strcmp(name, "..")) {
298 		if (IS_ROOT(cur_ino))
299 			r = ELEAVEMOUNT;
300 		else
301 			r = go_up(path, cur_ino, &next_ino, &attr);
302 	} else {
303 		r = go_down(path, cur_ino, name, &next_ino, &attr);
304 	}
305 
306 	if (r != OK)
307 		break;
308 
309 	assert(next_ino != NULL);
310 
311 	put_inode(cur_ino);
312 
313 	cur_ino = next_ino;
314   }
315 
316   dprintf(("%s: lookup: result %d\n", sffs_name, r));
317 
318   if (r != OK) {
319 	put_inode(cur_ino);
320 
321 	/* We'd need support for these here. We don't have such support. */
322 	assert(r != EENTERMOUNT && r != ESYMLINK);
323 
324 	if (r == ELEAVEMOUNT) {
325 		m_out.m_fs_vfs_lookup.offset = (last - buf);
326 		m_out.m_fs_vfs_lookup.symloop = 0;
327 	}
328 
329 	return r;
330   }
331 
332   m_out.m_fs_vfs_lookup.inode = INODE_NR(cur_ino);
333   m_out.m_fs_vfs_lookup.mode = get_mode(cur_ino, attr.a_mode);
334   m_out.m_fs_vfs_lookup.file_size = attr.a_size;
335   m_out.m_fs_vfs_lookup.uid = sffs_params->p_uid;
336   m_out.m_fs_vfs_lookup.gid = sffs_params->p_gid;
337   m_out.m_fs_vfs_lookup.device = NO_DEV;
338 
339   return OK;
340 }
341