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