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