]> git.saurik.com Git - apple/xnu.git/blob - bsd/miscfs/volfs/volfs_vnops.c
xnu-792.17.14.tar.gz
[apple/xnu.git] / bsd / miscfs / volfs / volfs_vnops.c
1 /*
2 * Copyright (c) 1998-2004 Apple Computer, 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
29 #include <mach/mach_types.h>
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/resourcevar.h>
34 #include <sys/kernel.h>
35 #include <sys/file.h>
36 #include <sys/filedesc.h>
37 #include <sys/stat.h>
38 #include <sys/proc_internal.h> /* for p_fd */
39 #include <sys/kauth.h>
40 #include <sys/conf.h>
41 #include <sys/mount_internal.h>
42 #include <sys/vnode_internal.h>
43 #include <sys/malloc.h>
44 #include <sys/dirent.h>
45 #include <sys/namei.h>
46 #include <sys/attr.h>
47 #include <sys/kdebug.h>
48 #include <sys/queue.h>
49 #include <sys/uio_internal.h>
50
51 #include <sys/vm.h>
52 #include <sys/errno.h>
53 #include <vfs/vfs_support.h>
54
55 #include <kern/locks.h>
56
57 #include "volfs.h"
58
59 /*
60 * volfs acts as a bridge between the requirements of the MacOS API and the Unix API.
61 * MacOS applications describe files by a <Volume ID><Directory ID><File Name> triple.
62 * The Unix API describes files by pathname. Volfs is a virtual file system that sits over
63 * the HFS VFS interface and allows files to be described by a <Volume ID>/<Directory ID>/<File Name>
64 * pathname.
65 *
66 * The root of the volfs filesystem consists of directories named the volume ID's of all the
67 * currently mounted filesystems which support the VFS vget() routine. Each of those directories
68 * supports the lookup by file ID of all files and directories within the filesystem. When a
69 * file or directory is resolved its vnode from that filesystem rather than a volfs vnode is returned
70 * allowing immediate access to the target file or directory.
71 *
72 * Readdir on the root of the volfs filesystem returns the list of available file systems. Readdir
73 * on a filesystem node, however, returns only . and .. since it is not practical to list all
74 * of the file ID's in a timely fashion and furthermore VFS does not provide a mechanism for
75 * enumerating all of the file id's.
76 *
77 * Volume ID's are taken from the low 32 bits of the f_fsid field, formatted as a base 10 ASCII
78 * string with no leading zeros (volume ID 1 is represented as "1").
79 *
80 * File ID's are created in same manner, with their 32 bits formatted as a base 10 ASCII
81 * string with no leading zeros.
82 *
83 * Volfs does create a security hole since it is possible to bypass directory permissions higher
84 * in the namespace tree. This security hole is about the same as the one created by NFS which uses
85 * a similar mechanism.
86 */
87
88 static int volfs_reclaim (struct vnop_reclaim_args*);
89 static int volfs_getattr (struct vnop_getattr_args *);
90 static int volfs_select (struct vnop_select_args *);
91 static int volfs_rmdir (struct vnop_rmdir_args *);
92 static int volfs_readdir (struct vnop_readdir_args *);
93 static int volfs_pathconf (struct vnop_pathconf_args *);
94 static int volfs_lookup (struct vnop_lookup_args *);
95
96 static int volfs_readdir_callback(mount_t, void *);
97 static int get_filevnode(struct mount *parent_fs, u_int id, vnode_t *ret_vnode, vfs_context_t context);
98 static int get_fsvnode(struct mount *our_mount, int id, vnode_t *ret_vnode);
99
100 /* for the call back function in volfs_readdir */
101 struct volfs_rdstruct {
102 int validindex;
103 vnode_t vp;
104 int rec_offset;
105 struct uio * uio;
106 };
107
108 #define VOPFUNC int (*)(void *)
109
110 /* Global vfs data structures for volfs. */
111 int (**volfs_vnodeop_p) (void *);
112 struct vnodeopv_entry_desc volfs_vnodeop_entries[] = {
113 {&vnop_default_desc, (VOPFUNC)vn_default_error},
114 {&vnop_strategy_desc, (VOPFUNC)err_strategy}, /* strategy */
115 {&vnop_bwrite_desc, (VOPFUNC)err_bwrite}, /* bwrite */
116 {&vnop_lookup_desc, (VOPFUNC)volfs_lookup}, /* lookup */
117 {&vnop_create_desc, (VOPFUNC)err_create}, /* create */
118 {&vnop_whiteout_desc, (VOPFUNC)err_whiteout}, /* whiteout */
119 {&vnop_mknod_desc, (VOPFUNC)err_mknod}, /* mknod */
120 {&vnop_open_desc, (VOPFUNC)nop_open}, /* open */
121 {&vnop_close_desc, (VOPFUNC)nop_close}, /* close */
122 {&vnop_getattr_desc, (VOPFUNC)volfs_getattr}, /* getattr */
123 {&vnop_setattr_desc, (VOPFUNC)err_setattr}, /* setattr */
124 {&vnop_getattrlist_desc, (VOPFUNC)err_getattrlist}, /* getattrlist */
125 {&vnop_setattrlist_desc, (VOPFUNC)err_setattrlist}, /* setattrlist */
126 {&vnop_read_desc, (VOPFUNC)err_read}, /* read */
127 {&vnop_write_desc, (VOPFUNC)err_write}, /* write */
128 {&vnop_ioctl_desc, (VOPFUNC)err_ioctl}, /* ioctl */
129 {&vnop_select_desc, (VOPFUNC)volfs_select}, /* select */
130 {&vnop_exchange_desc, (VOPFUNC)err_exchange}, /* exchange */
131 {&vnop_revoke_desc, (VOPFUNC)nop_revoke}, /* revoke */
132 {&vnop_mmap_desc, (VOPFUNC)err_mmap}, /* mmap */
133 {&vnop_fsync_desc, (VOPFUNC)err_fsync}, /* fsync */
134 {&vnop_remove_desc, (VOPFUNC)err_remove}, /* remove */
135 {&vnop_link_desc, (VOPFUNC)err_link}, /* link */
136 {&vnop_rename_desc, (VOPFUNC)err_rename}, /* rename */
137 {&vnop_mkdir_desc, (VOPFUNC)err_mkdir}, /* mkdir */
138 {&vnop_rmdir_desc, (VOPFUNC)volfs_rmdir}, /* rmdir */
139 {&vnop_symlink_desc, (VOPFUNC)err_symlink}, /* symlink */
140 {&vnop_readdir_desc, (VOPFUNC)volfs_readdir}, /* readdir */
141 {&vnop_readdirattr_desc, (VOPFUNC)err_readdirattr}, /* readdirattr */
142 {&vnop_readlink_desc, (VOPFUNC)err_readlink}, /* readlink */
143 {&vnop_inactive_desc, (VOPFUNC)err_inactive}, /* inactive */
144 {&vnop_reclaim_desc, (VOPFUNC)volfs_reclaim}, /* reclaim */
145 {&vnop_pathconf_desc, (VOPFUNC)volfs_pathconf}, /* pathconf */
146 {&vnop_advlock_desc, (VOPFUNC)err_advlock}, /* advlock */
147 {&vnop_allocate_desc, (VOPFUNC)err_allocate}, /* allocate */
148 {&vnop_pagein_desc, (VOPFUNC)err_pagein}, /* pagein */
149 {&vnop_pageout_desc, (VOPFUNC)err_pageout}, /* pageout */
150 {&vnop_devblocksize_desc, (VOPFUNC)err_devblocksize}, /* devblocksize */
151 {&vnop_searchfs_desc, (VOPFUNC)err_searchfs}, /* searchfs */
152 {&vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* Copyfile */
153 {&vnop_blktooff_desc, (VOPFUNC)err_blktooff}, /* blktooff */
154 {&vnop_offtoblk_desc, (VOPFUNC)err_offtoblk }, /* offtoblk */
155 {&vnop_blockmap_desc, (VOPFUNC)err_blockmap }, /* blockmap */
156 {(struct vnodeop_desc *) NULL, (int (*) ()) NULL}
157 };
158
159 /*
160 * Oh what a tangled web we weave. This structure will be used by
161 * bsd/vfs/vfs_conf.c to actually do the initialization of volfs_vnodeop_p
162 */
163 struct vnodeopv_desc volfs_vnodeop_opv_desc =
164 {&volfs_vnodeop_p, volfs_vnodeop_entries};
165
166 static char gDotDot[] = "..";
167
168 struct finfo {
169 fsobj_id_t parID;
170 };
171
172 struct finfoattrbuf {
173 unsigned long length;
174 struct finfo fi;
175 };
176
177
178 static int volfs_getattr_callback(mount_t, void *);
179
180
181 /*
182 * volfs_reclaim - Reclaim a vnode so that it can be used for other purposes.
183 */
184 static int
185 volfs_reclaim(ap)
186 struct vnop_reclaim_args /* { struct vnode *a_vp; vfs_context_t a_context; } */ *ap;
187 {
188 struct vnode *vp = ap->a_vp;
189 void *data = vp->v_data;
190
191 vp->v_data = NULL;
192 FREE(data, M_VOLFSNODE);
193
194 return (0);
195 }
196
197 struct volfsgetattr_struct{
198 int numMounts;
199 vnode_t a_vp;
200 };
201
202 static int
203 volfs_getattr_callback(mount_t mp, void * arg)
204 {
205 struct volfsgetattr_struct *vstrp = (struct volfsgetattr_struct *)arg;
206
207 if (mp != vnode_mount(vstrp->a_vp) && validfsnode(mp))
208 vstrp->numMounts++;
209 return(VFS_RETURNED);
210 }
211
212 /*
213 * volfs_getattr - fill in the attributes for this vnode
214 */
215 static int
216 volfs_getattr(ap)
217 struct vnop_getattr_args /* { struct vnode *a_vp; struct vnode_attr *a_vap;
218 vfs_context_t a_context; } */ *ap;
219 {
220 struct volfs_vndata *priv_data;
221 struct vnode *a_vp;
222 struct vnode_attr *a_vap;
223 int numMounts = 0;
224 struct volfsgetattr_struct vstr;
225 struct timespec ts;
226
227 a_vp = ap->a_vp;
228 a_vap = ap->a_vap;
229
230 priv_data = a_vp->v_data;
231
232 VATTR_RETURN(a_vap, va_type, VDIR);
233 VATTR_RETURN(a_vap, va_mode, 0555);
234 VATTR_RETURN(a_vap, va_nlink, 2);
235 VATTR_RETURN(a_vap, va_uid, 0);
236 VATTR_RETURN(a_vap, va_gid, 0);
237 VATTR_RETURN(a_vap, va_fsid, (int) a_vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
238 VATTR_RETURN(a_vap, va_fileid, (uint64_t)((u_long)priv_data->nodeID));
239 VATTR_RETURN(a_vap, va_acl, NULL);
240
241 /*
242 * If it's the root vnode calculate its size based on the number of eligible
243 * file systems
244 */
245 if (priv_data->vnode_type == VOLFS_ROOT) {
246 vstr.numMounts = 0;
247 vstr.a_vp = a_vp;
248
249 vfs_iterate(LK_NOWAIT, volfs_getattr_callback, (void *)&vstr);
250
251 numMounts = vstr.numMounts;
252
253 VATTR_RETURN(a_vap, va_data_size, (numMounts + 2) * VLFSDIRENTLEN);
254 } else {
255 VATTR_RETURN(a_vap, va_data_size, 2 * VLFSDIRENTLEN);
256 }
257
258 VATTR_RETURN(a_vap, va_iosize, 512);
259 ts.tv_sec = boottime_sec();
260 ts.tv_nsec = 0;
261 VATTR_RETURN(a_vap, va_access_time, ts);
262 VATTR_RETURN(a_vap, va_modify_time, ts);
263 VATTR_RETURN(a_vap, va_change_time, ts);
264
265 VATTR_RETURN(a_vap, va_gen, 0);
266 VATTR_RETURN(a_vap, va_flags, 0);
267 VATTR_RETURN(a_vap, va_rdev, 0);
268 VATTR_RETURN(a_vap, va_filerev, 0);
269
270 return (0);
271 }
272
273 /*
274 * volfs_select - just say OK. Only possible op is readdir
275 */
276 static int
277 volfs_select(__unused struct vnop_select_args *ap)
278 {
279 return (1);
280 }
281
282 /*
283 * vofls_rmdir - not possible to remove directories in volfs
284 */
285 static int
286 volfs_rmdir(ap)
287 struct vnop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp;
288 struct componentname *a_cnp; vfs_context_t a_context; } */ *ap;
289 {
290 if (ap->a_dvp == ap->a_vp) {
291 (void) nop_rmdir(ap);
292 return (EINVAL);
293 } else
294 return (err_rmdir(ap));
295 }
296
297
298
299 static int
300 volfs_readdir_callback(mount_t mp, void * v)
301 {
302 struct volfs_rdstruct * vcsp = (struct volfs_rdstruct *)v;
303 struct dirent local_dir;
304 int error;
305
306 if ((mp != vnode_mount(vcsp->vp)) && validfsnode(mp))
307 vcsp->validindex++;
308
309 if (vcsp->rec_offset == vcsp->validindex)
310 {
311 local_dir.d_fileno = mp->mnt_vfsstat.f_fsid.val[0];
312 local_dir.d_type = DT_DIR;
313 local_dir.d_reclen = VLFSDIRENTLEN;
314 local_dir.d_namlen = sprintf(&local_dir.d_name[0], "%d", mp->mnt_vfsstat.f_fsid.val[0]);
315 error = uiomove((char *) &local_dir, VLFSDIRENTLEN, vcsp->uio);
316 vcsp->rec_offset++;
317 }
318
319 return(VFS_RETURNED);
320 }
321
322 /*
323 * volfs_readdir - Get directory entries
324 *
325 * Directory listings are only produced for the root volfs node. Filesystems
326 * just return . & ..
327 * Filesystems contained within the volfs root are named by the decimal
328 * equivalent of the f_fsid.val[0] from their mount structure (typically
329 * the device id of the volume). The maximum length for a name, then is
330 * 10 characters.
331 */
332 static int
333 volfs_readdir(ap)
334 struct vnop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio;
335 * int *a_eofflag; int
336 *ncookies; u_long **a_cookies; vfs_context_t a_context; } */ *ap;
337 {
338 struct volfs_vndata *priv_data;
339 register struct uio *uio = ap->a_uio;
340 int error = 0;
341 size_t count, lost;
342 int rec_offset;
343 struct dirent local_dir;
344 int i;
345 int starting_resid;
346 off_t off;
347 struct volfs_rdstruct vcs;
348
349 off = uio->uio_offset;
350 priv_data = ap->a_vp->v_data;
351 // LP64todo - fix this!
352 starting_resid = count = uio_resid(uio);
353
354 /* Make sure we don't return partial entries. */
355 count -= (uio->uio_offset + count) & (VLFSDIRENTLEN - 1);
356 if (count <= 0) {
357 return (EINVAL);
358 }
359 /*
360 * Make sure we're starting on a directory boundary
361 */
362 if (off & (VLFSDIRENTLEN - 1)) {
363 return (EINVAL);
364 }
365 rec_offset = off / VLFSDIRENTLEN;
366 // LP64todo - fix this!
367 lost = uio_resid(uio) - count;
368 uio_setresid(uio, count);
369 uio_iov_len_set(uio, count);
370 #if LP64_DEBUG
371 if (IS_VALID_UIO_SEGFLG(uio->uio_segflg) == 0) {
372 panic("%s :%d - invalid uio_segflg\n", __FILE__, __LINE__);
373 }
374 #endif /* LP64_DEBUG */
375
376 local_dir.d_reclen = VLFSDIRENTLEN;
377 /*
378 * We must synthesize . and ..
379 */
380
381 if (rec_offset == 0)
382 {
383 /*
384 * Synthesize .
385 */
386 local_dir.d_fileno = priv_data->nodeID;
387 local_dir.d_type = DT_DIR;
388 local_dir.d_namlen = 1;
389 local_dir.d_name[0] = '.';
390 for (i = 1; i < MAXVLFSNAMLEN; i++)
391 local_dir.d_name[i] = 0;
392 error = uiomove((char *) &local_dir, VLFSDIRENTLEN, uio);
393 rec_offset++;
394 }
395 if (rec_offset == 1)
396 {
397 /*
398 * Synthesize ..
399 * We only have two levels in the volfs hierarchy. Root's
400 * .. points to itself and the second level points to root,
401 * hence we've hardcoded d_fileno for .. here
402 */
403 local_dir.d_fileno = ROOT_DIRID;
404 local_dir.d_type = DT_DIR;
405 local_dir.d_namlen = 2;
406 local_dir.d_name[0] = '.';
407 local_dir.d_name[1] = '.';
408 for (i = 2; i < MAXVLFSNAMLEN; i++)
409 local_dir.d_name[i] = 0;
410 error = uiomove((char *) &local_dir, VLFSDIRENTLEN, uio);
411 rec_offset++;
412 }
413
414 /*
415 * OK, we've given them the . & .. entries. If this is a
416 * filesystem node then we've gone as far as we're going
417 * to go
418 */
419 if (priv_data->vnode_type == VOLFS_FSNODE)
420 {
421 *ap->a_eofflag = 1; /* we got all the way to the end */
422 return (error);
423 }
424
425 if (rec_offset > 1) {
426 vcs.validindex = 1; /* we always have "." and ".." */
427 vcs.rec_offset = rec_offset;
428 vcs.vp = ap->a_vp;
429 vcs.uio = uio;
430
431
432 vfs_iterate(0, volfs_readdir_callback, &vcs);
433
434 //if (mp == (void *) &mountlist)
435 *ap->a_eofflag = 1; /* we got all the way to the end */
436 }
437 uio_setresid(uio, (uio_resid(uio) + lost));
438
439 if (starting_resid == uio_resid(uio))
440 uio->uio_offset = 0;
441
442 return (error);
443 }
444
445
446 /*
447 * validfsnode - test to see if a file system supports VGET
448 *
449 * This can cause context switching, so caller should be lock safe
450 */
451 int
452 validfsnode(struct mount *fsnode)
453 {
454
455 /*
456 * Just check to see if the the mount flag is set, if it is we assume the
457 * file system supports all of volfs symantecs
458 */
459
460 if ((! (fsnode->mnt_kern_flag & MNTK_UNMOUNT)) && (fsnode->mnt_flag & MNT_DOVOLFS))
461 return 1;
462 else
463 return 0;
464 }
465
466 /*
467 * volfs_pathconf - Return POSIX pathconf information applicable to ufs filesystems.
468 */
469 static int
470 volfs_pathconf(ap)
471 struct vnop_pathconf_args /* { struct vnode *a_vp; int a_name; int
472 *a_retval; vfs_context_t a_context; } */ *ap;
473 {
474 switch (ap->a_name)
475 {
476 case _PC_LINK_MAX:
477 *ap->a_retval = LINK_MAX;
478 return (0);
479 case _PC_NAME_MAX:
480 *ap->a_retval = NAME_MAX;
481 return (0);
482 case _PC_PATH_MAX:
483 *ap->a_retval = PATH_MAX;
484 return (0);
485 case _PC_PIPE_BUF:
486 *ap->a_retval = PIPE_BUF;
487 return (0);
488 case _PC_CHOWN_RESTRICTED:
489 *ap->a_retval = 1;
490 return (0);
491 case _PC_NO_TRUNC:
492 *ap->a_retval = 1;
493 return (0);
494 default:
495 return (EINVAL);
496 }
497 /* NOTREACHED */
498 }
499
500 /*
501 * get_parentvp() - internal routine that tries to lookup the parent of vpp.
502 * On success, *vpp is the parent vp and is returned with a reference.
503 */
504 static int
505 get_parentvp(struct vnode **vpp, struct mount *mp, vfs_context_t context)
506 {
507 int result;
508 struct vnode_attr va;
509 struct vnode *child_vp = *vpp;
510
511 VATTR_INIT(&va);
512 VATTR_WANTED(&va, va_parentid);
513 result = vnode_getattr(child_vp, &va, context);
514 if (result) {
515 return result;
516 }
517
518 /* Shift attention to the parent directory vnode: */
519 result = VFS_VGET(mp, (ino64_t)va.va_parentid, vpp, context);
520
521 if (result == 0 && child_vp->v_parent != *vpp) {
522 vnode_update_identity(child_vp, *vpp, NULL, 0, 0, VNODE_UPDATE_PARENT);
523 }
524
525 return result;
526 }
527
528
529 /*
530 * Look up the parent directory of a given vnode.
531 */
532 static int
533 lookup_parent(vnode_t child_vp, vnode_t *parent_vpp, int is_authorized, vfs_context_t context)
534 {
535 struct componentname cn;
536 vnode_t new_vp;
537 int error;
538
539 *parent_vpp = NULLVP;
540
541 if (is_authorized == 0) {
542 error = vnode_authorize(child_vp, NULL, KAUTH_VNODE_SEARCH, context);
543 if (error != 0) {
544 return (error);
545 }
546 }
547 new_vp = child_vp->v_parent;
548
549 if (new_vp != NULLVP) {
550 if ( (error = vnode_getwithref(new_vp)) == 0 )
551 *parent_vpp = new_vp;
552 return (error);
553 }
554 bzero(&cn, sizeof(cn));
555 cn.cn_nameiop = LOOKUP;
556 cn.cn_context = context;
557 cn.cn_pnbuf = CAST_DOWN(caddr_t, &gDotDot);
558 cn.cn_pnlen = strlen(cn.cn_pnbuf);
559 cn.cn_nameptr = cn.cn_pnbuf;
560 cn.cn_namelen = cn.cn_pnlen;
561 cn.cn_flags = (FOLLOW | LOCKLEAF | ISLASTCN | ISDOTDOT);
562
563 error = VNOP_LOOKUP(child_vp, &new_vp, &cn, context);
564 if (error != 0) {
565 return(error);
566 }
567 if (new_vp == child_vp) {
568 vnode_put(new_vp);
569 return ELOOP;
570 }
571 if (child_vp->v_parent == NULLVP) {
572 vnode_update_identity(child_vp, new_vp, NULL, 0, 0, VNODE_UPDATE_PARENT);
573 }
574 *parent_vpp = new_vp;
575 return 0;
576 }
577
578
579 /*
580 * verify_fullpathaccess(ret_vnode);
581 */
582
583 static int
584 verify_fullpathaccess(struct vnode *targetvp, vfs_context_t context)
585 {
586 struct vnode *vp, *parent_vp;
587 struct mount *mp = targetvp->v_mount;
588 struct proc *p = vfs_context_proc(context);
589 int result;
590 int dp_authorized;
591 struct filedesc *fdp = p->p_fd; /* pointer to file descriptor state */
592
593 vp = targetvp;
594 dp_authorized = 0;
595
596 /* get the parent directory. */
597 if ((vp->v_flag & VROOT) == 0 && vp != fdp->fd_cdir && vp != fdp->fd_rdir) {
598 if (vp->v_parent == NULLVP || (vp->v_flag & VISHARDLINK) || (vnode_getwithref(vp->v_parent) != 0)) {
599 if (vp->v_type == VDIR) {
600 result = lookup_parent(vp, &parent_vp, dp_authorized, context);
601
602 /*
603 * If the lookup fails with EACCES and the vp is a directory,
604 * we should try again but bypass authorization check. Without this
605 * workaround directories that you can navigate to but not traverse will
606 * disappear when clicked in the Finder.
607 */
608 if (result == EACCES && (vp->v_flag & VROOT) == 0) {
609 dp_authorized = 1; /* bypass auth check */
610 if (lookup_parent(vp, &parent_vp, dp_authorized, context) == 0) {
611 result = 0;
612 }
613 dp_authorized = 0; /* force us to authorize */
614 }
615 vp = parent_vp;
616 }
617 else {
618 /*
619 * this is not a directory so we must get parent object ID
620 */
621 result = get_parentvp(&vp, mp, context);
622 parent_vp = vp;
623 }
624 if (result != 0)
625 goto err_exit;
626 }
627 else {
628 /*
629 * we where able to get a reference on v_parent
630 */
631 parent_vp = vp = vp->v_parent;
632 }
633 }
634
635 /*
636 * Keep going up until either the process's root or the process's working
637 * directory is hit, either one of which are potential valid starting points
638 * for a full pathname
639 */
640 while (vp != NULLVP) {
641
642 result = reverse_lookup(vp, &parent_vp, fdp, context, &dp_authorized);
643 if (result == 0) {
644 /*
645 * we're done and we have access
646 */
647 break;
648 }
649 if (vp != parent_vp) {
650 /*
651 * we where able to walk up the parent chain so now we don't need
652 * vp any longer
653 */
654 vnode_put(vp);
655 vp = parent_vp;
656 }
657 /*
658 * we have a referenced vp at this point... if dp_authorized == 1, than
659 * it's been authorized for search, but v_parent was NULL...
660 * if dp_authorized == 0, than we need to do the authorization check
661 * before looking up the parent
662 */
663 if ((vp->v_flag & VROOT) != 0 ||
664 vp == fdp->fd_cdir || vp == fdp->fd_rdir) {
665 /*
666 * we're already at the termination point, which implies that
667 * the authorization check in the cache failed (otherwise we
668 * would have returned 'done' from "reverse_lookup"... so,
669 * do the authorization and bail
670 */
671 result = vnode_authorize(vp, NULL, KAUTH_VNODE_SEARCH, context);
672 goto lookup_exit;
673 }
674 result = lookup_parent(vp, &parent_vp, dp_authorized, context);
675 if (result != 0) {
676 goto lookup_exit;
677 }
678 if (vp != parent_vp) {
679 /*
680 * got the parent so now we don't need vp any longer
681 */
682 vnode_put(vp);
683 vp = parent_vp;
684 }
685 } /* while loop */
686
687 /*
688 * Success: the caller has complete access to the initial vnode
689 */
690 result = 0;
691
692 lookup_exit:
693 if (vp != NULLVP && vp != targetvp) {
694 vnode_put(vp);
695 }
696
697 err_exit:
698 return result;
699 };
700
701
702 /*
703 * get_fsvnode - internal routine to create a vnode for a file system. Called with mount pointer,
704 * id of filesystem to lookup and pointer to vnode pointer to fill in
705 */
706 static int
707 get_fsvnode(struct mount *our_mount, int id, vnode_t *ret_vnode)
708 {
709 struct mount *cur_mount;
710 fsid_t cur_fsid;
711 struct vnode *cur_vnode;
712 struct volfs_vndata *cur_privdata;
713 int retval;
714 struct vnode_fsparam vfsp;
715 int vid = 0;
716
717 /*
718 * OK, first look up the matching mount on the list of mounted file systems
719 */
720 /* the following will return the mount point with vfs_busy held */
721 cur_mount = mount_lookupby_volfsid(id, 1);
722
723 if (cur_mount == NULL) {
724 /*
725 * No mounted file system by the specified ID currently exists in the system.
726 *
727 * XXX We could deal with a vnode that is still hanging about for an FS that
728 * does not exists or has been unmounted now, or count on the update below
729 * to happen later...
730 */
731 *ret_vnode = NULL;
732 return ENOENT;
733 };
734
735 cur_fsid = cur_mount->mnt_vfsstat.f_fsid;
736
737 /*
738 * Now search the list attached to the mount structure to
739 * see if this vnode is already floating around
740 */
741 search_vnodelist:
742 mount_lock(our_mount);
743 TAILQ_FOREACH(cur_vnode, &our_mount->mnt_vnodelist, v_mntvnodes) {
744 cur_privdata = (struct volfs_vndata *) cur_vnode->v_data;
745 if (cur_privdata->nodeID == (unsigned int)id)
746 {
747 if (cur_privdata->fs_mount != cur_mount) {
748 cur_privdata->fs_mount = cur_mount;
749 cur_privdata->fs_fsid = cur_fsid;
750 };
751 break;
752 }
753 }
754 mount_unlock(our_mount);
755
756 if (cur_vnode) {
757 vid = vnode_vid(cur_vnode);
758
759 /*
760 * use vnode_getwithvid since it will wait for a vnode currently being
761 * terminated... if it returns an error, cur_vnode will not be what we
762 * think it is, try again
763 */
764 if (vnode_getwithvid(cur_vnode, vid) != 0) {
765 goto search_vnodelist;
766 };
767 }
768 else
769 {
770 MALLOC(cur_privdata, struct volfs_vndata *,
771 sizeof(struct volfs_vndata), M_VOLFSNODE, M_WAITOK);
772
773 cur_privdata->vnode_type = VOLFS_FSNODE;
774 cur_privdata->nodeID = id;
775
776 cur_privdata->fs_mount = cur_mount;
777 cur_privdata->fs_fsid = cur_fsid;
778
779 vfsp.vnfs_mp = our_mount;
780 vfsp.vnfs_vtype = VDIR;
781 vfsp.vnfs_str = "volfs";
782 vfsp.vnfs_dvp = 0;
783 vfsp.vnfs_fsnode = cur_privdata;
784 vfsp.vnfs_cnp = 0;
785 vfsp.vnfs_vops = volfs_vnodeop_p;
786 vfsp.vnfs_rdev = 0;
787 vfsp.vnfs_filesize = 0;
788 vfsp.vnfs_flags = VNFS_NOCACHE | VNFS_CANTCACHE;
789 vfsp.vnfs_marksystem = 0;
790 vfsp.vnfs_markroot = 0;
791
792 retval = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, &cur_vnode);
793 if (retval != 0) {
794 FREE(cur_privdata, M_VOLFSNODE);
795 goto out;
796 };
797 cur_vnode->v_tag = VT_VOLFS;
798
799 }
800
801 *ret_vnode = cur_vnode;
802 retval = 0;
803 out:
804 vfs_unbusy(cur_mount);
805 return (retval);
806 }
807
808
809
810 /*
811 * get_filevnode - returns the vnode for the given id within a filesystem. The parent vnode
812 * is a filesystem, id is the 32-bit id of the file/directory and ret_vnode is a pointer
813 * to a vnode pointer
814 */
815 static int
816 get_filevnode(struct mount *parent_fs, u_int id, vnode_t *ret_vnode, vfs_context_t context)
817 {
818 int retval;
819
820 again:
821 /*
822 * Special case 2 to mean the root of a file system
823 */
824 if (id == 2)
825 retval = VFS_ROOT(parent_fs, ret_vnode, context);
826 else
827 retval = VFS_VGET(parent_fs, (ino64_t)id, ret_vnode, context);
828 if (retval) goto error;
829
830 retval = verify_fullpathaccess(*ret_vnode, context);
831 if (retval) {
832 /* An error was encountered verifying that the caller has,
833 in fact, got access all the way from "/" or their working
834 directory to the specified item...
835 */
836 vnode_put(*ret_vnode);
837 *ret_vnode = NULL;
838 /* vnode was recycled during access verification. */
839 if (retval == EAGAIN) {
840 goto again;
841 }
842 };
843
844 error:
845 return (retval);
846 }
847
848
849 static int
850 volfs_lookup(struct vnop_lookup_args *ap)
851 {
852 struct volfs_vndata *priv_data;
853 char *nameptr;
854 long namelen;
855 struct mount *parent_fs;
856 vnode_t vp;
857 int isdot_or_dotdot = 0;
858 int ret_err = ENOENT;
859 char firstchar;
860 int ret_val;
861
862 #if 0
863 KERNEL_DEBUG((FSDBG_CODE(DBG_FSVN, 8)) | DBG_FUNC_START,
864 (unsigned int)ap->a_dvp, (unsigned int)ap->a_cnp, (unsigned int)p, 0, 0);
865 #endif
866 priv_data = ap->a_dvp->v_data;
867 nameptr = ap->a_cnp->cn_nameptr;
868 namelen = ap->a_cnp->cn_namelen;
869 firstchar = nameptr[0];
870
871 /* First check for "." and ".." */
872 if (firstchar == '.') {
873 if (namelen == 1) {
874 /* "." requested */
875 isdot_or_dotdot = 1;
876 *ap->a_vpp = ap->a_dvp;
877 vnode_get(*ap->a_vpp);
878 ret_err = 0;
879 } else if (nameptr[1] == '.' && namelen == 2) {
880 /* ".." requested */
881 isdot_or_dotdot = 1;
882 ret_err = VFS_ROOT(ap->a_dvp->v_mount, ap->a_vpp, ap->a_context);
883 }
884 } else if (firstchar == '@') { /* '@' is alias for system root */
885 if ((namelen == 1) && (priv_data->vnode_type != VOLFS_ROOT)) {
886 /* the following returns with iteration count on mount point */
887 parent_fs = mount_list_lookupby_fsid(&priv_data->fs_fsid, 0, 1);
888 if (parent_fs) {
889 ret_val = vfs_busy(parent_fs, LK_NOWAIT);
890 mount_iterdrop(parent_fs);
891 if (ret_val !=0) {
892 *ap->a_vpp = NULL;
893 ret_err = ENOENT;
894 } else {
895 ret_err = VFS_ROOT(parent_fs, ap->a_vpp, ap->a_context);
896 vfs_unbusy(parent_fs);
897 }
898 } else {
899 *ap->a_vpp = NULL;
900 ret_err = ENOENT;
901 }
902 } else {
903 *ap->a_vpp = NULL;
904 ret_err = ENOENT;
905 }
906 } else if (namelen <= 10 && firstchar > '0' && firstchar <= '9') {
907 char *check_ptr;
908 u_long id;
909
910 id = strtoul(nameptr, &check_ptr, 10);
911
912 /*
913 * strtol will leave us at the first non-numeric character.
914 * we've checked to make sure the component name does
915 * begin with a numeric so check_ptr must wind up on
916 * the terminating null or there was other junk following the
917 * number
918 */
919 if ((check_ptr - nameptr) == namelen) {
920 if (priv_data->vnode_type == VOLFS_ROOT) {
921 /*
922 * OPTIMIZATION
923 *
924 * Obtain the mountpoint and call VFS_VGET in
925 * one step (ie without creating a vnode for
926 * the mountpoint).
927 */
928 if (check_ptr[0] == '/' &&
929 check_ptr[1] > '0' && check_ptr[1] <= '9') {
930 struct mount *mp;
931 struct vnode *vp;
932 u_long id2;
933 char *endptr;
934
935 /* this call will return mount point with vfs_busy held */
936 mp = mount_lookupby_volfsid(id, 1);
937 if (mp == NULL) {
938 *ap->a_vpp = NULL;
939 return ENOENT;
940 }
941 id2 = strtoul(&check_ptr[1], &endptr, 10);
942 if ((endptr[0] == '/' || endptr[0] == '\0') &&
943 get_filevnode(mp, id2, &vp, ap->a_context) == 0) {
944 ap->a_cnp->cn_consume = endptr - check_ptr;
945 *ap->a_vpp = vp;
946 vfs_unbusy(mp);
947 return (0);
948 }
949 vfs_unbusy(mp);
950 }
951 /* Fall through to default behavior... */
952
953 ret_err = get_fsvnode(ap->a_dvp->v_mount, id, ap->a_vpp);
954
955 } else {
956 parent_fs = mount_list_lookupby_fsid(&priv_data->fs_fsid, 0, 1);
957 if (parent_fs) {
958 ret_val = vfs_busy(parent_fs, LK_NOWAIT);
959 mount_iterdrop(parent_fs);
960 if (ret_val !=0) {
961 *ap->a_vpp = NULL;
962 ret_err = ENOENT;
963 } else {
964 ret_err = get_filevnode(parent_fs, id, ap->a_vpp, ap->a_context);
965 vfs_unbusy(parent_fs);
966 }
967 } else {
968 *ap->a_vpp = NULL;
969 ret_err = ENOENT;
970 }
971 }
972 }
973 }
974 vp = *ap->a_vpp;
975
976 if ( ret_err == 0 && !isdot_or_dotdot && (vp != NULLVP) && (vp->v_parent == NULLVP))
977 vnode_update_identity(vp, ap->a_dvp, NULL, 0, 0, VNODE_UPDATE_PARENT);
978
979 #if 0
980 KERNEL_DEBUG((FSDBG_CODE(DBG_FSVN, 8)) | DBG_FUNC_START,
981 (unsigned int)ap->a_dvp, (unsigned int)ap->a_cnp, (unsigned int)p, ret_err, 0);
982 #endif
983 return (ret_err);
984 }
985