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>
18 #include <sys/dirent.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
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 /*===========================================================================*
38 *===========================================================================*/
40 advance(struct vnode
*dirp
, struct lookup
*resolve
, struct fproc
*rfp
)
42 /* Resolve a path name starting at dirp to a vnode. */
45 struct vnode
*new_vp
, *vp
;
47 struct node_details res
= {0,0,0,0,0,0,0};
48 tll_access_t initial_locktype
;
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
;
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
) {
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. */
86 if (vp
->v_mapfs_e
!= NONE
) vp
->v_mapfs_count
= 1;
88 vp
->v_fs_count
++; /* We got a reference from the FS */
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;
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
);
121 if (resolve
->l_vnode_lock
== VNODE_READ
)
129 /*===========================================================================*
131 *===========================================================================*/
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 /*===========================================================================*
144 *===========================================================================*/
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.
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
;
170 ret_on_symlink
= !!(resolve
->l_flags
& PATH_RET_SYMLINK
);
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
;
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. */
190 #if !DO_POSIX_PATHNAME_RES
191 /* Remove trailing slashes */
192 while (len
> 1 && resolve
->l_path
[len
-1] == '/') {
194 resolve
->l_path
[len
]= '\0';
198 cp
= strrchr(resolve
->l_path
, '/');
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
;
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);
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
;
222 dir_entry
[NAME_MAX
] = '\0';
225 /* Remove trailing slashes */
226 while (cp
> resolve
->l_path
&& cp
[0] == '/') {
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
) {
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
) {
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);
276 /* Failed to read link */
278 unlock_vnode(res_vp
);
279 unlock_vmnt(*resolve
->l_vmp
);
281 *resolve
->l_vmp
= NULL
;
282 *resolve
->l_vnode
= NULL
;
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
);
293 unlock_vmnt(sym_vmp
);
294 *resolve
->l_vmp
= NULL
;
300 /* Relative symlinks are relative to res_vp, not cwd */
301 if (resolve
->l_path
[0] != '/') {
304 /* Absolute symlink, forget about res_vp */
305 unlock_vnode(res_vp
);
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
);
328 /* Also unlock and release erroneous result */
329 unlock_vnode(*resolve
->l_vnode
);
330 unlock_vmnt(*resolve
->l_vmp
);
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
;
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);
349 } while (symloop
< _POSIX_SYMLOOP_MAX
);
351 if (symloop
>= _POSIX_SYMLOOP_MAX
) {
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
);
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
;
380 /*===========================================================================*
382 *===========================================================================*/
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. */
390 size_t path_off
, path_left_len
;
391 ino_t dir_ino
, root_ino
;
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;
410 if (!rfp
->fp_rd
|| !rfp
->fp_wd
) {
411 printf("VFS: lookup %d: no rd/wd\n", rfp
->fp_endpoint
);
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
;
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 */
435 if (resolve
->l_vmnt_lock
== VMNT_READ
)
436 mnt_lock_type
= VMNT_WRITE
;
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 */
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
;
474 /* Symlink encountered with absolute path */
478 } else if (r
== EENTERMOUNT
) {
479 /* Entering a new partition */
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
;
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
;
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
;
517 /* Start node is the vnode on which the partition is
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
;
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
) {
539 vmpres
= NULL
; /* Already locked */
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
;
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
;
571 /*===========================================================================*
573 *===========================================================================*/
575 lookup_init(struct lookup
*resolve
, char *path
, int flags
, struct vmnt
**vmp
, struct vnode
**vp
)
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 */
590 /*===========================================================================*
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)
599 int r
, consumed
, totalbytes
, name_len
;
600 char buf
[DIR_ENTRY_SIZE
* DIR_ENTRIES
];
605 if (!S_ISDIR(dirp
->v_mode
)) return(EBADF
);
608 r
= req_getdents(dirp
->v_fs_e
, dirp
->v_inode_nr
, pos
, (vir_bytes
)buf
,
609 sizeof(buf
), &new_pos
, 1);
612 return(ENOENT
); /* end of entries -- matching inode !found */
614 return(r
); /* error */
617 consumed
= 0; /* bytes consumed */
618 totalbytes
= r
; /* number of bytes to consume */
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';
636 /* not a match -- move on to the next dirent */
637 consumed
+= cur
->d_reclen
;
638 } while (consumed
< totalbytes
);
644 /*===========================================================================*
646 *===========================================================================*/
648 canonical_path(char orig_path
[PATH_MAX
], struct fproc
*rfp
)
650 /* Find canonical path of a given path */
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 */
667 unlock_vnode(dir_vp
);
668 unlock_vmnt(dir_vmp
);
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
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
);
691 /* encountered a symlink -- loop again */
692 strlcpy(orig_path
, temp_path
, PATH_MAX
);
694 } while (symloop
< _POSIX_SYMLOOP_MAX
);
696 if (symloop
>= _POSIX_SYMLOOP_MAX
) {
698 unlock_vnode(dir_vp
);
699 unlock_vmnt(dir_vmp
);
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 */
717 unlock_vnode(dir_vp
);
718 unlock_vmnt(dir_vmp
);
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");
729 lookup_init(&resolve
, temp_path
, PATH_NOFLAGS
, &parent_vmp
,
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
);
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
);
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
);
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
);
780 dir_vmp
= parent_vmp
;
784 unlock_vmnt(dir_vmp
);
785 unlock_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 */);
794 /* remove trailing slash if there is any */
795 if (len
> 1 && orig_path
[len
] == '/') orig_path
[len
] = '\0';
800 /*===========================================================================*
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.
816 struct vnode
*dirp
, *vp
;
817 struct vmnt
*vmp
, *vmp2
;
820 struct lookup resolve
, resolve2
;
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
);
834 /* Copy in the path name, which must not be empty. It is typically not null
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.
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 */
863 r
= forbidden(rfp
, vp
, R_BIT
| W_BIT
);
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
;
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
))
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
);
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
);
904 job_m_out
.m_vfs_lsys_socketpath
.device
=
906 job_m_out
.m_vfs_lsys_socketpath
.inode
=
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
);
919 } else if (r
== EEXIST
)