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