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.
8 #include <minix/callnr.h>
10 #include <minix/const.h>
11 #include <minix/endpoint.h>
15 #include <minix/vfsif.h>
16 #include <sys/param.h>
19 #include <sys/dirent.h>
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
32 #define DO_POSIX_PATHNAME_RES 0
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
39 /*===========================================================================*
41 *===========================================================================*/
42 struct vnode
*advance(dirp
, resolve
, rfp
)
44 struct lookup
*resolve
;
47 /* Resolve a path name starting at dirp to a vnode. */
50 struct vnode
*new_vp
, *vp
;
52 struct node_details res
= {0,0,0,0,0,0,0};
53 tll_access_t initial_locktype
;
56 assert(resolve
->l_vnode_lock
!= TLL_NONE
);
57 assert(resolve
->l_vmnt_lock
!= TLL_NONE
);
59 if (resolve
->l_vnode_lock
== VNODE_READ
)
60 initial_locktype
= VNODE_OPCL
;
62 initial_locktype
= resolve
->l_vnode_lock
;
64 /* Get a free vnode and lock it */
65 if ((new_vp
= get_free_vnode()) == NULL
) return(NULL
);
66 lock_vnode(new_vp
, initial_locktype
);
68 /* Lookup vnode belonging to the file. */
69 if ((r
= lookup(dirp
, resolve
, &res
, rfp
)) != OK
) {
75 /* Check whether we already have a vnode for that file */
76 if ((vp
= find_vnode(res
.fs_e
, res
.inode_nr
)) != NULL
) {
77 unlock_vnode(new_vp
); /* Don't need this anymore */
78 do_downgrade
= (lock_vnode(vp
, initial_locktype
) != EBUSY
);
80 /* Unfortunately, by the time we get the lock, another thread might've
81 * rid of the vnode (e.g., find_vnode found the vnode while a
82 * req_putnode was being processed). */
83 if (vp
->v_ref_count
== 0) { /* vnode vanished! */
84 /* As the lookup before increased the usage counters in the FS,
85 * we can simply set the usage counters to 1 and proceed as
86 * normal, because the putnode resulted in a use count of 1 in
87 * the FS. Other data is still valid, because the vnode was
88 * marked as pending lock, so get_free_vnode hasn't
89 * reinitialized the vnode yet. */
91 if (vp
->v_mapfs_e
!= NONE
) vp
->v_mapfs_count
= 1;
93 vp
->v_fs_count
++; /* We got a reference from the FS */
97 /* Vnode not found, fill in the free vnode's fields */
99 new_vp
->v_fs_e
= res
.fs_e
;
100 new_vp
->v_inode_nr
= res
.inode_nr
;
101 new_vp
->v_mode
= res
.fmode
;
102 new_vp
->v_size
= res
.fsize
;
103 new_vp
->v_uid
= res
.uid
;
104 new_vp
->v_gid
= res
.gid
;
105 new_vp
->v_sdev
= res
.dev
;
107 if( (vmp
= find_vmnt(new_vp
->v_fs_e
)) == NULL
)
108 panic("advance: vmnt not found");
110 new_vp
->v_vmnt
= vmp
;
111 new_vp
->v_dev
= vmp
->m_dev
;
112 new_vp
->v_fs_count
= 1;
119 /* Only downgrade a lock if we managed to lock it in the first place */
120 *(resolve
->l_vnode
) = vp
;
122 if (initial_locktype
!= resolve
->l_vnode_lock
)
123 tll_downgrade(&vp
->v_lock
);
126 if (resolve
->l_vnode_lock
== VNODE_READ
)
134 /*===========================================================================*
136 *===========================================================================*/
137 struct vnode
*eat_path(resolve
, rfp
)
138 struct lookup
*resolve
;
141 /* Resolve path to a vnode. advance does the actual work. */
142 struct vnode
*start_dir
;
144 start_dir
= (resolve
->l_path
[0] == '/' ? rfp
->fp_rd
: rfp
->fp_wd
);
145 return advance(start_dir
, resolve
, rfp
);
148 /*===========================================================================*
150 *===========================================================================*/
151 struct vnode
*last_dir(resolve
, rfp
)
152 struct lookup
*resolve
;
155 /* Parse a path, as far as the last directory, fetch the vnode
156 * for the last directory into the vnode table, and return a pointer to the
157 * vnode. In addition, return the final component of the path in 'string'. If
158 * the last directory can't be opened, return NULL and the reason for
159 * failure in 'err_code'. We can't parse component by component as that would
160 * be too expensive. Alternatively, we cut off the last component of the path,
161 * and parse the path up to the penultimate component.
166 char dir_entry
[NAME_MAX
+1];
167 struct vnode
*start_dir
, *res_vp
, *sym_vp
, *sym_vp_l
, *loop_start
;
168 struct vmnt
*sym_vmp
= NULL
;
169 int r
, symloop
= 0, ret_on_symlink
= 0;
170 struct lookup symlink
;
172 *resolve
->l_vnode
= NULL
;
173 *resolve
->l_vmp
= NULL
;
177 ret_on_symlink
= !!(resolve
->l_flags
& PATH_RET_SYMLINK
);
180 /* Is the path absolute or relative? Initialize 'start_dir'
181 * accordingly. Use loop_start in case we're looping.
183 if (loop_start
!= NULL
)
184 start_dir
= loop_start
;
186 start_dir
= (resolve
->l_path
[0] == '/' ? rfp
->fp_rd
:rfp
->fp_wd
);
188 len
= strlen(resolve
->l_path
);
190 /* If path is empty, return ENOENT. */
197 #if !DO_POSIX_PATHNAME_RES
198 /* Remove trailing slashes */
199 while (len
> 1 && resolve
->l_path
[len
-1] == '/') {
201 resolve
->l_path
[len
]= '\0';
205 cp
= strrchr(resolve
->l_path
, '/');
207 /* Just an entry in the current working directory. Prepend
208 * "./" in front of the path and resolve it.
210 if (strlcpy(dir_entry
, resolve
->l_path
, NAME_MAX
+1) >= NAME_MAX
+ 1) {
211 err_code
= ENAMETOOLONG
;
215 dir_entry
[NAME_MAX
] = '\0';
216 resolve
->l_path
[0] = '.';
217 resolve
->l_path
[1] = '\0';
218 } else if (cp
[1] == '\0') {
219 /* Path ends in a slash. The directory entry is '.' */
220 strlcpy(dir_entry
, ".", NAME_MAX
+1);
222 /* A path name for the directory and a directory entry */
223 if (strlcpy(dir_entry
, cp
+1, NAME_MAX
+1) >= NAME_MAX
+ 1) {
224 err_code
= ENAMETOOLONG
;
229 dir_entry
[NAME_MAX
] = '\0';
232 /* Remove trailing slashes */
233 while (cp
> resolve
->l_path
&& cp
[0] == '/') {
238 /* Resolve up to and including the last directory of the path. Turn off
239 * PATH_RET_SYMLINK, because we do want to follow the symlink in this
240 * case. That is, the flag is meant for the actual filename of the path,
241 * not the last directory.
243 resolve
->l_flags
&= ~PATH_RET_SYMLINK
;
244 if ((res_vp
= advance(start_dir
, resolve
, rfp
)) == NULL
) {
248 /* If the directory entry is not a symlink we're done now. If it is a
249 * symlink, then we're not at the last directory, yet. */
251 /* Copy the directory entry back to user_fullpath */
252 strlcpy(resolve
->l_path
, dir_entry
, NAME_MAX
+ 1);
254 /* Look up the directory entry, but do not follow the symlink when it
255 * is one. Note: depending on the previous advance, we might not be
256 * able to lock the resulting vnode. For example, when we look up "./."
257 * and request a VNODE_WRITE lock on the result, then the previous
258 * advance has "./" locked. The next advance to "." will try to lock
259 * the same vnode with a VNODE_READ lock, and fail. When that happens,
260 * sym_vp_l will be NULL and we must not unlock the vnode. If we would
261 * unlock, we actually unlock the vnode locked by the previous advance.
263 lookup_init(&symlink
, resolve
->l_path
,
264 resolve
->l_flags
|PATH_RET_SYMLINK
, &sym_vmp
, &sym_vp_l
);
265 symlink
.l_vmnt_lock
= VMNT_READ
;
266 symlink
.l_vnode_lock
= VNODE_READ
;
267 sym_vp
= advance(res_vp
, &symlink
, rfp
);
269 if (sym_vp
== NULL
) break;
271 if (S_ISLNK(sym_vp
->v_mode
)) {
272 /* Last component is a symlink, but if we've been asked to not
273 * resolve it, return now.
275 if (ret_on_symlink
) {
279 r
= req_rdlink(sym_vp
->v_fs_e
, sym_vp
->v_inode_nr
, NONE
,
280 (vir_bytes
) resolve
->l_path
, PATH_MAX
- 1, 1);
283 /* Failed to read link */
285 unlock_vnode(res_vp
);
286 unlock_vmnt(*resolve
->l_vmp
);
288 *resolve
->l_vmp
= NULL
;
289 *resolve
->l_vnode
= NULL
;
293 resolve
->l_path
[r
] = '\0';
295 if (strrchr(resolve
->l_path
, '/') != NULL
) {
296 if (sym_vp_l
!= NULL
)
297 unlock_vnode(sym_vp
);
298 unlock_vmnt(*resolve
->l_vmp
);
300 unlock_vmnt(sym_vmp
);
301 *resolve
->l_vmp
= NULL
;
307 /* Relative symlinks are relative to res_vp, not cwd */
308 if (resolve
->l_path
[0] != '/') {
311 /* Absolute symlink, forget about res_vp */
312 unlock_vnode(res_vp
);
319 symloop
= 0; /* Not a symlink, so restart counting */
321 /* If we're crossing a mount point, return root node of mount
322 * point on which the file resides. That's the 'real' last
323 * dir that holds the file we're looking for.
325 if (sym_vp
->v_fs_e
!= res_vp
->v_fs_e
) {
326 assert(sym_vmp
!= NULL
);
328 /* Unlock final file, it might have wrong lock types */
329 if (sym_vp_l
!= NULL
)
330 unlock_vnode(sym_vp
);
331 unlock_vmnt(sym_vmp
);
335 /* Also unlock and release erroneous result */
336 unlock_vnode(*resolve
->l_vnode
);
337 unlock_vmnt(*resolve
->l_vmp
);
340 /* Relock vmnt and vnode with correct lock types */
341 lock_vmnt(sym_vmp
, resolve
->l_vmnt_lock
);
342 lock_vnode(sym_vmp
->m_root_node
, resolve
->l_vnode_lock
);
343 res_vp
= sym_vmp
->m_root_node
;
345 *resolve
->l_vnode
= res_vp
;
346 *resolve
->l_vmp
= sym_vmp
;
348 /* We've effectively resolved the final component, so
349 * change it to current directory to prevent future
350 * 'advances' of returning erroneous results.
352 strlcpy(dir_entry
, ".", NAME_MAX
+1);
356 } while (symloop
< _POSIX_SYMLOOP_MAX
);
358 if (symloop
>= _POSIX_SYMLOOP_MAX
) {
363 if (sym_vp
!= NULL
) {
364 if (sym_vp_l
!= NULL
) {
365 unlock_vnode(sym_vp
);
367 if (sym_vmp
!= NULL
) {
368 unlock_vmnt(sym_vmp
);
373 if (loop_start
!= NULL
) {
374 unlock_vnode(loop_start
);
375 put_vnode(loop_start
);
378 /* Copy the directory entry back to user_fullpath */
379 strlcpy(resolve
->l_path
, dir_entry
, NAME_MAX
+ 1);
381 /* Turn PATH_RET_SYMLINK flag back on if it was on */
382 if (ret_on_symlink
) resolve
->l_flags
|= PATH_RET_SYMLINK
;
387 /*===========================================================================*
389 *===========================================================================*/
390 static int lookup(start_node
, resolve
, result_node
, rfp
)
391 struct vnode
*start_node
;
392 struct lookup
*resolve
;
393 node_details_t
*result_node
;
396 /* Resolve a path name relative to start_node. */
400 size_t path_off
, path_left_len
;
401 ino_t dir_ino
, root_ino
;
404 struct vnode
*dir_vp
;
405 struct vmnt
*vmp
, *vmpres
;
406 struct lookup_res res
;
407 tll_access_t mnt_lock_type
;
409 assert(resolve
->l_vmp
);
410 assert(resolve
->l_vnode
);
412 *(resolve
->l_vmp
) = vmpres
= NULL
; /* No vmnt found nor locked yet */
414 /* Empty (start) path? */
415 if (resolve
->l_path
[0] == '\0') {
416 result_node
->inode_nr
= 0;
420 if (!rfp
->fp_rd
|| !rfp
->fp_wd
) {
421 printf("VFS: lookup %d: no rd/wd\n", rfp
->fp_endpoint
);
425 fs_e
= start_node
->v_fs_e
;
426 dir_ino
= start_node
->v_inode_nr
;
427 vmpres
= find_vmnt(fs_e
);
429 if (vmpres
== NULL
) return(EIO
); /* mountpoint vanished? */
431 /* Is the process' root directory on the same partition?,
432 * if so, set the chroot directory too. */
433 if (rfp
->fp_rd
->v_dev
== rfp
->fp_wd
->v_dev
)
434 root_ino
= rfp
->fp_rd
->v_inode_nr
;
438 /* Set user and group ids according to the system call */
439 uid
= (job_call_nr
== VFS_ACCESS
? rfp
->fp_realuid
: rfp
->fp_effuid
);
440 gid
= (job_call_nr
== VFS_ACCESS
? rfp
->fp_realgid
: rfp
->fp_effgid
);
442 symloop
= 0; /* Number of symlinks seen so far */
445 if (resolve
->l_vmnt_lock
== VMNT_READ
)
446 mnt_lock_type
= VMNT_WRITE
;
448 mnt_lock_type
= resolve
->l_vmnt_lock
;
450 if ((r
= lock_vmnt(vmpres
, mnt_lock_type
)) != OK
) {
451 if (r
== EBUSY
) /* vmnt already locked */
456 *(resolve
->l_vmp
) = vmpres
;
458 /* Issue the request */
459 r
= req_lookup(fs_e
, dir_ino
, root_ino
, uid
, gid
, resolve
, &res
, rfp
);
461 if (r
!= OK
&& r
!= EENTERMOUNT
&& r
!= ELEAVEMOUNT
&& r
!= ESYMLINK
) {
462 if (vmpres
) unlock_vmnt(vmpres
);
463 *(resolve
->l_vmp
) = NULL
;
464 return(r
); /* i.e., an error occured */
467 /* While the response is related to mount control set the
468 * new requests respectively */
469 while (r
== EENTERMOUNT
|| r
== ELEAVEMOUNT
|| r
== ESYMLINK
) {
470 /* Update user_fullpath to reflect what's left to be parsed. */
471 path_off
= res
.char_processed
;
472 path_left_len
= strlen(&resolve
->l_path
[path_off
]);
473 memmove(resolve
->l_path
, &resolve
->l_path
[path_off
], path_left_len
);
474 resolve
->l_path
[path_left_len
] = '\0'; /* terminate string */
476 /* Update the current value of the symloop counter */
477 symloop
+= res
.symloop
;
478 if (symloop
> _POSIX_SYMLOOP_MAX
) {
479 if (vmpres
) unlock_vmnt(vmpres
);
480 *(resolve
->l_vmp
) = NULL
;
484 /* Symlink encountered with absolute path */
488 } else if (r
== EENTERMOUNT
) {
489 /* Entering a new partition */
491 /* Start node is now the mounted partition's root node */
492 for (vmp
= &vmnt
[0]; vmp
!= &vmnt
[NR_MNTS
]; ++vmp
) {
493 if (vmp
->m_dev
!= NO_DEV
&& vmp
->m_mounted_on
) {
494 if (vmp
->m_mounted_on
->v_inode_nr
== res
.inode_nr
&&
495 vmp
->m_mounted_on
->v_fs_e
== res
.fs_e
) {
496 dir_vp
= vmp
->m_root_node
;
501 if (dir_vp
== NULL
) {
502 printf("VFS: path lookup error; root node not found\n");
503 if (vmpres
) unlock_vmnt(vmpres
);
504 *(resolve
->l_vmp
) = NULL
;
508 /* Climbing up mount */
509 /* Find the vmnt that represents the partition on
510 * which we "climb up". */
511 if ((vmp
= find_vmnt(res
.fs_e
)) == NULL
) {
512 panic("VFS lookup: can't find parent vmnt");
515 /* Make sure that the child FS does not feed a bogus path
516 * to the parent FS. That is, when we climb up the tree, we
517 * must've encountered ".." in the path, and that is exactly
518 * what we're going to feed to the parent */
519 if(strncmp(resolve
->l_path
, "..", 2) != 0 ||
520 (resolve
->l_path
[2] != '\0' && resolve
->l_path
[2] != '/')) {
521 printf("VFS: bogus path: %s\n", resolve
->l_path
);
522 if (vmpres
) unlock_vmnt(vmpres
);
523 *(resolve
->l_vmp
) = NULL
;
527 /* Start node is the vnode on which the partition is
529 dir_vp
= vmp
->m_mounted_on
;
532 /* Set the starting directories inode number and FS endpoint */
533 fs_e
= dir_vp
->v_fs_e
;
534 dir_ino
= dir_vp
->v_inode_nr
;
536 /* Is the process' root directory on the same partition?,
537 * if so, set the chroot directory too. */
538 if (dir_vp
->v_dev
== rfp
->fp_rd
->v_dev
)
539 root_ino
= rfp
->fp_rd
->v_inode_nr
;
543 /* Unlock a previously locked vmnt if locked and lock new vmnt */
544 if (vmpres
) unlock_vmnt(vmpres
);
545 vmpres
= find_vmnt(fs_e
);
546 if (vmpres
== NULL
) return(EIO
); /* mount point vanished? */
547 if ((r
= lock_vmnt(vmpres
, mnt_lock_type
)) != OK
) {
549 vmpres
= NULL
; /* Already locked */
553 *(resolve
->l_vmp
) = vmpres
;
555 r
= req_lookup(fs_e
, dir_ino
, root_ino
, uid
, gid
, resolve
, &res
, rfp
);
557 if (r
!= OK
&& r
!= EENTERMOUNT
&& r
!= ELEAVEMOUNT
&& r
!= ESYMLINK
) {
558 if (vmpres
) unlock_vmnt(vmpres
);
559 *(resolve
->l_vmp
) = NULL
;
564 if (*(resolve
->l_vmp
) != NULL
&& resolve
->l_vmnt_lock
!= mnt_lock_type
) {
565 /* downgrade VMNT_WRITE to VMNT_READ */
566 downgrade_vmnt_lock(*(resolve
->l_vmp
));
569 /* Fill in response fields */
570 result_node
->inode_nr
= res
.inode_nr
;
571 result_node
->fmode
= res
.fmode
;
572 result_node
->fsize
= res
.fsize
;
573 result_node
->dev
= res
.dev
;
574 result_node
->fs_e
= res
.fs_e
;
575 result_node
->uid
= res
.uid
;
576 result_node
->gid
= res
.gid
;
581 /*===========================================================================*
583 *===========================================================================*/
584 void lookup_init(resolve
, path
, flags
, vmp
, vp
)
585 struct lookup
*resolve
;
594 resolve
->l_path
= path
;
595 resolve
->l_flags
= flags
;
596 resolve
->l_vmp
= vmp
;
597 resolve
->l_vnode
= vp
;
598 resolve
->l_vmnt_lock
= TLL_NONE
;
599 resolve
->l_vnode_lock
= TLL_NONE
;
600 *vmp
= NULL
; /* Initialize lookup result to NULL */
604 /*===========================================================================*
606 *===========================================================================*/
607 int get_name(dirp
, entry
, ename
)
610 char ename
[NAME_MAX
+ 1];
612 #define DIR_ENTRIES 8
613 #define DIR_ENTRY_SIZE (sizeof(struct dirent) + NAME_MAX)
615 int r
, consumed
, totalbytes
, name_len
;
616 char buf
[DIR_ENTRY_SIZE
* DIR_ENTRIES
];
621 if (!S_ISDIR(dirp
->v_mode
)) return(EBADF
);
624 r
= req_getdents(dirp
->v_fs_e
, dirp
->v_inode_nr
, pos
, (vir_bytes
)buf
,
625 sizeof(buf
), &new_pos
, 1);
628 return(ENOENT
); /* end of entries -- matching inode !found */
630 return(r
); /* error */
633 consumed
= 0; /* bytes consumed */
634 totalbytes
= r
; /* number of bytes to consume */
637 cur
= (struct dirent
*) (buf
+ consumed
);
638 name_len
= cur
->d_reclen
- offsetof(struct dirent
, d_name
) - 1;
640 if(cur
->d_name
+ name_len
+1 > &buf
[sizeof(buf
)])
641 return(EINVAL
); /* Rubbish in dir entry */
642 if (entry
->v_inode_nr
== cur
->d_fileno
) {
643 /* found the entry we were looking for */
644 int copylen
= MIN(name_len
+ 1, NAME_MAX
+ 1);
645 if (strlcpy(ename
, cur
->d_name
, copylen
) >= copylen
) {
646 return(ENAMETOOLONG
);
648 ename
[NAME_MAX
] = '\0';
652 /* not a match -- move on to the next dirent */
653 consumed
+= cur
->d_reclen
;
654 } while (consumed
< totalbytes
);
660 /*===========================================================================*
662 *===========================================================================*/
663 int canonical_path(orig_path
, rfp
)
664 char orig_path
[PATH_MAX
];
667 /* Find canonical path of a given path */
670 struct vnode
*dir_vp
, *parent_dir
;
671 struct vmnt
*dir_vmp
, *parent_vmp
;
672 char component
[NAME_MAX
+1]; /* NAME_MAX does /not/ include '\0' */
673 char temp_path
[PATH_MAX
];
674 struct lookup resolve
;
676 parent_dir
= dir_vp
= NULL
;
677 parent_vmp
= dir_vmp
= NULL
;
678 strlcpy(temp_path
, orig_path
, PATH_MAX
);
679 temp_path
[PATH_MAX
- 1] = '\0';
681 /* First resolve path to the last directory holding the file */
684 unlock_vnode(dir_vp
);
685 unlock_vmnt(dir_vmp
);
689 lookup_init(&resolve
, temp_path
, PATH_NOFLAGS
, &dir_vmp
, &dir_vp
);
690 resolve
.l_vmnt_lock
= VMNT_READ
;
691 resolve
.l_vnode_lock
= VNODE_READ
;
692 if ((dir_vp
= last_dir(&resolve
, rfp
)) == NULL
) return(err_code
);
694 /* dir_vp points to dir and resolve path now contains only the
697 strlcpy(orig_path
, temp_path
, NAME_MAX
+1); /* Store file name */
699 /* If we're just crossing a mount point, our name has changed to '.' */
700 if (!strcmp(orig_path
, ".")) orig_path
[0] = '\0';
702 /* check if the file is a symlink, if so resolve it */
703 r
= rdlink_direct(orig_path
, temp_path
, rfp
);
708 /* encountered a symlink -- loop again */
709 strlcpy(orig_path
, temp_path
, PATH_MAX
);
711 } while (symloop
< _POSIX_SYMLOOP_MAX
);
713 if (symloop
>= _POSIX_SYMLOOP_MAX
) {
715 unlock_vnode(dir_vp
);
716 unlock_vmnt(dir_vmp
);
722 /* We've got the filename and the actual directory holding the file. From
723 * here we start building up the canonical path by climbing up the tree */
724 while (dir_vp
!= rfp
->fp_rd
) {
726 strlcpy(temp_path
, "..", NAME_MAX
+1);
728 /* check if we're at the root node of the file system */
729 if (dir_vp
->v_vmnt
->m_root_node
== dir_vp
) {
730 if (dir_vp
->v_vmnt
->m_mounted_on
== NULL
) {
731 /* Bail out, we can't go any higher */
734 unlock_vnode(dir_vp
);
735 unlock_vmnt(dir_vmp
);
737 dir_vp
= dir_vp
->v_vmnt
->m_mounted_on
;
738 dir_vmp
= dir_vp
->v_vmnt
;
739 if (lock_vmnt(dir_vmp
, VMNT_READ
) != OK
)
740 panic("failed to lock vmnt");
741 if (lock_vnode(dir_vp
, VNODE_READ
) != OK
)
742 panic("failed to lock vnode");
746 lookup_init(&resolve
, temp_path
, PATH_NOFLAGS
, &parent_vmp
,
748 resolve
.l_vmnt_lock
= VMNT_READ
;
749 resolve
.l_vnode_lock
= VNODE_READ
;
751 if ((parent_dir
= advance(dir_vp
, &resolve
, rfp
)) == NULL
) {
752 unlock_vnode(dir_vp
);
753 unlock_vmnt(dir_vmp
);
758 /* now we have to retrieve the name of the parent directory */
759 if ((r
= get_name(parent_dir
, dir_vp
, component
)) != OK
) {
760 unlock_vnode(parent_dir
);
761 unlock_vmnt(parent_vmp
);
762 unlock_vnode(dir_vp
);
763 unlock_vmnt(dir_vmp
);
764 put_vnode(parent_dir
);
769 len
+= strlen(component
) + 1;
770 if (len
>= PATH_MAX
) {
771 /* adding the component to orig_path would exceed PATH_MAX */
772 unlock_vnode(parent_dir
);
773 unlock_vmnt(parent_vmp
);
774 unlock_vnode(dir_vp
);
775 unlock_vmnt(dir_vmp
);
776 put_vnode(parent_dir
);
781 /* Store result of component in orig_path. First make space by moving
782 * the contents of orig_path to the right. Move strlen + 1 bytes to
783 * include the terminating '\0'. Move to strlen + 1 bytes to reserve
784 * space for the slash.
786 memmove(orig_path
+strlen(component
)+1, orig_path
, strlen(orig_path
)+1);
787 /* Copy component into canon_path */
788 memmove(orig_path
, component
, strlen(component
));
789 /* Put slash into place */
790 orig_path
[strlen(component
)] = '/';
792 /* Store parent_dir result, and continue the loop once more */
793 unlock_vnode(dir_vp
);
794 unlock_vmnt(dir_vmp
);
797 dir_vmp
= parent_vmp
;
801 unlock_vmnt(dir_vmp
);
802 unlock_vnode(dir_vp
);
805 /* add the leading slash */
806 len
= strlen(orig_path
);
807 if (strlen(orig_path
) >= PATH_MAX
) return(ENAMETOOLONG
);
808 memmove(orig_path
+1, orig_path
, len
+ 1 /* include terminating nul */);
811 /* remove trailing slash if there is any */
812 if (len
> 1 && orig_path
[len
] == '/') orig_path
[len
] = '\0';
817 /*===========================================================================*
819 *===========================================================================*/
820 static int check_perms(ep
, io_gr
, pathlen
)
829 char canon_path
[PATH_MAX
];
830 struct lookup resolve
;
831 struct sockaddr_un sun
;
833 if (isokendpt(ep
, &slot
) != OK
) return(EINVAL
);
834 if (pathlen
< sizeof(sun
.sun_path
) || pathlen
>= PATH_MAX
) return(EINVAL
);
836 rfp
= &(fproc
[slot
]);
837 r
= sys_safecopyfrom(who_e
, io_gr
, (vir_bytes
) 0, (vir_bytes
) canon_path
,
839 if (r
!= OK
) return(r
);
840 canon_path
[pathlen
] = '\0';
842 /* Turn path into canonical path to the socket file */
843 if ((r
= canonical_path(canon_path
, rfp
)) != OK
) return(r
);
844 if (strlen(canon_path
) >= pathlen
) return(ENAMETOOLONG
);
846 /* copy canon_path back to the caller */
847 r
= sys_safecopyto(who_e
, (cp_grant_id_t
) io_gr
, (vir_bytes
) 0,
848 (vir_bytes
) canon_path
, pathlen
);
849 if (r
!= OK
) return(r
);
851 /* Now do permissions checking */
852 lookup_init(&resolve
, canon_path
, PATH_NOFLAGS
, &vmp
, &vp
);
853 resolve
.l_vmnt_lock
= VMNT_READ
;
854 resolve
.l_vnode_lock
= VNODE_READ
;
855 if ((vp
= eat_path(&resolve
, rfp
)) == NULL
) return(err_code
);
857 /* check permissions */
858 r
= forbidden(rfp
, vp
, (R_BIT
| W_BIT
));
867 /*===========================================================================*
869 *===========================================================================*/
870 int do_checkperms(void)
872 /* This should be replaced by an ACL check. */
873 if (!super_user
) return EPERM
;
875 return check_perms(job_m_in
.m_lsys_vfs_checkperms
.endpt
,
876 job_m_in
.m_lsys_vfs_checkperms
.grant
,
877 job_m_in
.m_lsys_vfs_checkperms
.count
);