]> git.saurik.com Git - apple/xnu.git/blob - bsd/miscfs/volfs/volfs_vnops.c
xnu-792.tar.gz
[apple/xnu.git] / bsd / miscfs / volfs / volfs_vnops.c
1 /*
2 * Copyright (c) 1998-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
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>
30 #include <sys/filedesc.h>
31 #include <sys/stat.h>
32 #include <sys/proc_internal.h> /* for p_fd */
33 #include <sys/kauth.h>
34 #include <sys/conf.h>
35 #include <sys/mount_internal.h>
36 #include <sys/vnode_internal.h>
37 #include <sys/malloc.h>
38 #include <sys/dirent.h>
39 #include <sys/namei.h>
40 #include <sys/attr.h>
41 #include <sys/kdebug.h>
42 #include <sys/queue.h>
43 #include <sys/uio_internal.h>
44
45 #include <sys/vm.h>
46 #include <sys/errno.h>
47 #include <vfs/vfs_support.h>
48
49 #include <kern/locks.h>
50
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
82 static int volfs_reclaim (struct vnop_reclaim_args*);
83 static int volfs_getattr (struct vnop_getattr_args *);
84 static int volfs_select (struct vnop_select_args *);
85 static int volfs_rmdir (struct vnop_rmdir_args *);
86 static int volfs_readdir (struct vnop_readdir_args *);
87 static int volfs_pathconf (struct vnop_pathconf_args *);
88 static int volfs_lookup (struct vnop_lookup_args *);
89
90 static int volfs_readdir_callback(mount_t, void *);
91 static int get_filevnode(struct mount *parent_fs, u_int id, vnode_t *ret_vnode, vfs_context_t context);
92 static int get_fsvnode(struct mount *our_mount, int id, vnode_t *ret_vnode);
93
94 /* for the call back function in volfs_readdir */
95 struct volfs_rdstruct {
96 int validindex;
97 vnode_t vp;
98 int rec_offset;
99 struct uio * uio;
100 };
101
102 #define VOPFUNC int (*)(void *)
103
104 /* Global vfs data structures for volfs. */
105 int (**volfs_vnodeop_p) (void *);
106 struct vnodeopv_entry_desc volfs_vnodeop_entries[] = {
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 */
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 */
157 struct vnodeopv_desc volfs_vnodeop_opv_desc =
158 {&volfs_vnodeop_p, volfs_vnodeop_entries};
159
160 static char gDotDot[] = "..";
161
162 struct finfo {
163 fsobj_id_t parID;
164 };
165
166 struct finfoattrbuf {
167 unsigned long length;
168 struct finfo fi;
169 };
170
171
172 static int volfs_getattr_callback(mount_t, void *);
173
174
175 /*
176 * volfs_reclaim - Reclaim a vnode so that it can be used for other purposes.
177 */
178 static int
179 volfs_reclaim(ap)
180 struct vnop_reclaim_args /* { struct vnode *a_vp; vfs_context_t a_context; } */ *ap;
181 {
182 struct vnode *vp = ap->a_vp;
183 void *data = vp->v_data;
184
185 vp->v_data = NULL;
186 FREE(data, M_VOLFSNODE);
187
188 return (0);
189 }
190
191 struct volfsgetattr_struct{
192 int numMounts;
193 vnode_t a_vp;
194 };
195
196 static int
197 volfs_getattr_callback(mount_t mp, void * arg)
198 {
199 struct volfsgetattr_struct *vstrp = (struct volfsgetattr_struct *)arg;
200
201 if (mp != vnode_mount(vstrp->a_vp) && validfsnode(mp))
202 vstrp->numMounts++;
203 return(VFS_RETURNED);
204 }
205
206 /*
207 * volfs_getattr - fill in the attributes for this vnode
208 */
209 static int
210 volfs_getattr(ap)
211 struct vnop_getattr_args /* { struct vnode *a_vp; struct vnode_attr *a_vap;
212 vfs_context_t a_context; } */ *ap;
213 {
214 struct volfs_vndata *priv_data;
215 struct vnode *a_vp;
216 struct vnode_attr *a_vap;
217 int numMounts = 0;
218 struct volfsgetattr_struct vstr;
219 struct timespec ts;
220
221 a_vp = ap->a_vp;
222 a_vap = ap->a_vap;
223
224 priv_data = a_vp->v_data;
225
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);
234
235 /*
236 * If it's the root vnode calculate its size based on the number of eligible
237 * file systems
238 */
239 if (priv_data->vnode_type == VOLFS_ROOT) {
240 vstr.numMounts = 0;
241 vstr.a_vp = a_vp;
242
243 vfs_iterate(LK_NOWAIT, volfs_getattr_callback, (void *)&vstr);
244
245 numMounts = vstr.numMounts;
246
247 VATTR_RETURN(a_vap, va_data_size, (numMounts + 2) * VLFSDIRENTLEN);
248 } else {
249 VATTR_RETURN(a_vap, va_data_size, 2 * VLFSDIRENTLEN);
250 }
251
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);
258
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);
263
264 return (0);
265 }
266
267 /*
268 * volfs_select - just say OK. Only possible op is readdir
269 */
270 static int
271 volfs_select(__unused struct vnop_select_args *ap)
272 {
273 return (1);
274 }
275
276 /*
277 * vofls_rmdir - not possible to remove directories in volfs
278 */
279 static int
280 volfs_rmdir(ap)
281 struct vnop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp;
282 struct componentname *a_cnp; vfs_context_t a_context; } */ *ap;
283 {
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
291
292
293 static int
294 volfs_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
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.
325 */
326 static int
327 volfs_readdir(ap)
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;
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;
341 struct volfs_rdstruct vcs;
342
343 off = uio->uio_offset;
344 priv_data = ap->a_vp->v_data;
345 // LP64todo - fix this!
346 starting_resid = count = uio_resid(uio);
347
348 /* Make sure we don't return partial entries. */
349 count -= (uio->uio_offset + count) & (VLFSDIRENTLEN - 1);
350 if (count <= 0) {
351 return (EINVAL);
352 }
353 /*
354 * Make sure we're starting on a directory boundary
355 */
356 if (off & (VLFSDIRENTLEN - 1)) {
357 return (EINVAL);
358 }
359 rec_offset = off / VLFSDIRENTLEN;
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 */
369
370 local_dir.d_reclen = VLFSDIRENTLEN;
371 /*
372 * We must synthesize . and ..
373 */
374
375 if (rec_offset == 0)
376 {
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);
387 rec_offset++;
388 }
389 if (rec_offset == 1)
390 {
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++;
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 */
416 return (error);
417 }
418
419 if (rec_offset > 1) {
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);
427
428 //if (mp == (void *) &mountlist)
429 *ap->a_eofflag = 1; /* we got all the way to the end */
430 }
431 uio_setresid(uio, (uio_resid(uio) + lost));
432
433 if (starting_resid == uio_resid(uio))
434 uio->uio_offset = 0;
435
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 */
445 int
446 validfsnode(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
460 /*
461 * volfs_pathconf - Return POSIX pathconf information applicable to ufs filesystems.
462 */
463 static int
464 volfs_pathconf(ap)
465 struct vnop_pathconf_args /* { struct vnode *a_vp; int a_name; int
466 *a_retval; vfs_context_t a_context; } */ *ap;
467 {
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
494 /*
495 * get_parentvp() - internal routine that tries to lookup the parent of vpp.
496 * On success, *vpp is the parent vp and is returned with a reference.
497 */
498 static int
499 get_parentvp(struct vnode **vpp, struct mount *mp, vfs_context_t context)
500 {
501 int result;
502 struct vnode_attr va;
503 struct vnode *child_vp = *vpp;
504
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
512 /* Shift attention to the parent directory vnode: */
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);
517 }
518
519 return result;
520 }
521
522
523 /*
524 * Look up the parent directory of a given vnode.
525 */
526 static int
527 lookup_parent(vnode_t child_vp, vnode_t *parent_vpp, int is_authorized, vfs_context_t context)
528 {
529 struct componentname cn;
530 vnode_t new_vp;
531 int error;
532
533 *parent_vpp = NULLVP;
534
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;
542
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 }
571
572
573 /*
574 * verify_fullpathaccess(ret_vnode);
575 */
576
577 static int
578 verify_fullpathaccess(struct vnode *targetvp, vfs_context_t context)
579 {
580 struct vnode *vp, *parent_vp;
581 struct mount *mp = targetvp->v_mount;
582 struct proc *p = vfs_context_proc(context);
583 int result;
584 int dp_authorized;
585 struct filedesc *fdp = p->p_fd; /* pointer to file descriptor state */
586
587 vp = targetvp;
588 dp_authorized = 0;
589
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 */
608 }
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 }
628
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) {
635
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;
642 }
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
686 lookup_exit:
687 if (vp != NULLVP && vp != targetvp) {
688 vnode_put(vp);
689 }
690
691 err_exit:
692 return result;
693 };
694
695
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 */
700 static int
701 get_fsvnode(struct mount *our_mount, int id, vnode_t *ret_vnode)
702 {
703 struct mount *cur_mount;
704 fsid_t cur_fsid;
705 struct vnode *cur_vnode;
706 struct volfs_vndata *cur_privdata;
707 int retval;
708 struct vnode_fsparam vfsp;
709 int vid = 0;
710
711 /*
712 * OK, first look up the matching mount on the list of mounted file systems
713 */
714 /* the following will return the mount point with vfs_busy held */
715 cur_mount = mount_lookupby_volfsid(id, 1);
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
729 cur_fsid = cur_mount->mnt_vfsstat.f_fsid;
730
731 /*
732 * Now search the list attached to the mount structure to
733 * see if this vnode is already floating around
734 */
735 search_vnodelist:
736 mount_lock(our_mount);
737 TAILQ_FOREACH(cur_vnode, &our_mount->mnt_vnodelist, v_mntvnodes) {
738 cur_privdata = (struct volfs_vndata *) cur_vnode->v_data;
739 if (cur_privdata->nodeID == (unsigned int)id)
740 {
741 if (cur_privdata->fs_mount != cur_mount) {
742 cur_privdata->fs_mount = cur_mount;
743 cur_privdata->fs_fsid = cur_fsid;
744 };
745 break;
746 }
747 }
748 mount_unlock(our_mount);
749
750 if (cur_vnode) {
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) {
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);
766
767 cur_privdata->vnode_type = VOLFS_FSNODE;
768 cur_privdata->nodeID = id;
769
770 cur_privdata->fs_mount = cur_mount;
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
793 }
794
795 *ret_vnode = cur_vnode;
796 retval = 0;
797 out:
798 vfs_unbusy(cur_mount);
799 return (retval);
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 */
809 static int
810 get_filevnode(struct mount *parent_fs, u_int id, vnode_t *ret_vnode, vfs_context_t context)
811 {
812 int retval;
813
814 again:
815 /*
816 * Special case 2 to mean the root of a file system
817 */
818 if (id == 2)
819 retval = VFS_ROOT(parent_fs, ret_vnode, context);
820 else
821 retval = VFS_VGET(parent_fs, (ino64_t)id, ret_vnode, context);
822 if (retval) goto error;
823
824 retval = verify_fullpathaccess(*ret_vnode, context);
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 */
830 vnode_put(*ret_vnode);
831 *ret_vnode = NULL;
832 /* vnode was recycled during access verification. */
833 if (retval == EAGAIN) {
834 goto again;
835 }
836 };
837
838 error:
839 return (retval);
840 }
841
842
843 static int
844 volfs_lookup(struct vnop_lookup_args *ap)
845 {
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;
855
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
860 priv_data = ap->a_dvp->v_data;
861 nameptr = ap->a_cnp->cn_nameptr;
862 namelen = ap->a_cnp->cn_namelen;
863 firstchar = nameptr[0];
864
865 /* First check for "." and ".." */
866 if (firstchar == '.') {
867 if (namelen == 1) {
868 /* "." requested */
869 isdot_or_dotdot = 1;
870 *ap->a_vpp = ap->a_dvp;
871 vnode_get(*ap->a_vpp);
872 ret_err = 0;
873 } else if (nameptr[1] == '.' && namelen == 2) {
874 /* ".." requested */
875 isdot_or_dotdot = 1;
876 ret_err = VFS_ROOT(ap->a_dvp->v_mount, ap->a_vpp, ap->a_context);
877 }
878 } else if (firstchar == '@') { /* '@' is alias for system root */
879 if ((namelen == 1) && (priv_data->vnode_type != VOLFS_ROOT)) {
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') {
901 char *check_ptr;
902 u_long id;
903
904 id = strtoul(nameptr, &check_ptr, 10);
905
906 /*
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 */
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
947 ret_err = get_fsvnode(ap->a_dvp->v_mount, id, ap->a_vpp);
948
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 }
966 }
967 }
968 vp = *ap->a_vpp;
969
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);
972
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
977 return (ret_err);
978 }
979