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