gems-kernel/source/THIRDPARTY/xnu/bsd/miscfs/mockfs/mockfs_vfsops.c

240 lines
7.3 KiB
C
Raw Normal View History

2024-06-03 16:29:39 +00:00
/*
* Copyright (c) 2012 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
#include <kern/assert.h>
#include <kern/debug.h>
#include <libkern/libkern.h>
#include <miscfs/mockfs/mockfs.h>
#include <miscfs/mockfs/mockfs_fsnode.h>
#include <miscfs/mockfs/mockfs_vnops.h>
#include <miscfs/specfs/specdev.h>
#include <sys/disk.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/mount_internal.h>
#include <sys/vnode_internal.h>
static LCK_GRP_DECLARE(mockfs_mtx_grp, "mockfs-mutex");
int mockfs_mountroot(mount_t mp, vnode_t rvp, __unused vfs_context_t ctx);
/*
* Functions that are part of the mockfs_vfsops structure.
*/
int mockfs_unmount(__unused struct mount *mp, __unused int mntflags, __unused vfs_context_t ctx);
int mockfs_root(mount_t mp, vnode_t * vpp, __unused vfs_context_t ctx);
int mockfs_sync(__unused struct mount *mp, __unused int waitfor, __unused vfs_context_t ctx);
int mockfs_init(__unused struct vfsconf * vfsc);
/*
* mockfs_mountroot:
* Given a mount (mp) and a vnode for the root device (rvp), builds a fake filesystem for rvp. This consists
* of three nodes; a directory node (to serve as a mountpoint for devfs), a file node meant to serve as an
* executable frontend for rootvp (we will assume that rootvp is an executable, that the kernel can subsequently
* run), and the root node for the mockfs filesystem. The structure of mockfs is memory-backed; only the
* contents of the file node refer to the backing device.
*
* Returns 0 on success, or an error.
*/
int
mockfs_mountroot(mount_t mp, vnode_t rvp, __unused vfs_context_t ctx)
{
int rvalue = 0;
mockfs_fsnode_t root_fsnode = NULL;
mockfs_fsnode_t dev_fsnode = NULL;
mockfs_fsnode_t file_fsnode = NULL;
mockfs_mount_t mockfs_mount_data = NULL;
dk_memdev_info_t memdev_info;
/*
* TODO: Validate that the device at least LOOKS like a mach-o (has a sane header); this would prevent us
* from causing EBADMACHO panics further along the boot path.
*/
/*
* There are no M_MOCKFS* definitions at the moment, just use M_TEMP.
*/
mockfs_mount_data = kalloc_type(struct mockfs_mount, Z_WAITOK | Z_ZERO);
mockfs_fsnode_create(mp, MOCKFS_ROOT, &root_fsnode);
mockfs_fsnode_create(mp, MOCKFS_DEV, &dev_fsnode);
mockfs_fsnode_create(mp, MOCKFS_FILE, &file_fsnode);
if (!root_fsnode || !dev_fsnode || !file_fsnode) {
rvalue = ENOMEM;
goto done;
}
/*
* If rvp is a memory device (with a few caveats), we can point to the same physical memory as the device
* and avoid pointless paging/copying; query the device node for the information we need to determine
* if we can do this.
*/
bzero(&memdev_info, sizeof(memdev_info));
if (!VNOP_IOCTL(rvp, DKIOCGETMEMDEVINFO, (caddr_t)&memdev_info, 0, NULL)) {
/*
* For the moment, we won't try to optimize when mi_phys is true.
*/
if (!mockfs_mount_data->mockfs_physical_memory) {
mockfs_mount_data->mockfs_memory_backed = memdev_info.mi_mdev;
mockfs_mount_data->mockfs_physical_memory = memdev_info.mi_phys;
mockfs_mount_data->mockfs_memdev_base = memdev_info.mi_base;
mockfs_mount_data->mockfs_memdev_size = memdev_info.mi_size;
}
}
lck_mtx_init(&mockfs_mount_data->mockfs_mnt_mtx, &mockfs_mtx_grp, LCK_ATTR_NULL);
/*
* All of the needed nodes/structures have been set up; now we just need to establish the relationships
* between the various mockfs nodes.
*/
if ((rvalue = mockfs_fsnode_adopt(root_fsnode, dev_fsnode))) {
goto done;
}
if ((rvalue = mockfs_fsnode_adopt(root_fsnode, file_fsnode))) {
goto done;
}
mockfs_mount_data->mockfs_root = root_fsnode;
mp->mnt_data = (typeof(mp->mnt_data))mockfs_mount_data;
done:
if (rvalue) {
if (file_fsnode) {
mockfs_fsnode_destroy(file_fsnode);
}
if (dev_fsnode) {
mockfs_fsnode_destroy(dev_fsnode);
}
if (root_fsnode) {
mockfs_fsnode_destroy(root_fsnode);
}
if (mockfs_mount_data) {
lck_mtx_destroy(&mockfs_mount_data->mockfs_mnt_mtx, &mockfs_mtx_grp);
kfree_type(struct mockfs_mount, mockfs_mount_data);
}
}
return rvalue;
}
/*
* mockfs_unmount:
* Given a mount (mp), and associated flags (mntflags), performs the necessary teardown to destroy the mount.
*
* Returns 0 on success, or an error.
*/
int
mockfs_unmount(struct mount *mp, int mntflags, __unused vfs_context_t ctx)
{
int rvalue;
int vflush_flags;
mockfs_fsnode_t root_fsnode;
mockfs_mount_t mockfs_mnt;
vflush_flags = 0;
mockfs_mnt = (mockfs_mount_t) mp->mnt_data;
/*
* Reclaim the vnodes for the mount (forcibly, if requested; given that mockfs only support mountroot
* at the moment, this should ALWAYS be forced),
*/
if (mntflags & MNT_FORCE) {
vflush_flags |= FORCECLOSE;
}
rvalue = vflush(mp, NULL, vflush_flags);
if (rvalue) {
return rvalue;
}
/*
* Past this point, errors are likely to be unrecoverable, so panic if we're given any excuse; we
* need to teardown the mockfs_mnt data now, so that VFS can cleanup the mount structure. Note
* that clearing mockfs_root before destroying the fsnode tree is related to an implementation
* detail of mockfs_fsnode_destroy (which will refuse to destroy the root node).
*/
root_fsnode = mockfs_mnt->mockfs_root;
mockfs_mnt->mockfs_root = NULL;
rvalue = mockfs_fsnode_destroy(root_fsnode);
if (rvalue) {
panic("mockfs_unmount: Failed to destroy the fsnode tree");
}
lck_mtx_destroy(&mockfs_mnt->mockfs_mnt_mtx, &mockfs_mtx_grp);
kfree_type(struct mockfs_mount, mockfs_mnt);
mp->mnt_data = NULL;
return rvalue;
}
/*
* mockfs_root:
* Given a mount (mp), returns the root vnode (*vpp) for that mount with an iocount.
*
* Returns 0 on success, or an error.
*/
int
mockfs_root(mount_t mp, vnode_t * vpp, __unused vfs_context_t ctx)
{
int rvalue;
rvalue = mockfs_fsnode_vnode(((mockfs_mount_t) mp->mnt_data)->mockfs_root, vpp);
return rvalue;
}
/*
* mockfs_sync:
* Returns success because we're a read-only filesystem.
*
* Returns 0.
*/
int
mockfs_sync(__unused struct mount *mp, __unused int waitfor, __unused vfs_context_t ctx)
{
return 0;
}
int
mockfs_init(__unused struct vfsconf * vfsc)
{
return 0;
}
struct vfsops mockfs_vfsops = {
.vfs_unmount = mockfs_unmount,
.vfs_root = mockfs_root,
.vfs_sync = mockfs_sync,
.vfs_init = mockfs_init,
};