etc/services - sync with NetBSD-8
[minix.git] / minix / servers / vfs / path.c
blob5360d2c1f3f7a5c9760fe128ae17770480c39aab
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 */
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"
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.
31 #define DO_POSIX_PATHNAME_RES 0
33 static int lookup(struct vnode *dirp, struct lookup *resolve,
34 node_details_t *node, struct fproc *rfp);
36 /*===========================================================================*
37 * advance *
38 *===========================================================================*/
39 struct vnode *
40 advance(struct vnode *dirp, struct lookup *resolve, struct fproc *rfp)
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;
50 assert(dirp);
51 assert(resolve->l_vnode_lock != TLL_NONE);
52 assert(resolve->l_vmnt_lock != TLL_NONE);
54 if (resolve->l_vnode_lock == VNODE_READ)
55 initial_locktype = VNODE_OPCL;
56 else
57 initial_locktype = resolve->l_vnode_lock;
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);
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);
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);
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 */
91 } else {
92 /* Vnode not found, fill in the free vnode's fields */
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;
102 if( (vmp = find_vmnt(new_vp->v_fs_e)) == NULL)
103 panic("advance: vmnt not found");
105 new_vp->v_vmnt = vmp;
106 new_vp->v_dev = vmp->m_dev;
107 new_vp->v_fs_count = 1;
109 vp = new_vp;
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;
117 if (initial_locktype != resolve->l_vnode_lock)
118 tll_downgrade(&vp->v_lock);
120 #if LOCK_DEBUG
121 if (resolve->l_vnode_lock == VNODE_READ)
122 fp->fp_vp_rdlocks++;
123 #endif
126 return(vp);
129 /*===========================================================================*
130 * eat_path *
131 *===========================================================================*/
132 struct vnode *
133 eat_path(struct lookup *resolve, struct fproc *rfp)
135 /* Resolve path to a vnode. advance does the actual work. */
136 struct vnode *start_dir;
138 start_dir = (resolve->l_path[0] == '/' ? rfp->fp_rd : rfp->fp_wd);
139 return advance(start_dir, resolve, rfp);
142 /*===========================================================================*
143 * last_dir *
144 *===========================================================================*/
145 struct vnode *
146 last_dir(struct lookup *resolve, struct fproc *rfp)
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.
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;
165 *resolve->l_vnode = NULL;
166 *resolve->l_vmp = NULL;
167 loop_start = NULL;
168 sym_vp = NULL;
170 ret_on_symlink = !!(resolve->l_flags & PATH_RET_SYMLINK);
172 do {
173 /* Is the path absolute or relative? Initialize 'start_dir'
174 * accordingly. Use loop_start in case we're looping.
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);
181 len = strlen(resolve->l_path);
183 /* If path is empty, return ENOENT. */
184 if (len == 0) {
185 err_code = ENOENT;
186 res_vp = NULL;
187 break;
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';
196 #endif
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.
203 if (strlcpy(dir_entry, resolve->l_path, NAME_MAX+1) >= NAME_MAX + 1) {
204 err_code = ENAMETOOLONG;
205 res_vp = NULL;
206 break;
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;
221 cp[1] = '\0';
222 dir_entry[NAME_MAX] = '\0';
225 /* Remove trailing slashes */
226 while (cp > resolve->l_path && cp[0] == '/') {
227 cp[0]= '\0';
228 cp--;
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.
236 resolve->l_flags &= ~PATH_RET_SYMLINK;
237 if ((res_vp = advance(start_dir, resolve, rfp)) == NULL) {
238 break;
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. */
244 /* Copy the directory entry back to user_fullpath */
245 strlcpy(resolve->l_path, dir_entry, NAME_MAX + 1);
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.
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);
262 if (sym_vp == NULL) break;
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.
268 if (ret_on_symlink) {
269 break;
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);
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;
286 resolve->l_path[r] = '\0';
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;
298 symloop++;
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);
309 continue;
311 } else {
312 symloop = 0; /* Not a symlink, so restart counting */
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.
318 if (sym_vp->v_fs_e != res_vp->v_fs_e) {
319 assert(sym_vmp != NULL);
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;
328 /* Also unlock and release erroneous result */
329 unlock_vnode(*resolve->l_vnode);
330 unlock_vmnt(*resolve->l_vmp);
331 put_vnode(res_vp);
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;
341 /* We've effectively resolved the final component, so
342 * change it to current directory to prevent future
343 * 'advances' of returning erroneous results.
345 strlcpy(dir_entry, ".", NAME_MAX+1);
348 break;
349 } while (symloop < _POSIX_SYMLOOP_MAX);
351 if (symloop >= _POSIX_SYMLOOP_MAX) {
352 err_code = ELOOP;
353 res_vp = NULL;
356 if (sym_vp != NULL) {
357 if (sym_vp_l != NULL) {
358 unlock_vnode(sym_vp);
360 if (sym_vmp != NULL) {
361 unlock_vmnt(sym_vmp);
363 put_vnode(sym_vp);
366 if (loop_start != NULL) {
367 unlock_vnode(loop_start);
368 put_vnode(loop_start);
371 /* Copy the directory entry back to user_fullpath */
372 strlcpy(resolve->l_path, dir_entry, NAME_MAX + 1);
374 /* Turn PATH_RET_SYMLINK flag back on if it was on */
375 if (ret_on_symlink) resolve->l_flags |= PATH_RET_SYMLINK;
377 return(res_vp);
380 /*===========================================================================*
381 * lookup *
382 *===========================================================================*/
383 static int
384 lookup(struct vnode *start_node, struct lookup *resolve, node_details_t *result_node, struct fproc *rfp)
386 /* Resolve a path name relative to start_node. */
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;
399 assert(resolve->l_vmp);
400 assert(resolve->l_vnode);
402 *(resolve->l_vmp) = vmpres = NULL; /* No vmnt found nor locked yet */
404 /* Empty (start) path? */
405 if (resolve->l_path[0] == '\0') {
406 result_node->inode_nr = 0;
407 return(ENOENT);
410 if (!rfp->fp_rd || !rfp->fp_wd) {
411 printf("VFS: lookup %d: no rd/wd\n", rfp->fp_endpoint);
412 return(ENOENT);
415 fs_e = start_node->v_fs_e;
416 dir_ino = start_node->v_inode_nr;
417 vmpres = find_vmnt(fs_e);
419 if (vmpres == NULL) return(EIO); /* mountpoint vanished? */
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;
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);
432 symloop = 0; /* Number of symlinks seen so far */
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;
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);
446 *(resolve->l_vmp) = vmpres;
448 /* Issue the request */
449 r = req_lookup(fs_e, dir_ino, root_ino, uid, gid, resolve, &res, rfp);
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 */
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 */
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);
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;
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);
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");
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);
517 /* Start node is the vnode on which the partition is
518 * mounted */
519 dir_vp = vmp->m_mounted_on;
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;
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;
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);
543 *(resolve->l_vmp) = vmpres;
545 r = req_lookup(fs_e, dir_ino, root_ino, uid, gid, resolve, &res, rfp);
547 if (r != OK && r != EENTERMOUNT && r != ELEAVEMOUNT && r != ESYMLINK) {
548 if (vmpres) unlock_vmnt(vmpres);
549 *(resolve->l_vmp) = NULL;
550 return(r);
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));
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;
568 return(r);
571 /*===========================================================================*
572 * lookup_init *
573 *===========================================================================*/
574 void
575 lookup_init(struct lookup *resolve, char *path, int flags, struct vmnt **vmp, struct vnode **vp)
577 assert(vmp != NULL);
578 assert(vp != NULL);
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;
590 /*===========================================================================*
591 * get_name *
592 *===========================================================================*/
594 get_name(struct vnode *dirp, struct vnode *entry, char ename[NAME_MAX + 1])
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;
603 pos = 0;
605 if (!S_ISDIR(dirp->v_mode)) return(EBADF);
607 do {
608 r = req_getdents(dirp->v_fs_e, dirp->v_inode_nr, pos, (vir_bytes)buf,
609 sizeof(buf), &new_pos, 1);
611 if (r == 0) {
612 return(ENOENT); /* end of entries -- matching inode !found */
613 } else if (r < 0) {
614 return(r); /* error */
617 consumed = 0; /* bytes consumed */
618 totalbytes = r; /* number of bytes to consume */
620 do {
621 cur = (struct dirent *) (buf + consumed);
622 name_len = cur->d_reclen - offsetof(struct dirent, d_name) - 1;
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);
632 ename[NAME_MAX] = '\0';
633 return(OK);
636 /* not a match -- move on to the next dirent */
637 consumed += cur->d_reclen;
638 } while (consumed < totalbytes);
640 pos = new_pos;
641 } while (1);
644 /*===========================================================================*
645 * canonical_path *
646 *===========================================================================*/
648 canonical_path(char orig_path[PATH_MAX], struct fproc *rfp)
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;
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';
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);
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);
677 /* dir_vp points to dir and resolve path now contains only the
678 * filename.
680 strlcpy(orig_path, temp_path, NAME_MAX+1); /* Store file name */
682 /* If we're just crossing a mount point, our name has changed to '.' */
683 if (!strcmp(orig_path, ".")) orig_path[0] = '\0';
685 /* check if the file is a symlink, if so resolve it */
686 r = rdlink_direct(orig_path, temp_path, rfp);
688 if (r <= 0)
689 break;
691 /* encountered a symlink -- loop again */
692 strlcpy(orig_path, temp_path, PATH_MAX);
693 symloop++;
694 } while (symloop < _POSIX_SYMLOOP_MAX);
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);
702 return(ELOOP);
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) {
709 strlcpy(temp_path, "..", NAME_MAX+1);
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;
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);
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;
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);
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);
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);
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.
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)] = '/';
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;
784 unlock_vmnt(dir_vmp);
785 unlock_vnode(dir_vp);
786 put_vnode(dir_vp);
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] = '/';
794 /* remove trailing slash if there is any */
795 if (len > 1 && orig_path[len] == '/') orig_path[len] = '\0';
797 return(OK);
800 /*===========================================================================*
801 * do_socketpath *
802 *===========================================================================*/
803 int do_socketpath(void)
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.
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;
823 /* This should be replaced by an ACL check. */
824 if (!super_user) return EPERM;
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;
831 if (isokendpt(ep, &slot) != OK) return(EINVAL);
832 rfp = &fproc[slot];
834 /* Copy in the path name, which must not be empty. It is typically not null
835 * terminated.
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';
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.
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.
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);
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);
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;
870 unlock_vnode(vp);
871 unlock_vmnt(vmp);
872 put_vnode(vp);
873 break;
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.
879 lookup_init(&resolve, path, PATH_RET_SYMLINK, &vmp, &dirp);
880 resolve.l_vmnt_lock = VMNT_WRITE;
881 resolve.l_vnode_lock = VNODE_WRITE;
883 if ((dirp = last_dir(&resolve, rfp)) == NULL) return(err_code);
885 bits = S_IFSOCK | (ACCESSPERMS & rfp->fp_umask);
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.
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.
915 printf("VFS: socketpath did not find created "
916 "node at %s (%d)\n", path, err_code);
917 r = err_code;
919 } else if (r == EEXIST)
920 r = EADDRINUSE;
923 unlock_vnode(dirp);
924 unlock_vmnt(vmp);
925 put_vnode(dirp);
926 break;
928 default:
929 r = ENOSYS;
932 return(r);