xref: /minix/minix/servers/vfs/path.c (revision 9f988b79)
1 /* lookup() is the main routine that controls the path name lookup. It
2  * handles mountpoints and symbolic links. The actual lookup requests
3  * are sent through the req_lookup wrapper function.
4  */
5 
6 #include "fs.h"
7 #include <string.h>
8 #include <minix/callnr.h>
9 #include <minix/com.h>
10 #include <minix/const.h>
11 #include <minix/endpoint.h>
12 #include <stddef.h>
13 #include <unistd.h>
14 #include <assert.h>
15 #include <minix/vfsif.h>
16 #include <sys/param.h>
17 #include <sys/stat.h>
18 #include <sys/un.h>
19 #include <sys/dirent.h>
20 #include "vmnt.h"
21 #include "vnode.h"
22 #include "path.h"
23 
24 /* Set to following define to 1 if you really want to use the POSIX definition
25  * (IEEE Std 1003.1, 2004) of pathname resolution. POSIX requires pathnames
26  * with a traling slash (and that do not entirely consist of slash characters)
27  * to be treated as if a single dot is appended. This means that for example
28  * mkdir("dir/", ...) and rmdir("dir/") will fail because the call tries to
29  * create or remove the directory '.'. Historically, Unix systems just ignore
30  * trailing slashes.
31  */
32 #define DO_POSIX_PATHNAME_RES	0
33 
34 static int lookup(struct vnode *dirp, struct lookup *resolve,
35 	node_details_t *node, struct fproc *rfp);
36 static int check_perms(endpoint_t ep, cp_grant_id_t io_gr, size_t
37 	pathlen);
38 
39 /*===========================================================================*
40  *				advance					     *
41  *===========================================================================*/
42 struct vnode *advance(dirp, resolve, rfp)
43 struct vnode *dirp;
44 struct lookup *resolve;
45 struct fproc *rfp;
46 {
47 /* Resolve a path name starting at dirp to a vnode. */
48   int r;
49   int do_downgrade = 1;
50   struct vnode *new_vp, *vp;
51   struct vmnt *vmp;
52   struct node_details res = {0,0,0,0,0,0,0};
53   tll_access_t initial_locktype;
54 
55   assert(dirp);
56   assert(resolve->l_vnode_lock != TLL_NONE);
57   assert(resolve->l_vmnt_lock != TLL_NONE);
58 
59   if (resolve->l_vnode_lock == VNODE_READ)
60 	initial_locktype = VNODE_OPCL;
61   else
62 	initial_locktype = resolve->l_vnode_lock;
63 
64   /* Get a free vnode and lock it */
65   if ((new_vp = get_free_vnode()) == NULL) return(NULL);
66   lock_vnode(new_vp, initial_locktype);
67 
68   /* Lookup vnode belonging to the file. */
69   if ((r = lookup(dirp, resolve, &res, rfp)) != OK) {
70 	err_code = r;
71 	unlock_vnode(new_vp);
72 	return(NULL);
73   }
74 
75   /* Check whether we already have a vnode for that file */
76   if ((vp = find_vnode(res.fs_e, res.inode_nr)) != NULL) {
77 	unlock_vnode(new_vp);	/* Don't need this anymore */
78 	do_downgrade = (lock_vnode(vp, initial_locktype) != EBUSY);
79 
80 	/* Unfortunately, by the time we get the lock, another thread might've
81 	 * rid of the vnode (e.g., find_vnode found the vnode while a
82 	 * req_putnode was being processed). */
83 	if (vp->v_ref_count == 0) { /* vnode vanished! */
84 		/* As the lookup before increased the usage counters in the FS,
85 		 * we can simply set the usage counters to 1 and proceed as
86 		 * normal, because the putnode resulted in a use count of 1 in
87 		 * the FS. Other data is still valid, because the vnode was
88 		 * marked as pending lock, so get_free_vnode hasn't
89 		 * reinitialized the vnode yet. */
90 		vp->v_fs_count = 1;
91 		if (vp->v_mapfs_e != NONE) vp->v_mapfs_count = 1;
92 	} else {
93 		vp->v_fs_count++;	/* We got a reference from the FS */
94 	}
95 
96   } else {
97 	/* Vnode not found, fill in the free vnode's fields */
98 
99 	new_vp->v_fs_e = res.fs_e;
100 	new_vp->v_inode_nr = res.inode_nr;
101 	new_vp->v_mode = res.fmode;
102 	new_vp->v_size = res.fsize;
103 	new_vp->v_uid = res.uid;
104 	new_vp->v_gid = res.gid;
105 	new_vp->v_sdev = res.dev;
106 
107 	if( (vmp = find_vmnt(new_vp->v_fs_e)) == NULL)
108 		  panic("advance: vmnt not found");
109 
110 	new_vp->v_vmnt = vmp;
111 	new_vp->v_dev = vmp->m_dev;
112 	new_vp->v_fs_count = 1;
113 
114 	vp = new_vp;
115   }
116 
117   dup_vnode(vp);
118   if (do_downgrade) {
119 	/* Only downgrade a lock if we managed to lock it in the first place */
120 	*(resolve->l_vnode) = vp;
121 
122 	if (initial_locktype != resolve->l_vnode_lock)
123 		tll_downgrade(&vp->v_lock);
124 
125 #if LOCK_DEBUG
126 	if (resolve->l_vnode_lock == VNODE_READ)
127 		fp->fp_vp_rdlocks++;
128 #endif
129   }
130 
131   return(vp);
132 }
133 
134 /*===========================================================================*
135  *				eat_path				     *
136  *===========================================================================*/
137 struct vnode *eat_path(resolve, rfp)
138 struct lookup *resolve;
139 struct fproc *rfp;
140 {
141 /* Resolve path to a vnode. advance does the actual work. */
142   struct vnode *start_dir;
143 
144   start_dir = (resolve->l_path[0] == '/' ? rfp->fp_rd : rfp->fp_wd);
145   return advance(start_dir, resolve, rfp);
146 }
147 
148 /*===========================================================================*
149  *				last_dir				     *
150  *===========================================================================*/
151 struct vnode *last_dir(resolve, rfp)
152 struct lookup *resolve;
153 struct fproc *rfp;
154 {
155 /* Parse a path, as far as the last directory, fetch the vnode
156  * for the last directory into the vnode table, and return a pointer to the
157  * vnode. In addition, return the final component of the path in 'string'. If
158  * the last directory can't be opened, return NULL and the reason for
159  * failure in 'err_code'. We can't parse component by component as that would
160  * be too expensive. Alternatively, we cut off the last component of the path,
161  * and parse the path up to the penultimate component.
162  */
163 
164   size_t len;
165   char *cp;
166   char dir_entry[NAME_MAX+1];
167   struct vnode *start_dir, *res_vp, *sym_vp, *sym_vp_l, *loop_start;
168   struct vmnt *sym_vmp = NULL;
169   int r, symloop = 0, ret_on_symlink = 0;
170   struct lookup symlink;
171 
172   *resolve->l_vnode = NULL;
173   *resolve->l_vmp = NULL;
174   loop_start = NULL;
175   sym_vp = NULL;
176 
177   ret_on_symlink = !!(resolve->l_flags & PATH_RET_SYMLINK);
178 
179   do {
180 	/* Is the path absolute or relative? Initialize 'start_dir'
181 	 * accordingly. Use loop_start in case we're looping.
182 	 */
183 	if (loop_start != NULL)
184 		start_dir = loop_start;
185 	else
186 		start_dir = (resolve->l_path[0] == '/' ? rfp->fp_rd:rfp->fp_wd);
187 
188 	len = strlen(resolve->l_path);
189 
190 	/* If path is empty, return ENOENT. */
191 	if (len == 0)	{
192 		err_code = ENOENT;
193 		res_vp = NULL;
194 		break;
195 	}
196 
197 #if !DO_POSIX_PATHNAME_RES
198 	/* Remove trailing slashes */
199 	while (len > 1 && resolve->l_path[len-1] == '/') {
200 		len--;
201 		resolve->l_path[len]= '\0';
202 	}
203 #endif
204 
205 	cp = strrchr(resolve->l_path, '/');
206 	if (cp == NULL) {
207 		/* Just an entry in the current working directory. Prepend
208 		 * "./" in front of the path and resolve it.
209 		 */
210 		if (strlcpy(dir_entry, resolve->l_path, NAME_MAX+1) >= NAME_MAX + 1) {
211 			err_code = ENAMETOOLONG;
212 			res_vp = NULL;
213 			break;
214 		}
215 		dir_entry[NAME_MAX] = '\0';
216 		resolve->l_path[0] = '.';
217 		resolve->l_path[1] = '\0';
218 	} else if (cp[1] == '\0') {
219 		/* Path ends in a slash. The directory entry is '.' */
220 		strlcpy(dir_entry, ".", NAME_MAX+1);
221 	} else {
222 		/* A path name for the directory and a directory entry */
223 		if (strlcpy(dir_entry, cp+1, NAME_MAX+1) >= NAME_MAX + 1) {
224 			err_code = ENAMETOOLONG;
225 			res_vp = NULL;
226 			break;
227 		}
228 		cp[1] = '\0';
229 		dir_entry[NAME_MAX] = '\0';
230 	}
231 
232 	/* Remove trailing slashes */
233 	while (cp > resolve->l_path && cp[0] == '/') {
234 		cp[0]= '\0';
235 		cp--;
236 	}
237 
238 	/* Resolve up to and including the last directory of the path. Turn off
239 	 * PATH_RET_SYMLINK, because we do want to follow the symlink in this
240 	 * case. That is, the flag is meant for the actual filename of the path,
241 	 * not the last directory.
242 	 */
243 	resolve->l_flags &= ~PATH_RET_SYMLINK;
244 	if ((res_vp = advance(start_dir, resolve, rfp)) == NULL) {
245 		break;
246 	}
247 
248 	/* If the directory entry is not a symlink we're done now. If it is a
249 	 * symlink, then we're not at the last directory, yet. */
250 
251 	/* Copy the directory entry back to user_fullpath */
252 	strlcpy(resolve->l_path, dir_entry, NAME_MAX + 1);
253 
254 	/* Look up the directory entry, but do not follow the symlink when it
255 	 * is one. Note: depending on the previous advance, we might not be
256 	 * able to lock the resulting vnode. For example, when we look up "./."
257 	 * and request a VNODE_WRITE lock on the result, then the previous
258 	 * advance has "./" locked. The next advance to "." will try to lock
259 	 * the same vnode with a VNODE_READ lock, and fail. When that happens,
260 	 * sym_vp_l will be NULL and we must not unlock the vnode. If we would
261 	 * unlock, we actually unlock the vnode locked by the previous advance.
262 	 */
263 	lookup_init(&symlink, resolve->l_path,
264 		    resolve->l_flags|PATH_RET_SYMLINK, &sym_vmp, &sym_vp_l);
265 	symlink.l_vmnt_lock = VMNT_READ;
266 	symlink.l_vnode_lock = VNODE_READ;
267 	sym_vp = advance(res_vp, &symlink, rfp);
268 
269 	if (sym_vp == NULL) break;
270 
271 	if (S_ISLNK(sym_vp->v_mode)) {
272 		/* Last component is a symlink, but if we've been asked to not
273 		 * resolve it, return now.
274 		 */
275 		if (ret_on_symlink) {
276 			break;
277 		}
278 
279 		r = req_rdlink(sym_vp->v_fs_e, sym_vp->v_inode_nr, NONE,
280 				(vir_bytes) resolve->l_path, PATH_MAX - 1, 1);
281 
282 		if (r < 0) {
283 			/* Failed to read link */
284 			err_code = r;
285 			unlock_vnode(res_vp);
286 			unlock_vmnt(*resolve->l_vmp);
287 			put_vnode(res_vp);
288 			*resolve->l_vmp = NULL;
289 			*resolve->l_vnode = NULL;
290 			res_vp = NULL;
291 			break;
292 		}
293 		resolve->l_path[r] = '\0';
294 
295 		if (strrchr(resolve->l_path, '/') != NULL) {
296 			if (sym_vp_l != NULL)
297 				unlock_vnode(sym_vp);
298 			unlock_vmnt(*resolve->l_vmp);
299 			if (sym_vmp != NULL)
300 				unlock_vmnt(sym_vmp);
301 			*resolve->l_vmp = NULL;
302 			put_vnode(sym_vp);
303 			sym_vp = NULL;
304 
305 			symloop++;
306 
307 			/* Relative symlinks are relative to res_vp, not cwd */
308 			if (resolve->l_path[0] != '/') {
309 				loop_start = res_vp;
310 			} else {
311 				/* Absolute symlink, forget about res_vp */
312 				unlock_vnode(res_vp);
313 				put_vnode(res_vp);
314 			}
315 
316 			continue;
317 		}
318 	} else {
319 		symloop = 0;	/* Not a symlink, so restart counting */
320 
321 		/* If we're crossing a mount point, return root node of mount
322 		 * point on which the file resides. That's the 'real' last
323 		 * dir that holds the file we're looking for.
324 		 */
325 		if (sym_vp->v_fs_e != res_vp->v_fs_e) {
326 			assert(sym_vmp != NULL);
327 
328 			/* Unlock final file, it might have wrong lock types */
329 			if (sym_vp_l != NULL)
330 				unlock_vnode(sym_vp);
331 			unlock_vmnt(sym_vmp);
332 			put_vnode(sym_vp);
333 			sym_vp = NULL;
334 
335 			/* Also unlock and release erroneous result */
336 			unlock_vnode(*resolve->l_vnode);
337 			unlock_vmnt(*resolve->l_vmp);
338 			put_vnode(res_vp);
339 
340 			/* Relock vmnt and vnode with correct lock types */
341 			lock_vmnt(sym_vmp, resolve->l_vmnt_lock);
342 			lock_vnode(sym_vmp->m_root_node, resolve->l_vnode_lock);
343 			res_vp = sym_vmp->m_root_node;
344 			dup_vnode(res_vp);
345 			*resolve->l_vnode = res_vp;
346 			*resolve->l_vmp = sym_vmp;
347 
348 			/* We've effectively resolved the final component, so
349 			 * change it to current directory to prevent future
350 			 * 'advances' of returning erroneous results.
351 			 */
352 			strlcpy(dir_entry, ".", NAME_MAX+1);
353 		}
354 	}
355 	break;
356   } while (symloop < _POSIX_SYMLOOP_MAX);
357 
358   if (symloop >= _POSIX_SYMLOOP_MAX) {
359 	err_code = ELOOP;
360 	res_vp = NULL;
361   }
362 
363   if (sym_vp != NULL) {
364 	if (sym_vp_l != NULL) {
365 		unlock_vnode(sym_vp);
366 	}
367 	if (sym_vmp != NULL) {
368 		unlock_vmnt(sym_vmp);
369 	}
370 	put_vnode(sym_vp);
371   }
372 
373   if (loop_start != NULL) {
374 	unlock_vnode(loop_start);
375 	put_vnode(loop_start);
376   }
377 
378   /* Copy the directory entry back to user_fullpath */
379   strlcpy(resolve->l_path, dir_entry, NAME_MAX + 1);
380 
381   /* Turn PATH_RET_SYMLINK flag back on if it was on */
382   if (ret_on_symlink) resolve->l_flags |= PATH_RET_SYMLINK;
383 
384   return(res_vp);
385 }
386 
387 /*===========================================================================*
388  *				lookup					     *
389  *===========================================================================*/
390 static int lookup(start_node, resolve, result_node, rfp)
391 struct vnode *start_node;
392 struct lookup *resolve;
393 node_details_t *result_node;
394 struct fproc *rfp;
395 {
396 /* Resolve a path name relative to start_node. */
397 
398   int r, symloop;
399   endpoint_t fs_e;
400   size_t path_off, path_left_len;
401   ino_t dir_ino, root_ino;
402   uid_t uid;
403   gid_t gid;
404   struct vnode *dir_vp;
405   struct vmnt *vmp, *vmpres;
406   struct lookup_res res;
407   tll_access_t mnt_lock_type;
408 
409   assert(resolve->l_vmp);
410   assert(resolve->l_vnode);
411 
412   *(resolve->l_vmp) = vmpres = NULL; /* No vmnt found nor locked yet */
413 
414   /* Empty (start) path? */
415   if (resolve->l_path[0] == '\0') {
416 	result_node->inode_nr = 0;
417 	return(ENOENT);
418   }
419 
420   if (!rfp->fp_rd || !rfp->fp_wd) {
421 	printf("VFS: lookup %d: no rd/wd\n", rfp->fp_endpoint);
422 	return(ENOENT);
423   }
424 
425   fs_e = start_node->v_fs_e;
426   dir_ino = start_node->v_inode_nr;
427   vmpres = find_vmnt(fs_e);
428 
429   if (vmpres == NULL) return(EIO);	/* mountpoint vanished? */
430 
431   /* Is the process' root directory on the same partition?,
432    * if so, set the chroot directory too. */
433   if (rfp->fp_rd->v_dev == start_node->v_dev)
434 	root_ino = rfp->fp_rd->v_inode_nr;
435   else
436 	root_ino = 0;
437 
438   /* Set user and group ids according to the system call */
439   uid = (job_call_nr == VFS_ACCESS ? rfp->fp_realuid : rfp->fp_effuid);
440   gid = (job_call_nr == VFS_ACCESS ? rfp->fp_realgid : rfp->fp_effgid);
441 
442   symloop = 0;	/* Number of symlinks seen so far */
443 
444   /* Lock vmnt */
445   if (resolve->l_vmnt_lock == VMNT_READ)
446 	mnt_lock_type = VMNT_WRITE;
447   else
448 	mnt_lock_type = resolve->l_vmnt_lock;
449 
450   if ((r = lock_vmnt(vmpres, mnt_lock_type)) != OK) {
451 	if (r == EBUSY) /* vmnt already locked */
452 		vmpres = NULL;
453 	else
454 		return(r);
455   }
456   *(resolve->l_vmp) = vmpres;
457 
458   /* Issue the request */
459   r = req_lookup(fs_e, dir_ino, root_ino, uid, gid, resolve, &res, rfp);
460 
461   if (r != OK && r != EENTERMOUNT && r != ELEAVEMOUNT && r != ESYMLINK) {
462 	if (vmpres) unlock_vmnt(vmpres);
463 	*(resolve->l_vmp) = NULL;
464 	return(r); /* i.e., an error occured */
465   }
466 
467   /* While the response is related to mount control set the
468    * new requests respectively */
469   while (r == EENTERMOUNT || r == ELEAVEMOUNT || r == ESYMLINK) {
470 	/* Update user_fullpath to reflect what's left to be parsed. */
471 	path_off = res.char_processed;
472 	path_left_len = strlen(&resolve->l_path[path_off]);
473 	memmove(resolve->l_path, &resolve->l_path[path_off], path_left_len);
474 	resolve->l_path[path_left_len] = '\0'; /* terminate string */
475 
476 	/* Update the current value of the symloop counter */
477 	symloop += res.symloop;
478 	if (symloop > _POSIX_SYMLOOP_MAX) {
479 		if (vmpres) unlock_vmnt(vmpres);
480 		*(resolve->l_vmp) = NULL;
481 		return(ELOOP);
482 	}
483 
484 	/* Symlink encountered with absolute path */
485 	if (r == ESYMLINK) {
486 		dir_vp = rfp->fp_rd;
487 		vmp = NULL;
488 	} else if (r == EENTERMOUNT) {
489 		/* Entering a new partition */
490 		dir_vp = NULL;
491 		/* Start node is now the mounted partition's root node */
492 		for (vmp = &vmnt[0]; vmp != &vmnt[NR_MNTS]; ++vmp) {
493 			if (vmp->m_dev != NO_DEV && vmp->m_mounted_on) {
494 			   if (vmp->m_mounted_on->v_inode_nr == res.inode_nr &&
495 			       vmp->m_mounted_on->v_fs_e == res.fs_e) {
496 				dir_vp = vmp->m_root_node;
497 				break;
498 			   }
499 			}
500 		}
501 		if (dir_vp == NULL) {
502 			printf("VFS: path lookup error; root node not found\n");
503 			if (vmpres) unlock_vmnt(vmpres);
504 			*(resolve->l_vmp) = NULL;
505 			return(EIO);
506 		}
507 	} else {
508 		/* Climbing up mount */
509 		/* Find the vmnt that represents the partition on
510 		 * which we "climb up". */
511 		if ((vmp = find_vmnt(res.fs_e)) == NULL) {
512 			panic("VFS lookup: can't find parent vmnt");
513 		}
514 
515 		/* Make sure that the child FS does not feed a bogus path
516 		 * to the parent FS. That is, when we climb up the tree, we
517 		 * must've encountered ".." in the path, and that is exactly
518 		 * what we're going to feed to the parent */
519 		if(strncmp(resolve->l_path, "..", 2) != 0 ||
520 		   (resolve->l_path[2] != '\0' && resolve->l_path[2] != '/')) {
521 			printf("VFS: bogus path: %s\n", resolve->l_path);
522 			if (vmpres) unlock_vmnt(vmpres);
523 			*(resolve->l_vmp) = NULL;
524 			return(ENOENT);
525 		}
526 
527 		/* Start node is the vnode on which the partition is
528 		 * mounted */
529 		dir_vp = vmp->m_mounted_on;
530 	}
531 
532 	/* Set the starting directories inode number and FS endpoint */
533 	fs_e = dir_vp->v_fs_e;
534 	dir_ino = dir_vp->v_inode_nr;
535 
536 	/* Is the process' root directory on the same partition?,
537 	 * if so, set the chroot directory too. */
538 	if (dir_vp->v_dev == rfp->fp_rd->v_dev)
539 		root_ino = rfp->fp_rd->v_inode_nr;
540 	else
541 		root_ino = 0;
542 
543 	/* Unlock a previously locked vmnt if locked and lock new vmnt */
544 	if (vmpres) unlock_vmnt(vmpres);
545 	vmpres = find_vmnt(fs_e);
546 	if (vmpres == NULL) return(EIO);	/* mount point vanished? */
547 	if ((r = lock_vmnt(vmpres, mnt_lock_type)) != OK) {
548 		if (r == EBUSY)
549 			vmpres = NULL;	/* Already locked */
550 		else
551 			return(r);
552 	}
553 	*(resolve->l_vmp) = vmpres;
554 
555 	r = req_lookup(fs_e, dir_ino, root_ino, uid, gid, resolve, &res, rfp);
556 
557 	if (r != OK && r != EENTERMOUNT && r != ELEAVEMOUNT && r != ESYMLINK) {
558 		if (vmpres) unlock_vmnt(vmpres);
559 		*(resolve->l_vmp) = NULL;
560 		return(r);
561 	}
562   }
563 
564   if (*(resolve->l_vmp) != NULL && resolve->l_vmnt_lock != mnt_lock_type) {
565 	/* downgrade VMNT_WRITE to VMNT_READ */
566 	downgrade_vmnt_lock(*(resolve->l_vmp));
567   }
568 
569   /* Fill in response fields */
570   result_node->inode_nr = res.inode_nr;
571   result_node->fmode = res.fmode;
572   result_node->fsize = res.fsize;
573   result_node->dev = res.dev;
574   result_node->fs_e = res.fs_e;
575   result_node->uid = res.uid;
576   result_node->gid = res.gid;
577 
578   return(r);
579 }
580 
581 /*===========================================================================*
582  *				lookup_init				     *
583  *===========================================================================*/
584 void lookup_init(resolve, path, flags, vmp, vp)
585 struct lookup *resolve;
586 char *path;
587 int flags;
588 struct vmnt **vmp;
589 struct vnode **vp;
590 {
591   assert(vmp != NULL);
592   assert(vp != NULL);
593 
594   resolve->l_path = path;
595   resolve->l_flags = flags;
596   resolve->l_vmp = vmp;
597   resolve->l_vnode = vp;
598   resolve->l_vmnt_lock = TLL_NONE;
599   resolve->l_vnode_lock = TLL_NONE;
600   *vmp = NULL;	/* Initialize lookup result to NULL */
601   *vp = NULL;
602 }
603 
604 /*===========================================================================*
605  *				get_name				     *
606  *===========================================================================*/
607 int get_name(dirp, entry, ename)
608 struct vnode *dirp;
609 struct vnode *entry;
610 char ename[NAME_MAX + 1];
611 {
612 #define DIR_ENTRIES 8
613 #define DIR_ENTRY_SIZE (sizeof(struct dirent) + NAME_MAX)
614   off_t pos, new_pos;
615   int r, consumed, totalbytes, name_len;
616   char buf[DIR_ENTRY_SIZE * DIR_ENTRIES];
617   struct dirent *cur;
618 
619   pos = 0;
620 
621   if (!S_ISDIR(dirp->v_mode)) return(EBADF);
622 
623   do {
624 	r = req_getdents(dirp->v_fs_e, dirp->v_inode_nr, pos, (vir_bytes)buf,
625 		sizeof(buf), &new_pos, 1);
626 
627 	if (r == 0) {
628 		return(ENOENT); /* end of entries -- matching inode !found */
629 	} else if (r < 0) {
630 		return(r); /* error */
631 	}
632 
633 	consumed = 0; /* bytes consumed */
634 	totalbytes = r; /* number of bytes to consume */
635 
636 	do {
637 		cur = (struct dirent *) (buf + consumed);
638 		name_len = cur->d_reclen - offsetof(struct dirent, d_name) - 1;
639 
640 		if(cur->d_name + name_len+1 > &buf[sizeof(buf)])
641 			return(EINVAL);	/* Rubbish in dir entry */
642 		if (entry->v_inode_nr == cur->d_fileno) {
643 			/* found the entry we were looking for */
644 			int copylen = MIN(name_len + 1, NAME_MAX + 1);
645 			if (strlcpy(ename, cur->d_name, copylen) >= copylen) {
646 				return(ENAMETOOLONG);
647 			}
648 			ename[NAME_MAX] = '\0';
649 			return(OK);
650 		}
651 
652 		/* not a match -- move on to the next dirent */
653 		consumed += cur->d_reclen;
654 	} while (consumed < totalbytes);
655 
656 	pos = new_pos;
657   } while (1);
658 }
659 
660 /*===========================================================================*
661  *				canonical_path				     *
662  *===========================================================================*/
663 int canonical_path(orig_path, rfp)
664 char orig_path[PATH_MAX];
665 struct fproc *rfp;
666 {
667 /* Find canonical path of a given path */
668   int len = 0;
669   int r, symloop = 0;
670   struct vnode *dir_vp, *parent_dir;
671   struct vmnt *dir_vmp, *parent_vmp;
672   char component[NAME_MAX+1];	/* NAME_MAX does /not/ include '\0' */
673   char temp_path[PATH_MAX];
674   struct lookup resolve;
675 
676   parent_dir = dir_vp = NULL;
677   parent_vmp = dir_vmp = NULL;
678   strlcpy(temp_path, orig_path, PATH_MAX);
679   temp_path[PATH_MAX - 1] = '\0';
680 
681   /* First resolve path to the last directory holding the file */
682   do {
683 	if (dir_vp) {
684 		unlock_vnode(dir_vp);
685 		unlock_vmnt(dir_vmp);
686 		put_vnode(dir_vp);
687 	}
688 
689 	lookup_init(&resolve, temp_path, PATH_NOFLAGS, &dir_vmp, &dir_vp);
690 	resolve.l_vmnt_lock = VMNT_READ;
691 	resolve.l_vnode_lock = VNODE_READ;
692 	if ((dir_vp = last_dir(&resolve, rfp)) == NULL) return(err_code);
693 
694 	/* dir_vp points to dir and resolve path now contains only the
695 	 * filename.
696 	 */
697 	strlcpy(orig_path, temp_path, NAME_MAX+1);	/* Store file name */
698 
699 	/* If we're just crossing a mount point, our name has changed to '.' */
700 	if (!strcmp(orig_path, ".")) orig_path[0] = '\0';
701 
702 	/* check if the file is a symlink, if so resolve it */
703 	r = rdlink_direct(orig_path, temp_path, rfp);
704 
705 	if (r <= 0)
706 		break;
707 
708 	/* encountered a symlink -- loop again */
709 	strlcpy(orig_path, temp_path, PATH_MAX);
710 	symloop++;
711   } while (symloop < _POSIX_SYMLOOP_MAX);
712 
713   if (symloop >= _POSIX_SYMLOOP_MAX) {
714 	if (dir_vp) {
715 		unlock_vnode(dir_vp);
716 		unlock_vmnt(dir_vmp);
717 		put_vnode(dir_vp);
718 	}
719 	return(ELOOP);
720   }
721 
722   /* We've got the filename and the actual directory holding the file. From
723    * here we start building up the canonical path by climbing up the tree */
724   while (dir_vp != rfp->fp_rd) {
725 
726 	strlcpy(temp_path, "..", NAME_MAX+1);
727 
728 	/* check if we're at the root node of the file system */
729 	if (dir_vp->v_vmnt->m_root_node == dir_vp) {
730 		if (dir_vp->v_vmnt->m_mounted_on == NULL) {
731 			/* Bail out, we can't go any higher */
732 			break;
733 		}
734 		unlock_vnode(dir_vp);
735 		unlock_vmnt(dir_vmp);
736 		put_vnode(dir_vp);
737 		dir_vp = dir_vp->v_vmnt->m_mounted_on;
738 		dir_vmp = dir_vp->v_vmnt;
739 		if (lock_vmnt(dir_vmp, VMNT_READ) != OK)
740 			panic("failed to lock vmnt");
741 		if (lock_vnode(dir_vp, VNODE_READ) != OK)
742 			panic("failed to lock vnode");
743 		dup_vnode(dir_vp);
744 	}
745 
746 	lookup_init(&resolve, temp_path, PATH_NOFLAGS, &parent_vmp,
747 		    &parent_dir);
748 	resolve.l_vmnt_lock = VMNT_READ;
749 	resolve.l_vnode_lock = VNODE_READ;
750 
751 	if ((parent_dir = advance(dir_vp, &resolve, rfp)) == NULL) {
752 		unlock_vnode(dir_vp);
753 		unlock_vmnt(dir_vmp);
754 		put_vnode(dir_vp);
755 		return(err_code);
756 	}
757 
758 	/* now we have to retrieve the name of the parent directory */
759 	if ((r = get_name(parent_dir, dir_vp, component)) != OK) {
760 		unlock_vnode(parent_dir);
761 		unlock_vmnt(parent_vmp);
762 		unlock_vnode(dir_vp);
763 		unlock_vmnt(dir_vmp);
764 		put_vnode(parent_dir);
765 		put_vnode(dir_vp);
766 		return(r);
767 	}
768 
769 	len += strlen(component) + 1;
770 	if (len >= PATH_MAX) {
771 		/* adding the component to orig_path would exceed PATH_MAX */
772 		unlock_vnode(parent_dir);
773 		unlock_vmnt(parent_vmp);
774 		unlock_vnode(dir_vp);
775 		unlock_vmnt(dir_vmp);
776 		put_vnode(parent_dir);
777 		put_vnode(dir_vp);
778 		return(ENOMEM);
779 	}
780 
781 	/* Store result of component in orig_path. First make space by moving
782 	 * the contents of orig_path to the right. Move strlen + 1 bytes to
783 	 * include the terminating '\0'. Move to strlen + 1 bytes to reserve
784 	 * space for the slash.
785 	 */
786 	memmove(orig_path+strlen(component)+1, orig_path, strlen(orig_path)+1);
787 	/* Copy component into canon_path */
788 	memmove(orig_path, component, strlen(component));
789 	/* Put slash into place */
790 	orig_path[strlen(component)] = '/';
791 
792 	/* Store parent_dir result, and continue the loop once more */
793 	unlock_vnode(dir_vp);
794 	unlock_vmnt(dir_vmp);
795 	put_vnode(dir_vp);
796 	dir_vp = parent_dir;
797 	dir_vmp = parent_vmp;
798 	parent_vmp = NULL;
799   }
800 
801   unlock_vmnt(dir_vmp);
802   unlock_vnode(dir_vp);
803   put_vnode(dir_vp);
804 
805   /* add the leading slash */
806   len = strlen(orig_path);
807   if (strlen(orig_path) >= PATH_MAX) return(ENAMETOOLONG);
808   memmove(orig_path+1, orig_path, len + 1 /* include terminating nul */);
809   orig_path[0] = '/';
810 
811   /* remove trailing slash if there is any */
812   if (len > 1 && orig_path[len] == '/') orig_path[len] = '\0';
813 
814   return(OK);
815 }
816 
817 /*===========================================================================*
818  *				check_perms				     *
819  *===========================================================================*/
820 static int check_perms(ep, io_gr, pathlen)
821 endpoint_t ep;
822 cp_grant_id_t io_gr;
823 size_t pathlen;
824 {
825   int r, slot;
826   struct vnode *vp;
827   struct vmnt *vmp;
828   struct fproc *rfp;
829   char canon_path[PATH_MAX];
830   struct lookup resolve;
831   struct sockaddr_un sun;
832 
833   if (isokendpt(ep, &slot) != OK) return(EINVAL);
834   if (pathlen < sizeof(sun.sun_path) || pathlen >= PATH_MAX) return(EINVAL);
835 
836   rfp = &(fproc[slot]);
837   r = sys_safecopyfrom(who_e, io_gr, (vir_bytes) 0, (vir_bytes) canon_path,
838 	pathlen);
839   if (r != OK) return(r);
840   canon_path[pathlen] = '\0';
841 
842   /* Turn path into canonical path to the socket file */
843   if ((r = canonical_path(canon_path, rfp)) != OK) return(r);
844   if (strlen(canon_path) >= pathlen) return(ENAMETOOLONG);
845 
846   /* copy canon_path back to the caller */
847   r = sys_safecopyto(who_e, (cp_grant_id_t) io_gr, (vir_bytes) 0,
848 	(vir_bytes) canon_path, pathlen);
849   if (r != OK) return(r);
850 
851   /* Now do permissions checking */
852   lookup_init(&resolve, canon_path, PATH_NOFLAGS, &vmp, &vp);
853   resolve.l_vmnt_lock = VMNT_READ;
854   resolve.l_vnode_lock = VNODE_READ;
855   if ((vp = eat_path(&resolve, rfp)) == NULL) return(err_code);
856 
857   /* check permissions */
858   r = forbidden(rfp, vp, (R_BIT | W_BIT));
859 
860   unlock_vnode(vp);
861   unlock_vmnt(vmp);
862 
863   put_vnode(vp);
864   return(r);
865 }
866 
867 /*===========================================================================*
868  *				do_checkperms				     *
869  *===========================================================================*/
870 int do_checkperms(void)
871 {
872   /* This should be replaced by an ACL check. */
873   if (!super_user) return EPERM;
874 
875   return check_perms(job_m_in.m_lsys_vfs_checkperms.endpt,
876 	job_m_in.m_lsys_vfs_checkperms.grant,
877 	job_m_in.m_lsys_vfs_checkperms.count);
878 }
879