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