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