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