-/*
- * This is the 10-Apr-92 bypass routine.
- * This version has been optimized for speed, throwing away some
- * safety checks. It should still always work, but it's not as
- * robust to programmer errors.
- * Define SAFETY to include some error checking code.
- *
- * In general, we map all vnodes going down and unmap them on the way back.
- * As an exception to this, vnodes can be marked "unmapped" by setting
- * the Nth bit in operation's vdesc_flags.
- *
- * Also, some BSD vnode operations have the side effect of node_put'ing
- * their arguments. With stacking, the reference counts are held
- * by the upper node, not the lower one, so we must handle these
- * side-effects here. This is not of concern in Sun-derived systems
- * since there are no such side-effects.
- *
- * This makes the following assumptions:
- * - only one returned vpp
- * - no INOUT vpp's (Sun's vnop_open has one of these)
- * - the vnode operation vector of the first vnode should be used
- * to determine what implementation of the op should be invoked
- * - all mapped vnodes are of our vnode-type (NEEDSWORK:
- * problems on rmdir'ing mount points and renaming?)
- */
-int
-null_bypass(ap)
- struct vnop_generic_args /* {
- struct vnodeop_desc *a_desc;
- <other random data follows, presumably>
- } */ *ap;
+vop_t * nullfs_vnodeop_p = NULL;
+
+/* the mountpoint lock should be held going into this function */
+static int
+nullfs_isspecialvp(struct vnode * vp)
+{
+ struct null_mount * null_mp;
+
+ null_mp = MOUNTTONULLMOUNT(vnode_mount(vp));
+
+ /* only check for root and second here, third is special in a different way,
+ * related only to lookup and readdir */
+ if (vp && (vp == null_mp->nullm_rootvp || vp == null_mp->nullm_secondvp)) {
+ return 1;
+ }
+ return 0;
+}
+
+/* helper function to handle locking where possible */
+static int
+nullfs_checkspecialvp(struct vnode* vp)
+{
+ int result = 0;
+ struct null_mount * null_mp;
+
+ null_mp = MOUNTTONULLMOUNT(vnode_mount(vp));
+
+ lck_mtx_lock(&null_mp->nullm_lock);
+ result = (nullfs_isspecialvp(vp));
+ lck_mtx_unlock(&null_mp->nullm_lock);
+
+ return result;
+}
+
+vfs_context_t
+nullfs_get_patched_context(struct null_mount * null_mp, vfs_context_t ctx)
+{
+ struct vfs_context* ectx = ctx;
+ if ((null_mp->nullm_flags & NULLM_UNVEIL) == NULLM_UNVEIL) {
+ ectx = vfs_context_create(ctx);
+ ectx->vc_ucred = kauth_cred_setuidgid(ectx->vc_ucred, null_mp->uid, null_mp->gid);
+ }
+ return ectx;
+}
+
+void
+nullfs_cleanup_patched_context(struct null_mount * null_mp, vfs_context_t ctx)
+{
+ if ((null_mp->nullm_flags & NULLM_UNVEIL) == NULLM_UNVEIL) {
+ vfs_context_rele(ctx);
+ }
+}
+
+static int
+nullfs_default(__unused struct vnop_generic_args * args)
+{
+ NULLFSDEBUG("%s (default)\n", ((struct vnodeop_desc_fake *)args->a_desc)->vdesc_name);
+ return ENOTSUP;
+}
+
+static int
+nullfs_special_getattr(struct vnop_getattr_args * args)
+{
+ mount_t mp = vnode_mount(args->a_vp);
+ struct null_mount * null_mp = MOUNTTONULLMOUNT(mp);
+
+ ino_t ino = NULL_ROOT_INO;
+ struct vnode_attr covered_rootattr;
+ vnode_t checkvp = null_mp->nullm_lowerrootvp;
+ vfs_context_t ectx = nullfs_get_patched_context(null_mp, args->a_context);
+
+ VATTR_INIT(&covered_rootattr);
+ VATTR_WANTED(&covered_rootattr, va_uid);
+ VATTR_WANTED(&covered_rootattr, va_gid);
+ VATTR_WANTED(&covered_rootattr, va_create_time);
+ VATTR_WANTED(&covered_rootattr, va_modify_time);
+ VATTR_WANTED(&covered_rootattr, va_access_time);
+
+ /* prefer to get this from the lower root vp, but if not (i.e. forced unmount
+ * of lower fs) try the mount point covered vnode */
+ if (vnode_getwithvid(checkvp, null_mp->nullm_lowerrootvid)) {
+ checkvp = vfs_vnodecovered(mp);
+ if (checkvp == NULL) {
+ nullfs_cleanup_patched_context(null_mp, ectx);
+ return EIO;
+ }
+ }
+
+ int error = vnode_getattr(checkvp, &covered_rootattr, ectx);
+
+ vnode_put(checkvp);
+ if (error) {
+ /* we should have been able to get attributes fore one of the two choices so
+ * fail if we didn't */
+ nullfs_cleanup_patched_context(null_mp, ectx);
+ return error;
+ }
+
+ /* we got the attributes of the vnode we cover so plow ahead */
+ if (args->a_vp == null_mp->nullm_secondvp) {
+ ino = NULL_SECOND_INO;
+ }
+
+ VATTR_RETURN(args->a_vap, va_type, vnode_vtype(args->a_vp));
+ VATTR_RETURN(args->a_vap, va_rdev, 0);
+ VATTR_RETURN(args->a_vap, va_nlink, 3); /* always just ., .., and the child */
+ VATTR_RETURN(args->a_vap, va_total_size, 0); // hoping this is ok
+
+ VATTR_RETURN(args->a_vap, va_data_size, 0); // hoping this is ok
+ VATTR_RETURN(args->a_vap, va_data_alloc, 0);
+ VATTR_RETURN(args->a_vap, va_iosize, vfs_statfs(mp)->f_iosize);
+ VATTR_RETURN(args->a_vap, va_fileid, ino);
+ VATTR_RETURN(args->a_vap, va_linkid, ino);
+ if (VATTR_IS_ACTIVE(args->a_vap, va_fsid)) {
+ VATTR_RETURN(args->a_vap, va_fsid, vfs_statfs(mp)->f_fsid.val[0]); // return the fsid of the mount point
+ }
+ if (VATTR_IS_ACTIVE(args->a_vap, va_fsid64)) {
+ VATTR_RETURN(args->a_vap, va_fsid64, vfs_statfs(mp)->f_fsid);
+ }
+ VATTR_RETURN(args->a_vap, va_filerev, 0);
+ VATTR_RETURN(args->a_vap, va_gen, 0);
+ VATTR_RETURN(args->a_vap, va_flags, UF_HIDDEN); /* mark our fake directories as hidden. People
+ * shouldn't be enocouraged to poke around in them */
+
+ if (ino == NULL_SECOND_INO) {
+ VATTR_RETURN(args->a_vap, va_parentid, NULL_ROOT_INO); /* no parent at the root, so
+ * the only other vnode that
+ * goes through this path is
+ * second and its parent is
+ * 1.*/
+ }
+
+ if (VATTR_IS_ACTIVE(args->a_vap, va_mode)) {
+ /* force dr_xr_xr_x */
+ VATTR_RETURN(args->a_vap, va_mode, S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+ }
+ if (VATTR_IS_ACTIVE(args->a_vap, va_uid)) {
+ VATTR_RETURN(args->a_vap, va_uid, covered_rootattr.va_uid);
+ }
+ if (VATTR_IS_ACTIVE(args->a_vap, va_gid)) {
+ VATTR_RETURN(args->a_vap, va_gid, covered_rootattr.va_gid);
+ }
+
+ if (VATTR_IS_ACTIVE(args->a_vap, va_create_time)) {
+ VATTR_SET_SUPPORTED(args->a_vap, va_create_time);
+ args->a_vap->va_create_time.tv_sec = covered_rootattr.va_create_time.tv_sec;
+ args->a_vap->va_create_time.tv_nsec = covered_rootattr.va_create_time.tv_nsec;
+ }
+ if (VATTR_IS_ACTIVE(args->a_vap, va_modify_time)) {
+ VATTR_SET_SUPPORTED(args->a_vap, va_modify_time);
+ args->a_vap->va_modify_time.tv_sec = covered_rootattr.va_modify_time.tv_sec;
+ args->a_vap->va_modify_time.tv_nsec = covered_rootattr.va_modify_time.tv_nsec;
+ }
+ if (VATTR_IS_ACTIVE(args->a_vap, va_access_time)) {
+ VATTR_SET_SUPPORTED(args->a_vap, va_access_time);
+ args->a_vap->va_modify_time.tv_sec = covered_rootattr.va_access_time.tv_sec;
+ args->a_vap->va_modify_time.tv_nsec = covered_rootattr.va_access_time.tv_nsec;
+ }
+
+ nullfs_cleanup_patched_context(null_mp, ectx);
+ return 0;
+}
+
+static int
+nullfs_getattr(struct vnop_getattr_args * args)