* Copyright (c) 2006 Apple Computer, 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
* 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,
* 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 <sys/vnode.h>
#include <sys/sysproto.h>
#include <sys/csr.h>
+#include <miscfs/devfs/devfsdefs.h>
#include <libkern/crypto/sha2.h>
#include <libkern/crypto/rsa.h>
#include <libkern/OSKextLibPrivate.h>
extern struct filedesc filedesc0;
extern int (*mountroot)(void);
-extern char rootdevice[];
+extern char rootdevice[DEVMAXNAMESIZE];
#define DEBUG_IMAGEBOOT 0
#define DBG_TRACE(...) do {} while(0)
#endif
-extern int di_root_image(const char *path, char devname[], dev_t *dev_p);
+extern int di_root_image(const char *path, char *devname, size_t devsz, dev_t *dev_p);
+extern int di_root_ramfile_buf(void *buf, size_t bufsz, char *devname, size_t devsz, dev_t *dev_p);
+
static boolean_t imageboot_setup_new(void);
#define kIBFilePrefix "file://"
__private_extern__ int
imageboot_format_is_valid(const char *root_path)
{
- return (strncmp(root_path, kIBFilePrefix,
- strlen(kIBFilePrefix)) == 0);
+ return strncmp(root_path, kIBFilePrefix,
+ strlen(kIBFilePrefix)) == 0;
}
static void
-vnode_get_and_drop_always(vnode_t vp)
+vnode_get_and_drop_always(vnode_t vp)
{
vnode_getalways(vp);
vnode_rele(vp);
{
int result = 0;
char *root_path = NULL;
-
+
DBG_TRACE("%s: checking for presence of root path\n", __FUNCTION__);
MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
- if (root_path == NULL)
+ if (root_path == NULL) {
panic("%s: M_NAMEI zone exhausted", __FUNCTION__);
+ }
/* Check for first layer */
if (!(PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) ||
- PE_parse_boot_argn("rp", root_path, MAXPATHLEN) ||
- PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) ||
- PE_parse_boot_argn(IMAGEBOOT_AUTHROOT_ARG, root_path, MAXPATHLEN))) {
+ PE_parse_boot_argn("rp", root_path, MAXPATHLEN) ||
+ PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) ||
+ PE_parse_boot_argn(IMAGEBOOT_AUTHROOT_ARG, root_path, MAXPATHLEN))) {
goto out;
}
/* Check for second layer */
if (!(PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) ||
- PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN))) {
+ PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN))) {
goto out;
}
DBG_TRACE("%s: Found %s\n", __FUNCTION__, root_path);
} else {
panic("%s: Invalid URL scheme for %s\n",
- __FUNCTION__, root_path);
+ __FUNCTION__, root_path);
}
out:
FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
- return (result);
+ return result;
}
/*
* Swaps in new root filesystem based on image path.
* Current root filesystem is removed from mount list and
- * tagged MNTK_BACKS_ROOT, MNT_ROOTFS is cleared on it, and
- * "rootvnode" is reset. Root vnode of currentroot filesystem
+ * tagged MNTK_BACKS_ROOT, MNT_ROOTFS is cleared on it, and
+ * "rootvnode" is reset. Root vnode of currentroot filesystem
* is returned with usecount (no iocount).
*/
__private_extern__ int
imageboot_mount_image(const char *root_path, int height)
{
- dev_t dev;
- int error;
- vnode_t old_rootvnode = NULL;
- vnode_t newdp;
- mount_t new_rootfs;
+ dev_t dev;
+ int error;
+ vnode_t old_rootvnode = NULL;
+ vnode_t newdp;
+ mount_t new_rootfs;
- error = di_root_image(root_path, rootdevice, &dev);
+ error = di_root_image(root_path, rootdevice, DEVMAXNAMESIZE, &dev);
if (error) {
panic("%s: di_root_image failed: %d\n", __FUNCTION__, error);
}
* Get the vnode for '/'.
* Set fdp->fd_fd.fd_cdir to reference it.
*/
- if (VFS_ROOT(TAILQ_LAST(&mountlist,mntlist), &newdp, vfs_context_kernel()))
+ if (VFS_ROOT(TAILQ_LAST(&mountlist, mntlist), &newdp, vfs_context_kernel())) {
panic("%s: cannot find root vnode", __FUNCTION__);
+ }
if (rootvnode != NULL) {
/* remember the old rootvnode, but remove it from mountlist */
- mount_t old_rootfs;
+ mount_t old_rootfs;
old_rootvnode = rootvnode;
old_rootfs = rootvnode->v_mount;
-
+
mount_list_remove(old_rootfs);
-
+
mount_lock(old_rootfs);
#ifdef CONFIG_IMGSRC_ACCESS
old_rootfs->mnt_kern_flag |= MNTK_BACKS_ROOT;
if (old_rootvnode != NULL) {
#ifdef CONFIG_IMGSRC_ACCESS
- if (height >= 0 && PE_imgsrc_mount_supported()) {
- imgsrc_rootvnodes[height] = old_rootvnode;
- } else {
+ if (height >= 0 && PE_imgsrc_mount_supported()) {
+ imgsrc_rootvnodes[height] = old_rootvnode;
+ } else {
+ vnode_get_and_drop_always(old_rootvnode);
+ }
+#else
+ height = 0; /* keep the compiler from complaining */
vnode_get_and_drop_always(old_rootvnode);
- }
-#else
- height = 0; /* keep the compiler from complaining */
- vnode_get_and_drop_always(old_rootvnode);
#endif /* CONFIG_IMGSRC_ACCESS */
}
return 0;
len = len / sizeof(uint32_t);
for (size_t i = 0; i < len; i++) {
- dst[len-i-1] = OSSwapInt32(src[i]);
+ dst[len - i - 1] = OSSwapInt32(src[i]);
}
}
proc_t p = vfs_context_proc(ctx);
kauth_cred_t kerncred = vfs_context_ucred(ctx);
- NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, path, ctx);
+ NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
if ((err = namei(&ndp)) != 0) {
AUTHPRNT("namei failed (%s)", path);
goto out;
key_byteswap(sig, sig_msb, siglen);
err = rsa_make_pub(rsa_ctx,
- sizeof(exponent), exponent,
- CHUNKLIST_PUBKEY_LEN, modulus);
+ sizeof(exponent), exponent,
+ CHUNKLIST_PUBKEY_LEN, modulus);
if (err) {
AUTHPRNT("rsa_make_pub() failed");
goto out;
}
err = rsa_verify_pkcs1v15(rsa_ctx, CC_DIGEST_OID_SHA256,
- SHA256_DIGEST_LENGTH, digest,
- siglen, sig,
- &sig_valid);
+ SHA256_DIGEST_LENGTH, digest,
+ siglen, sig,
+ &sig_valid);
if (err) {
sig_valid = false;
AUTHPRNT("rsa_verify() failed");
/* recognized file format? */
if (hdr->cl_magic != CHUNKLIST_MAGIC ||
- hdr->cl_file_ver != CHUNKLIST_FILE_VERSION_10 ||
- hdr->cl_chunk_method != CHUNKLIST_SIGNATURE_METHOD_10 ||
- hdr->cl_sig_method != CHUNKLIST_SIGNATURE_METHOD_10) {
+ hdr->cl_file_ver != CHUNKLIST_FILE_VERSION_10 ||
+ hdr->cl_chunk_method != CHUNKLIST_SIGNATURE_METHOD_10 ||
+ hdr->cl_sig_method != CHUNKLIST_SIGNATURE_METHOD_10) {
AUTHPRNT("unrecognized chunklist format");
return EINVAL;
}
/* does the chunk list fall within the bounds of the buffer? */
if (os_mul_and_add_overflow(hdr->cl_chunk_count, sizeof(struct chunklist_chunk), hdr->cl_chunk_offset, &chunks_end) ||
- hdr->cl_chunk_offset < sizeof(struct chunklist_hdr) || chunks_end > len) {
+ hdr->cl_chunk_offset < sizeof(struct chunklist_hdr) || chunks_end > len) {
AUTHPRNT("invalid chunk_count (%llu) or chunk_offset (%llu)",
- hdr->cl_chunk_count, hdr->cl_chunk_offset);
+ hdr->cl_chunk_count, hdr->cl_chunk_offset);
return EINVAL;
}
/* does the signature fall within the bounds of the buffer? */
if (os_add_overflow(hdr->cl_sig_offset, sizeof(struct chunklist_sig), &sig_end) ||
- hdr->cl_sig_offset < sizeof(struct chunklist_hdr) ||
- hdr->cl_sig_offset < chunks_end ||
- hdr->cl_sig_offset > len) {
+ hdr->cl_sig_offset < sizeof(struct chunklist_hdr) ||
+ hdr->cl_sig_offset < chunks_end ||
+ hdr->cl_sig_offset > len) {
AUTHPRNT("invalid signature offset (%llu)", hdr->cl_sig_offset);
return EINVAL;
}
for (size_t i = 0; i < CHUNKLIST_NPUBKEYS; i++) {
const struct chunklist_pubkey *key = &chunklist_pubkeys[i];
err = validate_signature(key->key, CHUNKLIST_PUBKEY_LEN,
- buf + hdr->cl_sig_offset, sigsz, sha_digest);
+ buf + hdr->cl_sig_offset, sigsz, sha_digest);
if (err == 0) {
AUTHDBG("validated chunklist signature with key %lu (prod=%d)", i, key->isprod);
valid_sig = key->isprod;
/*
* Open the DMG
*/
- NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, root_path, ctx);
+ NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(root_path), ctx);
if ((err = namei(&ndp)) != 0) {
AUTHPRNT("namei failed (%s)", root_path);
goto out;
{
char *chunklist_path = NULL;
void *chunklist_buf = NULL;
- size_t chunklist_len = 32*1024*1024UL;
+ size_t chunklist_len = 32 * 1024 * 1024UL;
int err = 0;
err = construct_chunklist_path(root_path, &chunklist_path);
/* everything checked out - go ahead and mount this */
AUTHDBG("root image authenticated");
- out:
+out:
kfree_safe(chunklist_buf);
kfree_safe(chunklist_path);
return err;
}
if (os_add_overflow(cmd->cmdsize, offset, &offset) ||
- offset > bufsz - sizeof(struct uuid_command)) {
+ offset > bufsz - sizeof(struct uuid_command)) {
return NULL;
}
}
{
int err = 0;
void *buf = NULL;
- size_t bufsz = 4*1024*1024UL;
+ size_t bufsz = 4 * 1024 * 1024UL;
/* get the UUID of the libkern in /S/L/E */
}
#endif
+/*
+ * Attach the image at 'path' as a ramdisk and mount it as our new rootfs.
+ * All existing mounts are first umounted.
+ */
+static int
+imageboot_mount_ramdisk(const char *path)
+{
+ int err = 0;
+ size_t bufsz = 0;
+ void *buf = NULL;
+ dev_t dev;
+ vnode_t newdp;
+ mount_t new_rootfs;
+
+ /* Read our target image from disk */
+ err = read_file(path, &buf, &bufsz);
+ if (err) {
+ printf("%s: failed: read_file() = %d\n", __func__, err);
+ goto out;
+ }
+ DBG_TRACE("%s: read '%s' sz = %lu\n", __func__, path, bufsz);
+
+#if CONFIG_IMGSRC_ACCESS
+ /* Re-add all root mounts to the mount list in the correct order... */
+ mount_list_remove(rootvnode->v_mount);
+ for (int i = 0; i < MAX_IMAGEBOOT_NESTING; i++) {
+ struct vnode *vn = imgsrc_rootvnodes[i];
+ if (vn) {
+ vnode_getalways(vn);
+ imgsrc_rootvnodes[i] = NULLVP;
+
+ mount_t mnt = vn->v_mount;
+ mount_lock(mnt);
+ mnt->mnt_flag |= MNT_ROOTFS;
+ mount_list_add(mnt);
+ mount_unlock(mnt);
+
+ vnode_rele(vn);
+ vnode_put(vn);
+ }
+ }
+ mount_list_add(rootvnode->v_mount);
+#endif
+
+ /* ... and unmount everything */
+ vnode_get_and_drop_always(rootvnode);
+ filedesc0.fd_cdir = NULL;
+ rootvnode = NULL;
+ vfs_unmountall();
+
+ /* Attach the ramfs image ... */
+ err = di_root_ramfile_buf(buf, bufsz, rootdevice, DEVMAXNAMESIZE, &dev);
+ if (err) {
+ printf("%s: failed: di_root_ramfile_buf() = %d\n", __func__, err);
+ goto out;
+ }
+
+ /* ... and mount it */
+ rootdev = dev;
+ mountroot = NULL;
+ err = vfs_mountroot();
+ if (err) {
+ printf("%s: failed: vfs_mountroot() = %d\n", __func__, err);
+ goto out;
+ }
+
+ /* Switch to new root vnode */
+ if (VFS_ROOT(TAILQ_LAST(&mountlist, mntlist), &newdp, vfs_context_kernel())) {
+ panic("%s: cannot find root vnode", __func__);
+ }
+ rootvnode = newdp;
+ rootvnode->v_flag |= VROOT;
+ new_rootfs = rootvnode->v_mount;
+ mount_lock(new_rootfs);
+ new_rootfs->mnt_flag |= MNT_ROOTFS;
+ mount_unlock(new_rootfs);
+
+ vnode_ref(newdp);
+ vnode_put(newdp);
+ filedesc0.fd_cdir = newdp;
+
+ DBG_TRACE("%s: root switched\n", __func__);
+
+out:
+ if (err) {
+ kfree_safe(buf);
+ }
+ return err;
+}
+
static boolean_t
imageboot_setup_new()
{
int height = 0;
boolean_t done = FALSE;
boolean_t auth_root = FALSE;
+ boolean_t ramdisk_root = FALSE;
MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
assert(root_path != NULL);
+ unsigned imgboot_arg;
+ if (PE_parse_boot_argn("-rootdmg-ramdisk", &imgboot_arg, sizeof(imgboot_arg))) {
+ ramdisk_root = TRUE;
+ }
+
if (PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN) == TRUE) {
printf("%s: container image url is %s\n", __FUNCTION__, root_path);
error = imageboot_mount_image(root_path, height);
}
#endif
- if (auth_root) {
- /* Copy the path to use locally */
- char *path_alloc = kalloc(MAXPATHLEN);
- if (path_alloc == NULL) {
- panic("imageboot path allocation failed\n");
- }
-
- char *path = path_alloc;
- strlcpy(path, root_path, MAXPATHLEN);
+ /* Make a copy of the path to URL-decode */
+ char *path_alloc = kalloc(MAXPATHLEN);
+ if (path_alloc == NULL) {
+ panic("imageboot path allocation failed\n");
+ }
+ char *path = path_alloc;
- size_t len = strlen(kIBFilePrefix);
- if (strncmp(kIBFilePrefix, path, len) == 0) {
- /* its a URL - remove the file:// prefix and percent-decode */
- path += len;
- url_decode(path);
- }
+ size_t len = strlen(kIBFilePrefix);
+ strlcpy(path, root_path, MAXPATHLEN);
+ if (strncmp(kIBFilePrefix, path, len) == 0) {
+ /* its a URL - remove the file:// prefix and percent-decode */
+ path += len;
+ url_decode(path);
+ }
+ if (auth_root) {
AUTHDBG("authenticating root image at %s", path);
error = authenticate_root(path);
if (error) {
panic("root image authentication failed (err = %d)\n", error);
}
AUTHDBG("successfully authenticated %s", path);
+ }
- kfree_safe(path_alloc);
+ if (ramdisk_root) {
+ error = imageboot_mount_ramdisk(path);
+ } else {
+ error = imageboot_mount_image(root_path, height);
}
- error = imageboot_mount_image(root_path, height);
- if (error != 0) {
- panic("Failed to mount root image.");
+ kfree_safe(path_alloc);
+
+ if (error) {
+ panic("Failed to mount root image (err=%d, auth=%d, ramdisk=%d)\n",
+ error, auth_root, ramdisk_root);
}
if (auth_root) {
/*
* New boot-arg scheme:
- * root-dmg : the dmg that will be the root filesystem.
- * auth-root-dmg : same as root-dmg but with image authentication.
- * container-dmg : an optional dmg that contains the root-dmg.
+ * root-dmg : the dmg that will be the root filesystem.
+ * auth-root-dmg : same as root-dmg but with image authentication.
+ * container-dmg : an optional dmg that contains the root-dmg.
*/
if (imageboot_setup_new()) {
return;
* Look for outermost disk image to root from. If we're doing a nested boot,
* there's some sense in which the outer image never needs to be the root filesystem,
* but it does need very similar treatment: it must not be unmounted, needs a fake
- * device vnode created for it, and should not show up in getfsstat() until exposed
+ * device vnode created for it, and should not show up in getfsstat() until exposed
* with MNT_IMGSRC. We just make it the temporary root.
*/
- if((PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == FALSE) &&
- (PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) == FALSE)) {
+ if ((PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == FALSE) &&
+ (PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) == FALSE)) {
panic("%s: no valid path to image.\n", __FUNCTION__);
}
printf("%s: root image url is %s\n", __FUNCTION__, root_path);
-
+
error = imageboot_mount_image(root_path, 0);
if (error) {
panic("Failed on first stage of imageboot.");
/*
* See if we are rooting from a nested image
*/
- if(PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) == FALSE) {
+ if (PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) == FALSE) {
goto done;
}
-
+
printf("%s: second level root image url is %s\n", __FUNCTION__, root_path);
/*
* If we fail to set up second image, it's not a given that we
- * can safely root off the first.
+ * can safely root off the first.
*/
error = imageboot_mount_image(root_path, 1);
if (error) {
- panic("Failed on second stage of imageboot.");
+ panic("Failed on second stage of imageboot.");
}
done: