]> git.saurik.com Git - apple/xnu.git/blob - bsd/vfs/vfs_lookup.c
xnu-1228.0.2.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_lookup.c
1 /*
2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
29 /*
30 * Copyright (c) 1982, 1986, 1989, 1993
31 * The Regents of the University of California. All rights reserved.
32 * (c) UNIX System Laboratories, Inc.
33 * All or some portions of this file are derived from material licensed
34 * to the University of California by American Telephone and Telegraph
35 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
36 * the permission of UNIX System Laboratories, Inc.
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 * 3. All advertising materials mentioning features or use of this software
47 * must display the following acknowledgement:
48 * This product includes software developed by the University of
49 * California, Berkeley and its contributors.
50 * 4. Neither the name of the University nor the names of its contributors
51 * may be used to endorse or promote products derived from this software
52 * without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
55 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64 * SUCH DAMAGE.
65 *
66 * @(#)vfs_lookup.c 8.10 (Berkeley) 5/27/95
67 */
68 /*
69 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
70 * support for mandatory and extensible security protections. This notice
71 * is included in support of clause 2.2 (b) of the Apple Public License,
72 * Version 2.0.
73 */
74
75 #include <sys/param.h>
76 #include <sys/systm.h>
77 #include <sys/syslimits.h>
78 #include <sys/time.h>
79 #include <sys/namei.h>
80 #include <sys/vm.h>
81 #include <sys/vnode_internal.h>
82 #include <sys/mount_internal.h>
83 #include <sys/errno.h>
84 #include <sys/malloc.h>
85 #include <sys/filedesc.h>
86 #include <sys/proc_internal.h>
87 #include <sys/kdebug.h>
88 #include <sys/unistd.h> /* For _PC_NAME_MAX */
89 #include <sys/uio_internal.h>
90 #include <sys/kauth.h>
91
92 #include <bsm/audit_kernel.h>
93
94 #if CONFIG_MACF
95 #include <security/mac_framework.h>
96 #endif
97
98 #if NAMEDRSRCFORK
99 #include <sys/xattr.h>
100 #endif
101 /*
102 * The minimum volfs-style pathname is 9.
103 * Example: "/.vol/1/2"
104 */
105 #define VOLFS_MIN_PATH_LEN 9
106
107
108 static void kdebug_lookup(struct vnode *dp, struct componentname *cnp);
109
110 #if CONFIG_VOLFS
111 static int vfs_getrealpath(const char * path, char * realpath, size_t bufsize, vfs_context_t ctx);
112 #endif
113
114 /*
115 * Convert a pathname into a pointer to a locked inode.
116 *
117 * The FOLLOW flag is set when symbolic links are to be followed
118 * when they occur at the end of the name translation process.
119 * Symbolic links are always followed for all other pathname
120 * components other than the last.
121 *
122 * The segflg defines whether the name is to be copied from user
123 * space or kernel space.
124 *
125 * Overall outline of namei:
126 *
127 * copy in name
128 * get starting directory
129 * while (!done && !error) {
130 * call lookup to search path.
131 * if symbolic link, massage name in buffer and continue
132 * }
133 *
134 * Returns: 0 Success
135 * ENOENT No such file or directory
136 * ELOOP Too many levels of symbolic links
137 * ENAMETOOLONG Filename too long
138 * copyinstr:EFAULT Bad address
139 * copyinstr:ENAMETOOLONG Filename too long
140 * lookup:EBADF Bad file descriptor
141 * lookup:EROFS
142 * lookup:EACCES
143 * lookup:EPERM
144 * lookup:???
145 * VNOP_READLINK:???
146 */
147 int
148 namei(struct nameidata *ndp)
149 {
150 struct filedesc *fdp; /* pointer to file descriptor state */
151 char *cp; /* pointer into pathname argument */
152 struct vnode *dp; /* the directory we are searching */
153 uio_t auio;
154 int error;
155 struct componentname *cnp = &ndp->ni_cnd;
156 vfs_context_t ctx = cnp->cn_context;
157 proc_t p = vfs_context_proc(ctx);
158 /* XXX ut should be from context */
159 uthread_t ut = (struct uthread *)get_bsdthread_info(current_thread());
160 char *tmppn;
161 char uio_buf[ UIO_SIZEOF(1) ];
162
163 #if DIAGNOSTIC
164 if (!vfs_context_ucred(ctx) || !p)
165 panic ("namei: bad cred/proc");
166 if (cnp->cn_nameiop & (~OPMASK))
167 panic ("namei: nameiop contaminated with flags");
168 if (cnp->cn_flags & OPMASK)
169 panic ("namei: flags contaminated with nameiops");
170 #endif
171 fdp = p->p_fd;
172
173 /*
174 * Get a buffer for the name to be translated, and copy the
175 * name into the buffer.
176 */
177 if ((cnp->cn_flags & HASBUF) == 0) {
178 cnp->cn_pnbuf = ndp->ni_pathbuf;
179 cnp->cn_pnlen = PATHBUFLEN;
180 }
181 #if LP64_DEBUG
182 if (IS_VALID_UIO_SEGFLG(ndp->ni_segflg) == 0) {
183 panic("%s :%d - invalid ni_segflg\n", __FILE__, __LINE__);
184 }
185 #endif /* LP64_DEBUG */
186
187 retry_copy:
188 if (UIO_SEG_IS_USER_SPACE(ndp->ni_segflg)) {
189 error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf,
190 cnp->cn_pnlen, (size_t *)&ndp->ni_pathlen);
191 } else {
192 error = copystr(CAST_DOWN(void *, ndp->ni_dirp), cnp->cn_pnbuf,
193 cnp->cn_pnlen, (size_t *)&ndp->ni_pathlen);
194 }
195 if (error == ENAMETOOLONG && !(cnp->cn_flags & HASBUF)) {
196 MALLOC_ZONE(cnp->cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
197 if (cnp->cn_pnbuf == NULL) {
198 error = ENOMEM;
199 goto error_out;
200 }
201
202 cnp->cn_flags |= HASBUF;
203 cnp->cn_pnlen = MAXPATHLEN;
204
205 goto retry_copy;
206 }
207 if (error)
208 goto error_out;
209
210 #if CONFIG_VOLFS
211 /*
212 * Check for legacy volfs style pathnames.
213 *
214 * For compatibility reasons we currently allow these paths,
215 * but future versions of the OS may not support them.
216 */
217 if (ndp->ni_pathlen >= VOLFS_MIN_PATH_LEN &&
218 cnp->cn_pnbuf[0] == '/' &&
219 cnp->cn_pnbuf[1] == '.' &&
220 cnp->cn_pnbuf[2] == 'v' &&
221 cnp->cn_pnbuf[3] == 'o' &&
222 cnp->cn_pnbuf[4] == 'l' &&
223 cnp->cn_pnbuf[5] == '/' ) {
224 char * realpath;
225 int realpath_err;
226 /* Attempt to resolve a legacy volfs style pathname. */
227 MALLOC_ZONE(realpath, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
228 if (realpath) {
229 if ((realpath_err= vfs_getrealpath(&cnp->cn_pnbuf[6], realpath, MAXPATHLEN, ctx))) {
230 FREE_ZONE(realpath, MAXPATHLEN, M_NAMEI);
231 if (realpath_err == ENOSPC){
232 error = ENAMETOOLONG;
233 goto error_out;
234 }
235 } else {
236 if (cnp->cn_flags & HASBUF) {
237 FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
238 }
239 cnp->cn_pnbuf = realpath;
240 cnp->cn_pnlen = MAXPATHLEN;
241 ndp->ni_pathlen = strlen(realpath) + 1;
242 cnp->cn_flags |= HASBUF | CN_VOLFSPATH;
243 }
244 }
245 }
246 #endif /* CONFIG_VOLFS */
247
248 /* If we are auditing the kernel pathname, save the user pathname */
249 if (cnp->cn_flags & AUDITVNPATH1)
250 AUDIT_ARG(upath, ut->uu_cdir, cnp->cn_pnbuf, ARG_UPATH1);
251 if (cnp->cn_flags & AUDITVNPATH2)
252 AUDIT_ARG(upath, ut->uu_cdir, cnp->cn_pnbuf, ARG_UPATH2);
253
254 /*
255 * Do not allow empty pathnames
256 */
257 if (*cnp->cn_pnbuf == '\0') {
258 error = ENOENT;
259 goto error_out;
260 }
261 ndp->ni_loopcnt = 0;
262
263 /*
264 * determine the starting point for the translation.
265 */
266 if ((ndp->ni_rootdir = fdp->fd_rdir) == NULLVP) {
267 if ( !(fdp->fd_flags & FD_CHROOT))
268 ndp->ni_rootdir = rootvnode;
269 }
270 cnp->cn_nameptr = cnp->cn_pnbuf;
271
272 ndp->ni_usedvp = NULLVP;
273
274 if (*(cnp->cn_nameptr) == '/') {
275 while (*(cnp->cn_nameptr) == '/') {
276 cnp->cn_nameptr++;
277 ndp->ni_pathlen--;
278 }
279 dp = ndp->ni_rootdir;
280 } else if (cnp->cn_flags & USEDVP) {
281 dp = ndp->ni_dvp;
282 ndp->ni_usedvp = dp;
283 } else
284 dp = vfs_context_cwd(ctx);
285
286 if (dp == NULLVP || (dp->v_lflag & VL_DEAD)) {
287 error = ENOENT;
288 goto error_out;
289 }
290 ndp->ni_dvp = NULLVP;
291 ndp->ni_vp = NULLVP;
292
293 for (;;) {
294 int need_newpathbuf;
295 int linklen;
296
297 ndp->ni_startdir = dp;
298
299 if ( (error = lookup(ndp)) ) {
300 goto error_out;
301 }
302 /*
303 * Check for symbolic link
304 */
305 if ((cnp->cn_flags & ISSYMLINK) == 0) {
306 return (0);
307 }
308 if ((cnp->cn_flags & FSNODELOCKHELD)) {
309 cnp->cn_flags &= ~FSNODELOCKHELD;
310 unlock_fsnode(ndp->ni_dvp, NULL);
311 }
312 if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
313 error = ELOOP;
314 break;
315 }
316 #if CONFIG_MACF
317 if ((error = mac_vnode_check_readlink(ctx, ndp->ni_vp)) != 0)
318 break;
319 #endif /* MAC */
320 if (ndp->ni_pathlen > 1 || !(cnp->cn_flags & HASBUF))
321 need_newpathbuf = 1;
322 else
323 need_newpathbuf = 0;
324
325 if (need_newpathbuf) {
326 MALLOC_ZONE(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
327 if (cp == NULL) {
328 error = ENOMEM;
329 break;
330 }
331 } else {
332 cp = cnp->cn_pnbuf;
333 }
334 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
335
336 uio_addiov(auio, CAST_USER_ADDR_T(cp), MAXPATHLEN);
337
338 error = VNOP_READLINK(ndp->ni_vp, auio, ctx);
339 if (error) {
340 if (need_newpathbuf)
341 FREE_ZONE(cp, MAXPATHLEN, M_NAMEI);
342 break;
343 }
344 // LP64todo - fix this
345 linklen = MAXPATHLEN - uio_resid(auio);
346 if (linklen + ndp->ni_pathlen > MAXPATHLEN) {
347 if (need_newpathbuf)
348 FREE_ZONE(cp, MAXPATHLEN, M_NAMEI);
349
350 error = ENAMETOOLONG;
351 break;
352 }
353 if (need_newpathbuf) {
354 long len = cnp->cn_pnlen;
355
356 tmppn = cnp->cn_pnbuf;
357 bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
358 cnp->cn_pnbuf = cp;
359 cnp->cn_pnlen = MAXPATHLEN;
360
361 if ( (cnp->cn_flags & HASBUF) )
362 FREE_ZONE(tmppn, len, M_NAMEI);
363 else
364 cnp->cn_flags |= HASBUF;
365 } else
366 cnp->cn_pnbuf[linklen] = '\0';
367
368 ndp->ni_pathlen += linklen;
369 cnp->cn_nameptr = cnp->cn_pnbuf;
370
371 /*
372 * starting point for 'relative'
373 * symbolic link path
374 */
375 dp = ndp->ni_dvp;
376 /*
377 * get rid of references returned via 'lookup'
378 */
379 vnode_put(ndp->ni_vp);
380 vnode_put(ndp->ni_dvp);
381
382 ndp->ni_vp = NULLVP;
383 ndp->ni_dvp = NULLVP;
384
385 /*
386 * Check if symbolic link restarts us at the root
387 */
388 if (*(cnp->cn_nameptr) == '/') {
389 while (*(cnp->cn_nameptr) == '/') {
390 cnp->cn_nameptr++;
391 ndp->ni_pathlen--;
392 }
393 if ((dp = ndp->ni_rootdir) == NULLVP) {
394 error = ENOENT;
395 goto error_out;
396 }
397 }
398 }
399 /*
400 * only come here if we fail to handle a SYMLINK...
401 * if either ni_dvp or ni_vp is non-NULL, then
402 * we need to drop the iocount that was picked
403 * up in the lookup routine
404 */
405 if (ndp->ni_dvp)
406 vnode_put(ndp->ni_dvp);
407 if (ndp->ni_vp)
408 vnode_put(ndp->ni_vp);
409 error_out:
410 if ( (cnp->cn_flags & HASBUF) ) {
411 cnp->cn_flags &= ~HASBUF;
412 FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
413 }
414 cnp->cn_pnbuf = NULL;
415 ndp->ni_vp = NULLVP;
416
417 return (error);
418 }
419
420
421 /*
422 * Search a pathname.
423 * This is a very central and rather complicated routine.
424 *
425 * The pathname is pointed to by ni_ptr and is of length ni_pathlen.
426 * The starting directory is taken from ni_startdir. The pathname is
427 * descended until done, or a symbolic link is encountered. The variable
428 * ni_more is clear if the path is completed; it is set to one if a
429 * symbolic link needing interpretation is encountered.
430 *
431 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
432 * whether the name is to be looked up, created, renamed, or deleted.
433 * When CREATE, RENAME, or DELETE is specified, information usable in
434 * creating, renaming, or deleting a directory entry may be calculated.
435 * If flag has LOCKPARENT or'ed into it, the parent directory is returned
436 * locked. If flag has WANTPARENT or'ed into it, the parent directory is
437 * returned unlocked. Otherwise the parent directory is not returned. If
438 * the target of the pathname exists and LOCKLEAF is or'ed into the flag
439 * the target is returned locked, otherwise it is returned unlocked.
440 * When creating or renaming and LOCKPARENT is specified, the target may not
441 * be ".". When deleting and LOCKPARENT is specified, the target may be ".".
442 *
443 * Overall outline of lookup:
444 *
445 * dirloop:
446 * identify next component of name at ndp->ni_ptr
447 * handle degenerate case where name is null string
448 * if .. and crossing mount points and on mounted filesys, find parent
449 * call VNOP_LOOKUP routine for next component name
450 * directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set
451 * component vnode returned in ni_vp (if it exists), locked.
452 * if result vnode is mounted on and crossing mount points,
453 * find mounted on vnode
454 * if more components of name, do next level at dirloop
455 * return the answer in ni_vp, locked if LOCKLEAF set
456 * if LOCKPARENT set, return locked parent in ni_dvp
457 * if WANTPARENT set, return unlocked parent in ni_dvp
458 *
459 * Returns: 0 Success
460 * ENOENT No such file or directory
461 * EBADF Bad file descriptor
462 * ENOTDIR Not a directory
463 * EROFS Read-only file system [CREATE]
464 * EISDIR Is a directory [CREATE]
465 * cache_lookup_path:ENOENT
466 * vnode_authorize:EROFS
467 * vnode_authorize:EACCES
468 * vnode_authorize:EPERM
469 * vnode_authorize:???
470 * VNOP_LOOKUP:ENOENT No such file or directory
471 * VNOP_LOOKUP:EJUSTRETURN Restart system call (INTERNAL)
472 * VNOP_LOOKUP:???
473 * VFS_ROOT:ENOTSUP
474 * VFS_ROOT:ENOENT
475 * VFS_ROOT:???
476 */
477 int
478 lookup(struct nameidata *ndp)
479 {
480 char *cp; /* pointer into pathname argument */
481 vnode_t tdp; /* saved dp */
482 vnode_t dp; /* the directory we are searching */
483 mount_t mp; /* mount table entry */
484 int docache = 1; /* == 0 do not cache last component */
485 int wantparent; /* 1 => wantparent or lockparent flag */
486 int rdonly; /* lookup read-only flag bit */
487 int trailing_slash = 0;
488 int dp_authorized = 0;
489 int error = 0;
490 struct componentname *cnp = &ndp->ni_cnd;
491 vfs_context_t ctx = cnp->cn_context;
492 int mounted_on_depth = 0;
493 int dont_cache_mp = 0;
494 vnode_t mounted_on_dp = NULLVP;
495 int current_mount_generation = 0;
496 int vbusyflags = 0;
497 int nc_generation = 0;
498
499 /*
500 * Setup: break out flag bits into variables.
501 */
502 if (cnp->cn_flags & (NOCACHE | DOWHITEOUT)) {
503 if ((cnp->cn_flags & NOCACHE) || (cnp->cn_nameiop == DELETE))
504 docache = 0;
505 }
506 wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT);
507 rdonly = cnp->cn_flags & RDONLY;
508 cnp->cn_flags &= ~ISSYMLINK;
509 cnp->cn_consume = 0;
510
511 dp = ndp->ni_startdir;
512 ndp->ni_startdir = NULLVP;
513
514 if ((cnp->cn_flags & CN_NBMOUNTLOOK) != 0)
515 vbusyflags = LK_NOWAIT;
516 cp = cnp->cn_nameptr;
517
518 if (*cp == '\0') {
519 if ( (vnode_getwithref(dp)) ) {
520 dp = NULLVP;
521 error = ENOENT;
522 goto bad;
523 }
524 goto emptyname;
525 }
526 dirloop:
527 ndp->ni_vp = NULLVP;
528
529 if ( (error = cache_lookup_path(ndp, cnp, dp, ctx, &trailing_slash, &dp_authorized)) ) {
530 dp = NULLVP;
531 goto bad;
532 }
533 if ((cnp->cn_flags & ISLASTCN)) {
534 if (docache)
535 cnp->cn_flags |= MAKEENTRY;
536 } else
537 cnp->cn_flags |= MAKEENTRY;
538
539 dp = ndp->ni_dvp;
540
541 if (ndp->ni_vp != NULLVP) {
542 /*
543 * cache_lookup_path returned a non-NULL ni_vp then,
544 * we're guaranteed that the dp is a VDIR, it's
545 * been authorized, and vp is not ".."
546 *
547 * make sure we don't try to enter the name back into
548 * the cache if this vp is purged before we get to that
549 * check since we won't have serialized behind whatever
550 * activity is occurring in the FS that caused the purge
551 */
552 if (dp != NULLVP)
553 nc_generation = dp->v_nc_generation - 1;
554
555 goto returned_from_lookup_path;
556 }
557
558 /*
559 * Handle "..": two special cases.
560 * 1. If at root directory (e.g. after chroot)
561 * or at absolute root directory
562 * then ignore it so can't get out.
563 * 2. If this vnode is the root of a mounted
564 * filesystem, then replace it with the
565 * vnode which was mounted on so we take the
566 * .. in the other file system.
567 */
568 if ( (cnp->cn_flags & ISDOTDOT) ) {
569 for (;;) {
570 if (dp == ndp->ni_rootdir || dp == rootvnode) {
571 ndp->ni_dvp = dp;
572 ndp->ni_vp = dp;
573 /*
574 * we're pinned at the root
575 * we've already got one reference on 'dp'
576 * courtesy of cache_lookup_path... take
577 * another one for the ".."
578 * if we fail to get the new reference, we'll
579 * drop our original down in 'bad'
580 */
581 if ( (vnode_get(dp)) ) {
582 error = ENOENT;
583 goto bad;
584 }
585 goto nextname;
586 }
587 if ((dp->v_flag & VROOT) == 0 ||
588 (cnp->cn_flags & NOCROSSMOUNT))
589 break;
590 if (dp->v_mount == NULL) { /* forced umount */
591 error = EBADF;
592 goto bad;
593 }
594 tdp = dp;
595 dp = tdp->v_mount->mnt_vnodecovered;
596
597 vnode_put(tdp);
598
599 if ( (vnode_getwithref(dp)) ) {
600 dp = NULLVP;
601 error = ENOENT;
602 goto bad;
603 }
604 ndp->ni_dvp = dp;
605 dp_authorized = 0;
606 }
607 }
608
609 /*
610 * We now have a segment name to search for, and a directory to search.
611 */
612 unionlookup:
613 ndp->ni_vp = NULLVP;
614
615 if (dp->v_type != VDIR) {
616 error = ENOTDIR;
617 goto lookup_error;
618 }
619 if ( (cnp->cn_flags & DONOTAUTH) != DONOTAUTH ) {
620 if (!dp_authorized) {
621 error = vnode_authorize(dp, NULL, KAUTH_VNODE_SEARCH, ctx);
622 if (error)
623 goto lookup_error;
624 }
625 #if CONFIG_MACF
626 error = mac_vnode_check_lookup(ctx, dp, cnp);
627 if (error)
628 goto lookup_error;
629 #endif /* CONFIG_MACF */
630 }
631
632 nc_generation = dp->v_nc_generation;
633
634 if ( (error = VNOP_LOOKUP(dp, &ndp->ni_vp, cnp, ctx)) ) {
635 lookup_error:
636 if ((error == ENOENT) &&
637 (dp->v_flag & VROOT) && (dp->v_mount != NULL) &&
638 (dp->v_mount->mnt_flag & MNT_UNION)) {
639 if ((cnp->cn_flags & FSNODELOCKHELD)) {
640 cnp->cn_flags &= ~FSNODELOCKHELD;
641 unlock_fsnode(dp, NULL);
642 }
643 tdp = dp;
644 dp = tdp->v_mount->mnt_vnodecovered;
645
646 vnode_put(tdp);
647
648 if ( (vnode_getwithref(dp)) ) {
649 dp = NULLVP;
650 error = ENOENT;
651 goto bad;
652 }
653 ndp->ni_dvp = dp;
654 dp_authorized = 0;
655 goto unionlookup;
656 }
657
658 if (error != EJUSTRETURN)
659 goto bad;
660
661 if (ndp->ni_vp != NULLVP)
662 panic("leaf should be empty");
663
664 /*
665 * If creating and at end of pathname, then can consider
666 * allowing file to be created.
667 */
668 if (rdonly) {
669 error = EROFS;
670 goto bad;
671 }
672 if ((cnp->cn_flags & ISLASTCN) && trailing_slash && !(cnp->cn_flags & WILLBEDIR)) {
673 error = ENOENT;
674 goto bad;
675 }
676 /*
677 * We return with ni_vp NULL to indicate that the entry
678 * doesn't currently exist, leaving a pointer to the
679 * referenced directory vnode in ndp->ni_dvp.
680 */
681 if (cnp->cn_flags & SAVESTART) {
682 if ( (vnode_get(ndp->ni_dvp)) ) {
683 error = ENOENT;
684 goto bad;
685 }
686 ndp->ni_startdir = ndp->ni_dvp;
687 }
688 if (!wantparent)
689 vnode_put(ndp->ni_dvp);
690
691 if (kdebug_enable)
692 kdebug_lookup(ndp->ni_dvp, cnp);
693 return (0);
694 }
695 returned_from_lookup_path:
696 dp = ndp->ni_vp;
697
698 /*
699 * Take into account any additional components consumed by
700 * the underlying filesystem.
701 */
702 if (cnp->cn_consume > 0) {
703 cnp->cn_nameptr += cnp->cn_consume;
704 ndp->ni_next += cnp->cn_consume;
705 ndp->ni_pathlen -= cnp->cn_consume;
706 cnp->cn_consume = 0;
707 } else {
708 if (dp->v_name == NULL || dp->v_parent == NULLVP) {
709 int isdot_or_dotdot;
710 int update_flags = 0;
711
712 isdot_or_dotdot = (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') || (cnp->cn_flags & ISDOTDOT);
713
714 if (isdot_or_dotdot == 0) {
715 if (dp->v_name == NULL)
716 update_flags |= VNODE_UPDATE_NAME;
717 if (ndp->ni_dvp != NULLVP && dp->v_parent == NULLVP)
718 update_flags |= VNODE_UPDATE_PARENT;
719
720 if (update_flags)
721 vnode_update_identity(dp, ndp->ni_dvp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_hash, update_flags);
722 }
723 }
724 if ( (cnp->cn_flags & MAKEENTRY) && (dp->v_flag & VNCACHEABLE) && LIST_FIRST(&dp->v_nclinks) == NULL) {
725 /*
726 * missing from name cache, but should
727 * be in it... this can happen if volfs
728 * causes the vnode to be created or the
729 * name cache entry got recycled but the
730 * vnode didn't...
731 * check to make sure that ni_dvp is valid
732 * cache_lookup_path may return a NULL
733 * do a quick check to see if the generation of the
734 * directory matches our snapshot... this will get
735 * rechecked behind the name cache lock, but if it
736 * already fails to match, no need to go any further
737 */
738 if (ndp->ni_dvp != NULLVP && (nc_generation == ndp->ni_dvp->v_nc_generation))
739 cache_enter_with_gen(ndp->ni_dvp, dp, cnp, nc_generation);
740 }
741 }
742
743 mounted_on_dp = dp;
744 mounted_on_depth = 0;
745 dont_cache_mp = 0;
746 current_mount_generation = mount_generation;
747 /*
748 * Check to see if the vnode has been mounted on...
749 * if so find the root of the mounted file system.
750 */
751 check_mounted_on:
752 if ((dp->v_type == VDIR) && dp->v_mountedhere &&
753 ((cnp->cn_flags & NOCROSSMOUNT) == 0)) {
754
755 vnode_lock(dp);
756
757 if ((dp->v_type == VDIR) && (mp = dp->v_mountedhere)) {
758 struct uthread *uth = (struct uthread *)get_bsdthread_info(current_thread());
759
760 mp->mnt_crossref++;
761 vnode_unlock(dp);
762
763
764 if (vfs_busy(mp, vbusyflags)) {
765 mount_dropcrossref(mp, dp, 0);
766 if (vbusyflags == LK_NOWAIT) {
767 error = ENOENT;
768 goto bad2;
769 }
770 goto check_mounted_on;
771 }
772
773 /*
774 * XXX - if this is the last component of the
775 * pathname, and it's either not a lookup operation
776 * or the NOTRIGGER flag is set for the operation,
777 * set a uthread flag to let VFS_ROOT() for autofs
778 * know it shouldn't trigger a mount.
779 */
780 if ((cnp->cn_flags & ISLASTCN) &&
781 (cnp->cn_nameiop != LOOKUP ||
782 (cnp->cn_flags & NOTRIGGER))) {
783 uth->uu_notrigger = 1;
784 dont_cache_mp = 1;
785 }
786 error = VFS_ROOT(mp, &tdp, ctx);
787 /* XXX - clear the uthread flag */
788 uth->uu_notrigger = 0;
789 /*
790 * mount_dropcrossref does a vnode_put
791 * on dp if the 3rd arg is non-zero
792 */
793 mount_dropcrossref(mp, dp, 1);
794 dp = NULL;
795 vfs_unbusy(mp);
796
797 if (error) {
798 goto bad2;
799 }
800 ndp->ni_vp = dp = tdp;
801 mounted_on_depth++;
802
803 goto check_mounted_on;
804 }
805 vnode_unlock(dp);
806 }
807
808 #if CONFIG_MACF
809 if (vfs_flags(vnode_mount(dp)) & MNT_MULTILABEL) {
810 error = vnode_label(vnode_mount(dp), NULL, dp, NULL,
811 VNODE_LABEL_NEEDREF, ctx);
812 if (error)
813 goto bad2;
814 }
815 #endif
816
817 if (mounted_on_depth && !dont_cache_mp) {
818 mp = mounted_on_dp->v_mountedhere;
819
820 if (mp) {
821 mount_lock(mp);
822 mp->mnt_realrootvp_vid = dp->v_id;
823 mp->mnt_realrootvp = dp;
824 mp->mnt_generation = current_mount_generation;
825 mount_unlock(mp);
826 }
827 }
828
829 /*
830 * Check for symbolic link
831 */
832 if ((dp->v_type == VLNK) &&
833 ((cnp->cn_flags & FOLLOW) || trailing_slash || *ndp->ni_next == '/')) {
834 cnp->cn_flags |= ISSYMLINK;
835 return (0);
836 }
837
838 /*
839 * Check for bogus trailing slashes.
840 */
841 if (trailing_slash) {
842 if (dp->v_type != VDIR) {
843 error = ENOTDIR;
844 goto bad2;
845 }
846 trailing_slash = 0;
847 }
848
849 nextname:
850 /*
851 * Not a symbolic link. If more pathname,
852 * continue at next component, else return.
853 */
854 if (*ndp->ni_next == '/') {
855 cnp->cn_nameptr = ndp->ni_next + 1;
856 ndp->ni_pathlen--;
857 while (*cnp->cn_nameptr == '/') {
858 cnp->cn_nameptr++;
859 ndp->ni_pathlen--;
860 }
861 vnode_put(ndp->ni_dvp);
862
863 cp = cnp->cn_nameptr;
864
865 if (*cp == '\0')
866 goto emptyname;
867
868 vnode_put(dp);
869 goto dirloop;
870 }
871
872 /*
873 * Disallow directory write attempts on read-only file systems.
874 */
875 if (rdonly &&
876 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
877 error = EROFS;
878 goto bad2;
879 }
880 if (cnp->cn_flags & SAVESTART) {
881 /*
882 * note that we already hold a reference
883 * on both dp and ni_dvp, but for some reason
884 * can't get another one... in this case we
885 * need to do vnode_put on dp in 'bad2'
886 */
887 if ( (vnode_get(ndp->ni_dvp)) ) {
888 error = ENOENT;
889 goto bad2;
890 }
891 ndp->ni_startdir = ndp->ni_dvp;
892 }
893 if (!wantparent && ndp->ni_dvp) {
894 vnode_put(ndp->ni_dvp);
895 ndp->ni_dvp = NULLVP;
896 }
897
898 if (cnp->cn_flags & AUDITVNPATH1)
899 AUDIT_ARG(vnpath, dp, ARG_VNODE1);
900 else if (cnp->cn_flags & AUDITVNPATH2)
901 AUDIT_ARG(vnpath, dp, ARG_VNODE2);
902
903 #if NAMEDRSRCFORK
904 /*
905 * Caller wants the resource fork.
906 */
907 if ((cnp->cn_flags & CN_WANTSRSRCFORK) && (dp != NULLVP)) {
908 vnode_t svp = NULLVP;
909 enum nsoperation nsop;
910
911 if (dp->v_type != VREG) {
912 error = ENOENT;
913 goto bad2;
914 }
915 switch (cnp->cn_nameiop) {
916 case DELETE:
917 nsop = NS_DELETE;
918 break;
919 case CREATE:
920 nsop = NS_CREATE;
921 break;
922 case LOOKUP:
923 /* Make sure our lookup of "/..namedfork/rsrc" is allowed. */
924 if (cnp->cn_flags & CN_ALLOWRSRCFORK) {
925 nsop = NS_OPEN;
926 } else {
927 error = EPERM;
928 goto bad2;
929 }
930 break;
931 default:
932 error = EPERM;
933 goto bad2;
934 }
935 /* Ask the file system for the resource fork. */
936 error = vnode_getnamedstream(dp, &svp, XATTR_RESOURCEFORK_NAME, nsop, 0, ctx);
937
938 /* During a create, it OK for stream vnode to be missing. */
939 if (error == ENOATTR || error == ENOENT) {
940 error = (nsop == NS_CREATE) ? 0 : ENOENT;
941 }
942 if (error) {
943 goto bad2;
944 }
945 /* The "parent" of the stream is the file. */
946 if (wantparent) {
947 if (ndp->ni_dvp) {
948 if (ndp->ni_cnd.cn_flags & FSNODELOCKHELD) {
949 ndp->ni_cnd.cn_flags &= ~FSNODELOCKHELD;
950 unlock_fsnode(ndp->ni_dvp, NULL);
951 }
952 vnode_put(ndp->ni_dvp);
953 }
954 ndp->ni_dvp = dp;
955 } else {
956 vnode_put(dp);
957 }
958 ndp->ni_vp = dp = svp; /* on create this may be null */
959
960 /* Restore the truncated pathname buffer (for audits). */
961 if (ndp->ni_pathlen == 1 && ndp->ni_next[0] == '\0') {
962 ndp->ni_next[0] = '/';
963 }
964 cnp->cn_flags &= ~MAKEENTRY;
965 }
966 #endif
967 if (kdebug_enable)
968 kdebug_lookup(dp, cnp);
969 return (0);
970
971 emptyname:
972 cnp->cn_namelen = 0;
973 /*
974 * A degenerate name (e.g. / or "") which is a way of
975 * talking about a directory, e.g. like "/." or ".".
976 */
977 if (dp->v_type != VDIR) {
978 error = ENOTDIR;
979 goto bad;
980 }
981 if (cnp->cn_nameiop != LOOKUP) {
982 error = EISDIR;
983 goto bad;
984 }
985 if (wantparent) {
986 /*
987 * note that we already hold a reference
988 * on dp, but for some reason can't
989 * get another one... in this case we
990 * need to do vnode_put on dp in 'bad'
991 */
992 if ( (vnode_get(dp)) ) {
993 error = ENOENT;
994 goto bad;
995 }
996 ndp->ni_dvp = dp;
997 }
998 cnp->cn_flags &= ~ISDOTDOT;
999 cnp->cn_flags |= ISLASTCN;
1000 ndp->ni_next = cp;
1001 ndp->ni_vp = dp;
1002
1003 if (cnp->cn_flags & AUDITVNPATH1)
1004 AUDIT_ARG(vnpath, dp, ARG_VNODE1);
1005 else if (cnp->cn_flags & AUDITVNPATH2)
1006 AUDIT_ARG(vnpath, dp, ARG_VNODE2);
1007 if (cnp->cn_flags & SAVESTART)
1008 panic("lookup: SAVESTART");
1009 return (0);
1010
1011 bad2:
1012 if ((cnp->cn_flags & FSNODELOCKHELD)) {
1013 cnp->cn_flags &= ~FSNODELOCKHELD;
1014 unlock_fsnode(ndp->ni_dvp, NULL);
1015 }
1016 if (ndp->ni_dvp)
1017 vnode_put(ndp->ni_dvp);
1018 if (dp)
1019 vnode_put(dp);
1020 ndp->ni_vp = NULLVP;
1021
1022 if (kdebug_enable)
1023 kdebug_lookup(dp, cnp);
1024 return (error);
1025
1026 bad:
1027 if ((cnp->cn_flags & FSNODELOCKHELD)) {
1028 cnp->cn_flags &= ~FSNODELOCKHELD;
1029 unlock_fsnode(ndp->ni_dvp, NULL);
1030 }
1031 if (dp)
1032 vnode_put(dp);
1033 ndp->ni_vp = NULLVP;
1034
1035 if (kdebug_enable)
1036 kdebug_lookup(dp, cnp);
1037 return (error);
1038 }
1039
1040 /*
1041 * relookup - lookup a path name component
1042 * Used by lookup to re-aquire things.
1043 */
1044 int
1045 relookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp)
1046 {
1047 struct vnode *dp = NULL; /* the directory we are searching */
1048 int wantparent; /* 1 => wantparent or lockparent flag */
1049 int rdonly; /* lookup read-only flag bit */
1050 int error = 0;
1051 #ifdef NAMEI_DIAGNOSTIC
1052 int i, newhash; /* DEBUG: check name hash */
1053 char *cp; /* DEBUG: check name ptr/len */
1054 #endif
1055 vfs_context_t ctx = cnp->cn_context;;
1056
1057 /*
1058 * Setup: break out flag bits into variables.
1059 */
1060 wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT);
1061 rdonly = cnp->cn_flags & RDONLY;
1062 cnp->cn_flags &= ~ISSYMLINK;
1063
1064 if (cnp->cn_flags & NOCACHE)
1065 cnp->cn_flags &= ~MAKEENTRY;
1066 else
1067 cnp->cn_flags |= MAKEENTRY;
1068
1069 dp = dvp;
1070
1071 /*
1072 * Check for degenerate name (e.g. / or "")
1073 * which is a way of talking about a directory,
1074 * e.g. like "/." or ".".
1075 */
1076 if (cnp->cn_nameptr[0] == '\0') {
1077 if (cnp->cn_nameiop != LOOKUP || wantparent) {
1078 error = EISDIR;
1079 goto bad;
1080 }
1081 if (dp->v_type != VDIR) {
1082 error = ENOTDIR;
1083 goto bad;
1084 }
1085 if ( (vnode_get(dp)) ) {
1086 error = ENOENT;
1087 goto bad;
1088 }
1089 *vpp = dp;
1090
1091 if (cnp->cn_flags & SAVESTART)
1092 panic("lookup: SAVESTART");
1093 return (0);
1094 }
1095 /*
1096 * We now have a segment name to search for, and a directory to search.
1097 */
1098 if ( (error = VNOP_LOOKUP(dp, vpp, cnp, ctx)) ) {
1099 if (error != EJUSTRETURN)
1100 goto bad;
1101 #if DIAGNOSTIC
1102 if (*vpp != NULL)
1103 panic("leaf should be empty");
1104 #endif
1105 /*
1106 * If creating and at end of pathname, then can consider
1107 * allowing file to be created.
1108 */
1109 if (rdonly) {
1110 error = EROFS;
1111 goto bad;
1112 }
1113 /*
1114 * We return with ni_vp NULL to indicate that the entry
1115 * doesn't currently exist, leaving a pointer to the
1116 * (possibly locked) directory inode in ndp->ni_dvp.
1117 */
1118 return (0);
1119 }
1120 dp = *vpp;
1121
1122 #if DIAGNOSTIC
1123 /*
1124 * Check for symbolic link
1125 */
1126 if (dp->v_type == VLNK && (cnp->cn_flags & FOLLOW))
1127 panic ("relookup: symlink found.\n");
1128 #endif
1129
1130 /*
1131 * Disallow directory write attempts on read-only file systems.
1132 */
1133 if (rdonly &&
1134 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
1135 error = EROFS;
1136 goto bad2;
1137 }
1138 /* ASSERT(dvp == ndp->ni_startdir) */
1139
1140 return (0);
1141
1142 bad2:
1143 vnode_put(dp);
1144 bad:
1145 *vpp = NULL;
1146
1147 return (error);
1148 }
1149
1150 /*
1151 * Free pathname buffer
1152 */
1153 void
1154 nameidone(struct nameidata *ndp)
1155 {
1156 if ((ndp->ni_cnd.cn_flags & FSNODELOCKHELD)) {
1157 ndp->ni_cnd.cn_flags &= ~FSNODELOCKHELD;
1158 unlock_fsnode(ndp->ni_dvp, NULL);
1159 }
1160 if (ndp->ni_cnd.cn_flags & HASBUF) {
1161 char *tmp = ndp->ni_cnd.cn_pnbuf;
1162
1163 ndp->ni_cnd.cn_pnbuf = NULL;
1164 ndp->ni_cnd.cn_flags &= ~HASBUF;
1165 FREE_ZONE(tmp, ndp->ni_cnd.cn_pnlen, M_NAMEI);
1166 }
1167 }
1168
1169
1170 #define NUMPARMS 23
1171
1172 /*
1173 * Log (part of) a pathname using the KERNEL_DEBUG_CONSTANT mechanism, as used
1174 * by fs_usage. The path up to and including the current component name are
1175 * logged. Up to NUMPARMS*4 bytes of pathname will be logged. If the path
1176 * to be logged is longer than that, then the last NUMPARMS*4 bytes are logged.
1177 * That is, the truncation removes the leading portion of the path.
1178 *
1179 * The logging is done via multiple KERNEL_DEBUG_CONSTANT calls. The first one
1180 * is marked with DBG_FUNC_START. The last one is marked with DBG_FUNC_END
1181 * (in addition to DBG_FUNC_START if it is also the first). There may be
1182 * intermediate ones with neither DBG_FUNC_START nor DBG_FUNC_END.
1183 *
1184 * The first KERNEL_DEBUG_CONSTANT passes the vnode pointer and 12 bytes of
1185 * pathname. The remaining KERNEL_DEBUG_CONSTANT calls add 16 bytes of pathname
1186 * each. The minimum number of KERNEL_DEBUG_CONSTANT calls required to pass
1187 * the path are used. Any excess padding in the final KERNEL_DEBUG_CONSTANT
1188 * (because not all of the 12 or 16 bytes are needed for the remainder of the
1189 * path) is set to zero bytes, or '>' if there is more path beyond the
1190 * current component name (usually because an intermediate component was not
1191 * found).
1192 *
1193 * NOTE: If the path length is greater than NUMPARMS*4, or is not of the form
1194 * 12+N*16, there will be no padding.
1195 *
1196 * TODO: If there is more path beyond the current component name, should we
1197 * force some padding? For example, a lookup for /foo_bar_baz/spam that
1198 * fails because /foo_bar_baz is not found will only log "/foo_bar_baz", with
1199 * no '>' padding. But /foo_bar/spam would log "/foo_bar>>>>".
1200 */
1201 static void
1202 kdebug_lookup(struct vnode *dp, struct componentname *cnp)
1203 {
1204 unsigned int i;
1205 int code;
1206 int dbg_namelen;
1207 char *dbg_nameptr;
1208 long dbg_parms[NUMPARMS];
1209
1210 /* Collect the pathname for tracing */
1211 dbg_namelen = (cnp->cn_nameptr - cnp->cn_pnbuf) + cnp->cn_namelen;
1212 dbg_nameptr = cnp->cn_nameptr + cnp->cn_namelen;
1213
1214 if (dbg_namelen > (int)sizeof(dbg_parms))
1215 dbg_namelen = sizeof(dbg_parms);
1216 dbg_nameptr -= dbg_namelen;
1217
1218 /* Copy the (possibly truncated) path itself */
1219 memcpy(dbg_parms, dbg_nameptr, dbg_namelen);
1220
1221 /* Pad with '\0' or '>' */
1222 if (dbg_namelen < (int)sizeof(dbg_parms)) {
1223 memset((char *)dbg_parms + dbg_namelen,
1224 *(cnp->cn_nameptr + cnp->cn_namelen) ? '>' : 0,
1225 sizeof(dbg_parms) - dbg_namelen);
1226 }
1227
1228 /*
1229 * In the event that we collect multiple, consecutive pathname
1230 * entries, we must mark the start of the path's string and the end.
1231 */
1232 code = (FSDBG_CODE(DBG_FSRW,36)) | DBG_FUNC_START;
1233
1234 if (dbg_namelen <= 12)
1235 code |= DBG_FUNC_END;
1236
1237 KERNEL_DEBUG_CONSTANT(code, (unsigned int)dp, dbg_parms[0], dbg_parms[1], dbg_parms[2], 0);
1238
1239 code &= ~DBG_FUNC_START;
1240
1241 for (i=3, dbg_namelen -= 12; dbg_namelen > 0; i+=4, dbg_namelen -= 16) {
1242 if (dbg_namelen <= 16)
1243 code |= DBG_FUNC_END;
1244
1245 KERNEL_DEBUG_CONSTANT(code, dbg_parms[i], dbg_parms[i+1], dbg_parms[i+2], dbg_parms[i+3], 0);
1246 }
1247 }
1248
1249 /*
1250 * Obtain the real path from a legacy volfs style path.
1251 *
1252 * Valid formats of input path:
1253 *
1254 * "555/@"
1255 * "555/2"
1256 * "555/123456"
1257 * "555/123456/foobar"
1258 *
1259 * Where:
1260 * 555 represents the volfs file system id
1261 * '@' and '2' are aliases to the root of a file system
1262 * 123456 represents a file id
1263 * "foobar" represents a file name
1264 */
1265 #if CONFIG_VOLFS
1266 static int
1267 vfs_getrealpath(const char * path, char * realpath, size_t bufsize, vfs_context_t ctx)
1268 {
1269 vnode_t vp;
1270 struct mount *mp = NULL;
1271 char *str;
1272 char ch;
1273 unsigned long id;
1274 ino64_t ino;
1275 int error;
1276 int length;
1277
1278 /* Get file system id and move str to next component. */
1279 id = strtoul(path, &str, 10);
1280 if (id == 0 || str[0] != '/') {
1281 return (EINVAL);
1282 }
1283 while (*str == '/') {
1284 str++;
1285 }
1286 ch = *str;
1287
1288 mp = mount_lookupby_volfsid(id, 1);
1289 if (mp == NULL) {
1290 return (EINVAL); /* unexpected failure */
1291 }
1292 /* Check for an alias to a file system root. */
1293 if (ch == '@' && str[1] == '\0') {
1294 ino = 2;
1295 str++;
1296 } else {
1297 /* Get file id and move str to next component. */
1298 ino = strtouq(str, &str, 10);
1299 }
1300
1301 /* Get the target vnode. */
1302 if (ino == 2) {
1303 error = VFS_ROOT(mp, &vp, ctx);
1304 } else {
1305 error = VFS_VGET(mp, ino, &vp, ctx);
1306 }
1307 vfs_unbusy(mp);
1308 if (error) {
1309 goto out;
1310 }
1311 realpath[0] = '\0';
1312
1313 /* Get the absolute path to this vnode. */
1314 error = build_path(vp, realpath, bufsize, &length, 0, ctx);
1315 vnode_put(vp);
1316
1317 if (error == 0 && *str != '\0') {
1318 int attempt = strlcat(realpath, str, MAXPATHLEN);
1319 if (attempt > MAXPATHLEN){
1320 error = ENAMETOOLONG;
1321 }
1322 }
1323 out:
1324 return (error);
1325 }
1326 #endif