aboutsummaryrefslogtreecommitdiffstats
path: root/diskdev_cmds/mount.tproj/mount.c
diff options
context:
space:
mode:
Diffstat (limited to 'diskdev_cmds/mount.tproj/mount.c')
-rw-r--r--diskdev_cmds/mount.tproj/mount.c1696
1 files changed, 1696 insertions, 0 deletions
diff --git a/diskdev_cmds/mount.tproj/mount.c b/diskdev_cmds/mount.tproj/mount.c
new file mode 100644
index 0000000..ace7cbe
--- /dev/null
+++ b/diskdev_cmds/mount.tproj/mount.c
@@ -0,0 +1,1696 @@
+/*
+ * Copyright (c) 1999-2020 Apple Inc. All rights reserved.
+ *
+ * @APPLE_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. 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_LICENSE_HEADER_END@
+ */
+/*-
+ * Copyright (c) 1980, 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <System/sys/reason.h>
+#include <err.h>
+#include <os/errno.h>
+#include <os/bsd.h>
+#include <os/variant_private.h>
+#include <fstab.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <TargetConditionals.h>
+#include <sysexits.h>
+#include <sys/sysctl.h>
+#include <APFS/APFS.h>
+#include <APFS/APFSConstants.h>
+#include <pthread.h>
+#include <spawn.h>
+#include <crt_externs.h>
+
+#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
+#include <paths.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/cdefs.h>
+
+/* Some APFS specific goop */
+#include <copyfile.h>
+#include <MediaKit/MKMedia.h>
+#include <MediaKit/MKMediaAccess.h>
+#include <MediaKit/GPTTypes.h>
+#endif
+
+#if TARGET_OS_OSX
+// To unmount the BaseSystem disk image.
+#include <paths.h>
+#include <IOKit/IOBSD.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/storage/IOMedia.h>
+#endif
+
+#include "mount_flags.h"
+#include "edt_fstab.h"
+#include "pathnames.h"
+#include "fsck.h"
+
+#if TARGET_OS_OSX
+#define APFS_BOOT_UTIL_PATH "/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs_boot_util"
+#define PLATFORM_DATA_VOLUME_MOUNT_POINT "/System/Volumes/Data"
+#define BASE_SYSTEM_PATH "/System/Volumes/BaseSystem"
+#define RECOVERY_PATH "/System/Volumes/Recovery"
+#else
+#define APFS_BOOT_UTIL_PATH "/System/Library/Filesystems/apfs.fs/apfs_boot_util"
+#define PLATFORM_DATA_VOLUME_MOUNT_POINT "/private/var"
+#endif
+
+#define environ (*_NSGetEnviron())
+#define COMMAND_OUTPUT_MAX 1024
+
+int debug;
+int verbose;
+int bootstrap_macos = 0;
+int passno = 0;
+
+int checkvfsname __P((const char *, const char **));
+char *catopt __P((char *, const char *));
+struct statfs *getmntpt __P((const char *));
+int hasopt __P((const char *, const char *));
+const char
+ **makevfslist __P((char *));
+void mangle __P((char *, int *, const char **));
+void prmount __P((struct statfs *));
+void usage __P((void));
+
+int run_command(char **command_argv, char *output, int *rc, int *signal_no);
+void print_mount(const char **vfslist);
+int ismounted(const char *fs_spec, const char *fs_file, long *flags);
+int mountfs(const char *vfstype, const char *fs_spec, const char *fs_file, int flags, const char *options, const char *mntopts);
+int unmount_location(char *mount_point);
+
+#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
+
+char* parse_parameter_for_token(char * opts, char * search_string);
+int verify_executable_file_existence (char *path);
+int verify_file_existence (char *path);
+int _verify_file_flags (char *path, int flags);
+int preflight_create_mount_ramdisk (char *mnt_opts, size_t *ramdisk_size, char *template);
+char* split_ramdisk_params(char *opts);
+int create_mount_ramdisk(struct fstab *fs, int init_flags, char *options);
+int construct_apfs_volume(char *mounted_device_name);
+int create_partition_table(size_t partition_size, char *device);
+int attach_device(size_t device_size , char* deviceOut);
+void truncate_whitespace(char* str);
+
+#define RAMDISK_BLCK_OFFSET 34
+#define RAMDISK_TMP_MOUNT "/mnt2"
+#define RAMDISK_BCK_MOUNT "/.mb"
+#define RAMDISK_SIZE_TOK "size="
+#define RAMDISK_TPLT_TOK "template="
+#define HDIK_PATH "/usr/sbin/hdik"
+
+#endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */
+
+//pull in the optnames array from the mount_flags.c file
+extern mountopt_t optnames[];
+#if TARGET_OS_OSX
+static int booted_rosv(void);
+static int booted_apfs(void);
+#endif /* TARGET_OS_OSX */
+static int upgrade_mount(const char *mountpt, int init_flags, char *options);
+
+/*
+ * Map a POSIX error code to a representative sysexits(3) code. Can be disabled
+ * to exit with errno error code by passing -e as a command line argument to mount
+ */
+static int ret_errno = 0;
+static inline int
+errno_or_sysexit(int err, int sysexit)
+{
+ if (sysexit == -1) {
+ sysexit = sysexit_np(err);
+ }
+ return (ret_errno ? err : sysexit);
+}
+
+#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
+#define BINDFS_MOUNT_TYPE "bindfs"
+#define PREBOOT_VOL_MOUNTPOINT "/private/preboot"
+#define HARDWARE_VOL_MOUNTPOINT "/private/var/hardware"
+
+#define BOOT_MANIFEST_HASH_LEN 256
+
+typedef struct bind_mount {
+ char *bm_mnt_prefix;
+ char *bm_mnt_to;
+ bool bm_mandatory;
+} bind_mount_t;
+
+static void
+do_bindfs_mount(char *from, const char *to, bool required)
+{
+ struct statfs sfs;
+ uint32_t mnt_flags = MNT_RDONLY | MNT_NODEV | MNT_NOSUID | MNT_DONTBROWSE;
+ int err = 0;
+
+ if (debug) {
+ printf("call: mount %s %s %x %s", BINDFS_MOUNT_TYPE, to, mnt_flags, from);
+ return;
+ }
+
+ if ((statfs(from, &sfs) == 0) &&
+ (strncmp(sfs.f_fstypename, BINDFS_MOUNT_TYPE, sizeof(BINDFS_MOUNT_TYPE)) == 0)) {
+ return;
+ }
+
+ err = mount(BINDFS_MOUNT_TYPE, to, mnt_flags, from);
+ if (err) {
+ if ((errno == ENOENT) && !required) {
+ err = 0;
+ } else {
+ errx(errno_or_sysexit(errno, -1), "failed to mount %s -> %s - %s(%d)",
+ from, to, strerror(errno), errno);
+ }
+ }
+}
+
+static void
+setup_preboot_mounts(int pass)
+{
+ int err = 0;
+ const char *mnt_to;
+ char mnt_from[MAXPATHLEN], boot_manifest_hash[BOOT_MANIFEST_HASH_LEN];
+
+ err = get_boot_manifest_hash(boot_manifest_hash, sizeof(boot_manifest_hash));
+ if (err) {
+ errx(errno_or_sysexit(err, -1), "failed to get boot manifest hash - %s",
+ strerror(err));
+ }
+
+ const bind_mount_t preboot_mnts[] = {
+ {.bm_mnt_prefix = PREBOOT_VOL_MOUNTPOINT,
+ .bm_mnt_to = "/usr/standalone/firmware",
+ .bm_mandatory = true},
+ {.bm_mnt_prefix = PREBOOT_VOL_MOUNTPOINT,
+ .bm_mnt_to = "/usr/local/standalone/firmware",
+ .bm_mandatory = false}
+ };
+
+ const bind_mount_t hw_mnts[] = {
+ {.bm_mnt_prefix = HARDWARE_VOL_MOUNTPOINT "/Pearl",
+ .bm_mnt_to = "/System/Library/Pearl/ReferenceFrames",
+ .bm_mandatory = false},
+ {.bm_mnt_prefix = HARDWARE_VOL_MOUNTPOINT "/FactoryData",
+ .bm_mnt_to = "/System/Library/Caches/com.apple.factorydata",
+ .bm_mandatory = true}
+ };
+
+ if (pass == ROOT_PASSNO) {
+ for (int i = 0; i < (sizeof(preboot_mnts) / sizeof(preboot_mnts[0])); i++) {
+ mnt_to = preboot_mnts[i].bm_mnt_to;
+ snprintf(mnt_from, sizeof(mnt_from), "%s/%s%s",
+ preboot_mnts[i].bm_mnt_prefix, boot_manifest_hash, mnt_to);
+
+ do_bindfs_mount(mnt_from, mnt_to, preboot_mnts[i].bm_mandatory);
+ }
+ } else {
+ for (int i = 0; i < (sizeof(hw_mnts) / sizeof(hw_mnts[0])); i++) {
+ mnt_to = hw_mnts[i].bm_mnt_to;
+ snprintf(mnt_from, sizeof(mnt_from), "%s%s",
+ hw_mnts[i].bm_mnt_prefix, mnt_to);
+
+ do_bindfs_mount(mnt_from, mnt_to, hw_mnts[i].bm_mandatory);
+ }
+ }
+}
+#endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */
+
+/*
+ * mount phases to be used during boot to perform the following operations:
+ * first phase:
+ * TARGET_OS_OSX: (call `apfs_boot_util 1`
+ * TARGET_OS_IPHONE: mount System, Preboot and xART volumes (if present)
+ *
+ * second phase:
+ * TARGET_OS_OSX: unmount base system DMG if needed, upgrade System
+ * volume to RW, and call `apfs_boot_util 2` (on ROSV config)
+ * TARGET_OS_IPHONE: mount remaining volumes
+ *
+ * For more info on mount phases, see apfs_boot_util.
+ */
+#define MOUNT_PHASE_1 1 /* first phase */
+#define MOUNT_PHASE_2 2 /* second phase */
+
+#define NONFS "nonfs"
+
+static void
+bootstrap_apfs(int phase)
+{
+ char * const apfs_util_argv[] = {
+ APFS_BOOT_UTIL_PATH,
+ ((phase == MOUNT_PHASE_1) ? "1" : ((phase == MOUNT_PHASE_2) ? "2" : NULL)),
+ NULL,
+ };
+ execv(APFS_BOOT_UTIL_PATH, apfs_util_argv);
+ errx(errno_or_sysexit(errno, -1), "apfs_boot_util exec failed");
+}
+
+int
+main(argc, argv)
+ int argc;
+ char * const argv[];
+{
+ const char **vfslist, *vfstype;
+ struct fstab *fs;
+ struct statfs *mntbuf;
+ int all, ch, init_flags, rval;
+ char *options, *ep;
+ int mount_phase = 0;
+
+ all = init_flags = 0;
+ options = NULL;
+ vfslist = NULL;
+ vfstype = NULL;
+
+ while ((ch = getopt(argc, argv, "headfo:rwt:uvP:")) != EOF)
+ switch (ch) {
+ case 'a':
+ all = 1;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'f':
+ init_flags |= MNT_FORCE;
+ break;
+ case 'o':
+ if (*optarg) {
+ options = catopt(options, optarg);
+ if (strstr(optarg, "union"))
+ init_flags |= MNT_UNION;
+ }
+ break;
+ case 'r':
+ init_flags |= MNT_RDONLY;
+ break;
+ case 't':
+ if (vfslist != NULL)
+ errx(errno_or_sysexit(EINVAL, EX_USAGE),
+ "only one -t option may be specified.");
+ vfslist = makevfslist(optarg);
+ vfstype = optarg;
+ break;
+ case 'u':
+ init_flags |= MNT_UPDATE;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'w':
+ init_flags &= ~MNT_RDONLY;
+ break;
+ case 'e':
+ ret_errno = 1;
+ break;
+ case 'P':
+ /* only allowed to specify 1 or 2 as argument here */
+ mount_phase = (int)strtol(optarg, &ep, 10);
+ if ((ep == optarg) || (*ep) ||
+ (mount_phase < MOUNT_PHASE_1) ||
+ (mount_phase > MOUNT_PHASE_2)) {
+ errx(errno_or_sysexit(EINVAL, EX_USAGE),
+ "-P flag requires a valid mount phase number");
+ }
+ break;
+ case 'h':
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ argc -= optind;
+ argv += optind;
+
+#define BADTYPE(type) \
+ (strcmp(type, FSTAB_RO) && \
+ strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
+
+// mount boot tasks
+ if (mount_phase != 0) {
+#if TARGET_OS_OSX
+ bootstrap_macos = 1;
+#else /* !TARGET_OS_OSX */
+ if (mount_phase == MOUNT_PHASE_1) {
+ /* mount -vat -nonfs -P 1 */
+ passno = ROOT_PASSNO;
+ } else if (mount_phase == MOUNT_PHASE_2) {
+ /* mount -vat -nonfs -R 2 */
+ passno = NONROOT_PASSNO;
+ }
+ verbose = 1;
+ all = 1;
+ vfslist = makevfslist(NONFS);
+ vfstype = NONFS;
+#endif /* !TARGET_OS_OSX */
+ }
+
+ rval = 0;
+ switch (argc) {
+ case 0:
+ /*
+ * Note - mount should never be called with "-a" on OSX
+ * as per fstab(5) - you may insert entries with UUID=,LABEL=
+ * and mount(8) has no knowledge of these entries
+ */
+ if (all) {
+ int err = 0;
+ long fs_flags = 0;
+
+ if ((setfsent() == 0)) {
+ errx(errno_or_sysexit(errno ? errno : ENXIO, -1),
+ "mount: can't get filesystem checklist");
+ }
+
+#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
+ uint32_t os_env;
+ const char *container_dev = get_boot_container(&os_env);
+ const char *data_vol_dev = get_data_volume();
+ /*
+ * It is a given that the boot container must be present
+ * in order to locate the data volume.
+ *
+ * The data volume is required if it is present in the EDT.
+ * This is always the case when booting the main OS (EDT_OS_ENV_MAIN),
+ * however there are some exceptions.
+ *
+ * When the data volume is required,
+ * we defer checking it is present until the second mount-phase,
+ * when the data volume is actually mounted, in order to allow
+ * MobileObliteration the opportunity to fix the container or fail gracefully.
+ */
+ if (data_vol_dev) {
+ fprintf(stdout, "mount: found boot container: %s, data volume: %s env: %u\n",
+ container_dev, data_vol_dev, os_env);
+ } else if ((os_env == EDT_OS_ENV_MAIN) &&
+ (passno == MOUNT_PHASE_2)) {
+ errx(errno_or_sysexit(errno ? errno : ENXIO, -1),
+ "mount: missing data volume");
+ } else {
+ fprintf(stdout, "mount: data volume missing, but not required in env: %u\n",
+ os_env);
+ }
+#endif
+
+ while ((fs = getfsent()) != NULL) {
+ int ro_mount = !strcmp(fs->fs_type, FSTAB_RO);
+
+ if (BADTYPE(fs->fs_type))
+ continue;
+ if (checkvfsname(fs->fs_vfstype, vfslist))
+ continue;
+ if (hasopt(fs->fs_mntops, "noauto"))
+ continue;
+ if (!strcmp(fs->fs_vfstype, "nfs")) {
+ if (hasopt(fs->fs_mntops, "net"))
+ continue;
+ /* check if already mounted */
+ if (fs->fs_spec == NULL ||
+ fs->fs_file == NULL ||
+ ismounted(fs->fs_spec, fs->fs_file, NULL))
+ continue;
+ }
+
+ /* If this volume is not needed for this pass, skip it. */
+ if (passno && fs->fs_passno != passno)
+ continue;
+
+ /*
+ * Check if already mounted:
+ * 1) If mounted RW this is either an attempt to
+ * downgrade (RW -> RO) or someone else already
+ * mounted this volume as RW.
+ * 2) If mounted RO and not upgrading to RW nothing
+ * nothing need to be done so we should skip this entry.
+ * Skip this entry in both cases (basically only keep going
+ * if this is an acctual mount RW upgrade).
+ */
+ if (ismounted(fs->fs_spec, fs->fs_file, &fs_flags) &&
+ (!(fs_flags & MNT_RDONLY) || ro_mount))
+ continue;
+
+#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
+ if (!strcmp(fs->fs_spec, RAMDISK_FS_SPEC)) {
+ if (verbose) {
+ fprintf(stdout, "mount: encountered ramdisk\n");
+ }
+ rval = create_mount_ramdisk(fs, init_flags, options);
+ continue;
+ } else if (fs->fs_passno > ROOT_PASSNO &&
+ !strcmp(fs->fs_vfstype, EDTVolumeFSType) &&
+ !strcmp(fs->fs_type, FSTAB_RW)) {
+
+ /*
+ * Perform media keys migration if this is the data volume
+ * of the main OS environment
+ */
+ if (!debug && data_vol_dev &&
+ (os_env == EDT_OS_ENV_MAIN) &&
+ (strcmp(data_vol_dev, fs->fs_spec) == 0)) {
+ kern_return_t mig_err = APFSContainerMigrateMediaKeys(container_dev);
+ if (mig_err) {
+ fprintf(stderr, "mount: failed to migrate Media Keys, error = %x\n", mig_err);
+ } else {
+ fprintf(stdout, "mount: successfully migrated Media Keys\n");
+ }
+ }
+ }
+#endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */
+
+ if ((err = mountfs(fs->fs_vfstype, fs->fs_spec,
+ fs->fs_file, init_flags, options,
+ fs->fs_mntops)))
+ rval = err;
+ }
+ endfsent();
+
+#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
+ /* Setup bindfs mounts */
+ if (os_env != EDT_OS_ENV_OTHER) {
+ setup_preboot_mounts(passno);
+ }
+ /* Hand the rest of the process over to apfs_boot_util */
+ if (os_variant_has_internal_content(APFS_BUNDLE_ID) &&
+ (mount_phase == MOUNT_PHASE_2)) {
+ bootstrap_apfs(MOUNT_PHASE_2);
+ }
+#endif
+ }
+ else if (bootstrap_macos) {
+#if TARGET_OS_OSX
+ if (mount_phase == MOUNT_PHASE_1) {
+ if (booted_apfs()) {
+ bootstrap_apfs(MOUNT_PHASE_1);
+ } else {
+ fprintf(stdout, "Not booted from APFS, skipping apfs_boot_util\n");
+ exit(0);
+ }
+ } else if (mount_phase == MOUNT_PHASE_2) {
+ /*
+ * We centralize the logic for dealing with a read-only system (ROSV) volume here.
+ * If it is not set up, then default to the old logic of a `mount -uw /`
+ *
+ * Note: We can safely do this boot-task even if we are
+ * already mounted RW (e.g. boot from single user mode).
+ * In that case this will effectively be a no-op.
+ */
+ if (booted_rosv()) {
+ /* Hand the rest of the process over to apfs_boot_util */
+ bootstrap_apfs(MOUNT_PHASE_2);
+ } else {
+ /* upgrade mount "/" read-write */
+ rval = upgrade_mount("/", MNT_UPDATE, options);
+ }
+ }
+#endif /* TARGET_OS_OSX */
+ } else {
+ print_mount(vfslist);
+ }
+ exit(rval);
+ case 1:
+ if (vfslist != NULL)
+ usage();
+
+ if (init_flags & MNT_UPDATE) {
+
+ rval = upgrade_mount (*argv, init_flags, options);
+
+ break;
+ }
+
+ if ((fs = getfsfile(*argv)) == NULL &&
+ (fs = getfsspec(*argv)) == NULL)
+ errx(errno_or_sysexit(errno , -1),
+ "%s: unknown special file or file system.",
+ *argv);
+ if (BADTYPE(fs->fs_type))
+ errx(errno_or_sysexit(EINVAL, EX_DATAERR),
+ "%s has unknown file system type.",
+ *argv);
+ if (!strcmp(fs->fs_vfstype, "nfs")) {
+ if (hasopt(fs->fs_mntops, "net"))
+ errx(errno_or_sysexit(EINVAL, EX_DATAERR),
+ "%s is owned by the automounter.",
+ *argv);
+ if (ismounted(fs->fs_spec, fs->fs_file, NULL))
+ errx(errno_or_sysexit(EALREADY, EX_CONFIG),
+ "%s is already mounted at %s.",
+ fs->fs_spec, fs->fs_file);
+ }
+
+#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
+ if (!strcmp(fs->fs_spec, RAMDISK_FS_SPEC)) {
+ if (verbose) {
+ fprintf(stdout, "Found a ramdisk entry\n");
+ }
+ rval = create_mount_ramdisk(fs, init_flags, options);
+ break;
+ }
+#endif
+ rval = mountfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file,
+ init_flags, options, fs->fs_mntops);
+ break;
+ case 2:
+ /*
+ * If -t flag has not been specified, and spec contains a ':'
+ * then assume that an NFS filesystem is being specified.
+ */
+ if (vfslist == NULL && strchr(argv[0], ':') != NULL) {
+ vfstype = "nfs";
+ /* check if already mounted */
+ if (ismounted(argv[0], argv[1], NULL))
+ errx(errno_or_sysexit(EALREADY, EX_CONFIG),
+ "%s is already mounted at %s.",
+ argv[0], argv[1]);
+ }
+
+ /* If we have both a devnode and a pathname, and an update mount was requested,
+ * then figure out where the devnode is mounted. We will need to run
+ * an update mount on its path. It wouldn't make sense to do an
+ * update mount on a path other than the one it's already using.
+ *
+ * XXX: Should we implement the same workaround for updating the
+ * root file system at boot time?
+ */
+ if (init_flags & MNT_UPDATE) {
+ if ((mntbuf = getmntpt(*argv)) == NULL)
+ errx(errno_or_sysexit(errno ? errno : ENOENT, -1),
+ "unknown special file or file system %s.",
+ *argv);
+ rval = mountfs(mntbuf->f_fstypename, mntbuf->f_mntfromname,
+ mntbuf->f_mntonname, init_flags, options, 0);
+ }
+ else {
+ /*
+ * If update mount not requested, then go with the vfstype and arguments
+ * specified. If no vfstype specified, then error out.
+ */
+ if (vfstype == NULL) {
+ errx (errno_or_sysexit(EINVAL, EX_USAGE),
+ "You must specify a filesystem type with -t.");
+ }
+ rval = mountfs(vfstype,
+ argv[0], argv[1], init_flags, options, NULL);
+ }
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+
+ exit(rval);
+}
+
+static int
+upgrade_mount (const char *mountpt, int init_flags, char *options) {
+
+ struct statfs *mntbuf = NULL;
+ const char *mntfromname = NULL;
+ struct fstab *fs = NULL;
+
+ if ((mntbuf = getmntpt(mountpt)) == NULL) {
+ errx(errno_or_sysexit(errno, -1),
+ "unknown special file or file system %s.",
+ mountpt);
+ }
+
+ /*
+ * Handle the special case of upgrading the root file
+ * system from read-only to read/write. The root file
+ * system was originally mounted with a "mount from" name
+ * of "root_device". The getfsfile("/") returns non-
+ * NULL at this point, with fs_spec set to the true
+ * path to the root device (regardless of what either the real
+ * or synthesized /etc/fstab contained).
+ */
+ mntfromname = mntbuf->f_mntfromname;
+ if (strchr(mntfromname, '/') == NULL) {
+ fs = getfsfile(mntbuf->f_mntonname);
+ if (fs != NULL)
+ mntfromname = fs->fs_spec;
+ }
+
+ /*
+ * Handle the special case of upgrading a content protected
+ * file system from read-only to read/write. While our caller
+ * is nominally required to pass the protect option to maintain
+ * content protection, the kernel requires it anyway, so just add it
+ * in.
+ */
+ if (mntbuf->f_flags & MNT_CPROTECT) {
+ init_flags |= MNT_CPROTECT;
+ }
+
+
+ /* Do the update mount */
+ return mountfs(mntbuf->f_fstypename, mntfromname,
+ mntbuf->f_mntonname, init_flags, options, 0);
+
+}
+
+int
+hasopt(mntopts, option)
+ const char *mntopts, *option;
+{
+ int negative, found;
+ char *opt, *optbuf;
+
+ if (option[0] == 'n' && option[1] == 'o') {
+ negative = 1;
+ option += 2;
+ } else
+ negative = 0;
+ optbuf = strdup(mntopts);
+ found = 0;
+ for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
+ if (opt[0] == 'n' && opt[1] == 'o') {
+ if (!strcasecmp(opt + 2, option))
+ found = negative;
+ } else if (!strcasecmp(opt, option))
+ found = !negative;
+ }
+ free(optbuf);
+ return (found);
+}
+
+int
+ismounted(const char *fs_spec, const char *fs_file, long *flags)
+{
+ int i, mntsize;
+ struct statfs *mntbuf;
+
+ if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
+ err(errno_or_sysexit(errno , -1), "getmntinfo");
+ for (i = 0; i < mntsize; i++) {
+ if (strcmp(mntbuf[i].f_mntfromname, fs_spec))
+ continue;
+ if (strcmp(mntbuf[i].f_mntonname, fs_file))
+ continue;
+ if (flags)
+ *flags = mntbuf[i].f_flags;
+ return 1;
+ }
+ return 0;
+}
+
+#if TARGET_OS_OSX
+static int
+booted_apfs(void) {
+ struct statfs *mntbuf;
+
+ if ((mntbuf = getmntpt("/")) == NULL) {
+ errx(errno_or_sysexit(errno, -1),
+ "failed to lookup root file system");
+ }
+
+ return (strcmp(mntbuf->f_fstypename, "apfs") == 0);
+}
+
+static int
+booted_rosv(void) {
+ /* use sysctl to query kernel */
+ uint32_t is_rosp = 0;
+ size_t rospsize = sizeof(is_rosp);
+ int err = sysctlbyname ("vfs.generic.apfs.rosp", &is_rosp, &rospsize, NULL, NULL);
+
+ if (!err && is_rosp) {
+ return 1;
+ }
+
+ return 0;
+}
+#endif /* TARGET_OS_OSX */
+
+// prints currently mounted filesystems
+void
+print_mount(const char **vfslist)
+{
+ struct statfs *mntbuf;
+ int mntsize;
+
+ if ( (mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0 )
+ err(errno_or_sysexit(errno , -1), "getmntinfo");
+ for (int i = 0; i < mntsize; i++) {
+ if ( checkvfsname(mntbuf[i].f_fstypename, vfslist) )
+ continue;
+ prmount(&mntbuf[i]);
+ }
+}
+
+#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
+int
+verify_executable_file_existence (char *path)
+{
+ return _verify_file_flags(path, F_OK | X_OK);
+}
+
+int
+verify_file_existence (char *path)
+{
+ return _verify_file_flags(path, F_OK);
+}
+
+int
+_verify_file_flags (char *path, int flags)
+{
+
+ if ( access(path, flags) ) {
+ fprintf(stderr, "Failed access check for %s with issue %s\n", path, strerror(errno));
+ return errno;
+ }
+ return 0;
+}
+
+int
+preflight_create_mount_ramdisk (char *mnt_opts, size_t *ramdisk_size, char *template)
+{
+ char *special_ramdisk_params;
+
+ if ( mnt_opts == NULL ) {
+ fprintf(stderr, "No mnt_opts provided to ramdisk preflight.\n");
+ return EINVAL;
+ }
+
+ if ( verify_executable_file_existence(HDIK_PATH) != 0 ) {
+ fprintf(stderr, "Failed to find executable hdik at location %s \n", HDIK_PATH);
+ return ENOENT;
+ }
+
+ special_ramdisk_params = split_ramdisk_params(mnt_opts);
+ if ( special_ramdisk_params == NULL ) {
+ fprintf(stderr, "Ramdisk fstab not in expected format.\n");
+ return EINVAL;
+ }
+
+ if ( ramdisk_size ) {
+ char *ramdisk_size_str = parse_parameter_for_token(special_ramdisk_params, RAMDISK_SIZE_TOK);
+
+ if ( ramdisk_size_str != NULL ) {
+ *ramdisk_size = atoi(ramdisk_size_str);
+ free(ramdisk_size_str);
+ }
+
+ if ( *ramdisk_size == 0 ) {
+ fprintf(stderr, "Unexpected ramdisk size %zu\n", *ramdisk_size);
+ return EINVAL;
+ }
+ }
+
+ if ( template ) {
+ char *template_str = parse_parameter_for_token(special_ramdisk_params, RAMDISK_TPLT_TOK);
+ if (template_str != NULL) {
+ strlcpy(template, template_str, PATH_MAX);
+ free(template_str);
+ }
+
+ if ( template == NULL ) {
+ fprintf(stderr, "Ramdisk template path not found\n");
+ return EINVAL;
+ }
+
+ }
+
+ return 0;
+}
+#endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */
+
+/*
+ * Helper function that posix_spawn a child process
+ * as defined by command_argv[0].
+ * If `output` is non-null, then the command's stdout will be read
+ * into that buffer.
+ * If `rc` is non-null, then the command's return code will be set
+ * there.
+ * If `signal_no` is non-null, then if the command is signaled, the
+ * signal number will be set there.
+ *
+ *
+ * This function returns
+ * -1, if there's an internal error. errno will be set
+ * 0, if command exit normally with 0 as return code
+ * 1, if command exit abnormally or with a non-zero return code
+ */
+int
+run_command(char **command_argv, char *output, int *rc, int *signal_no)
+{
+ int error = -1;
+ int faulting_errno = 0;
+ int status = -1;
+ int internal_result = -1;
+ pid_t pid;
+ posix_spawn_file_actions_t actions = NULL;
+ int output_pipe[2] = {-1, -1};
+ char *command_out = NULL;
+ FILE *stream = NULL;
+
+ if ( !command_argv ) {
+ fprintf(stderr, "command_argv is NULL\n");
+ errno = EINVAL;
+ goto done;
+ }
+
+ if ( pipe(output_pipe) ) {
+ fprintf(stderr, "Failed to create pipe for command output: %d (%s)\n", errno, strerror(errno));
+ goto done;
+ }
+
+ if ( (internal_result = posix_spawn_file_actions_init(&actions)) != 0 ) {
+ errno = internal_result;
+ fprintf(stderr, "posix_spawn_file_actions_init failed: %d (%s)\n", errno, strerror(errno));
+ goto done;
+ }
+
+ if ( (internal_result = posix_spawn_file_actions_addclose(&actions, output_pipe[0])) != 0 ) {
+ errno = internal_result;
+ fprintf(stderr, "posix_spawn_file_actions_addclose output_pipe[0] failed: %d (%s)\n", errno, strerror(errno));
+ goto done;
+ }
+
+ if ( (internal_result = posix_spawn_file_actions_adddup2(&actions, output_pipe[1], STDOUT_FILENO)) != 0 ) {
+ errno = internal_result;
+ fprintf(stderr, "posix_spawn_file_actions_adddup2 output_pipe[1] failed: %d (%s)\n", errno, strerror(errno));
+ goto done;
+ }
+
+ if ( (internal_result = posix_spawn_file_actions_addclose(&actions, output_pipe[1])) != 0 ) {
+ errno = internal_result;
+ fprintf(stderr, "posix_spawn_file_actions_addclose output_pipe[1] failed: %d (%s)\n", errno, strerror(errno));
+ goto done;
+ }
+
+ if ( verbose ) {
+ fprintf(stdout, "Executing command: ");
+ for (char **command_segment = command_argv; *command_segment; command_segment++) {
+ fprintf(stdout, "%s ", *command_segment);
+ }
+ fprintf(stdout, "\n");
+ }
+
+ if ( (internal_result = posix_spawn(&pid, command_argv[0], &actions, NULL, command_argv, environ)) != 0 ) {
+ errno = internal_result;
+ fprintf(stderr, "posix_spawn failed: %d (%s)\n", errno, strerror(errno));
+ goto done;
+ }
+
+ // Close out our side of the pipe
+ close(output_pipe[1]);
+ output_pipe[1] = -1;
+
+ // If caller specified the output buffer, we'll use that
+ // Otherwise allocate a buffer and capture the output ourselves for verbose logging
+ if ( output != NULL ) {
+ command_out = output;
+ } else {
+ command_out = calloc(COMMAND_OUTPUT_MAX, sizeof(char));
+ if (!command_out) {
+ fprintf(stderr, "calloc failed: %d (%s)\n", errno, strerror(errno));
+ goto done;
+ }
+ }
+
+ stream = fdopen(output_pipe[0], "r");
+ if ( !stream ) {
+ fprintf(stderr, "fdopen failed: %d (%s)\n", errno, strerror(errno));
+ goto done;
+ }
+
+ size_t length;
+ size_t count = 0;
+ char *line;
+ while ( (line = fgetln(stream, &length)) && (count < COMMAND_OUTPUT_MAX - length - 1) ) {
+ strncat(command_out, line, length);
+ count += length;
+ }
+
+ if ( ferror(stream) ) {
+ fprintf(stderr, "fgetln failed: %d (%s)\n", errno, strerror(errno));
+ goto done;
+ }
+
+ if ( fclose(stream) ) {
+ fprintf(stderr, "fclose failed: %d (%s)\n", errno, strerror(errno));
+ stream = NULL;
+ goto done;
+ }
+ stream = NULL;
+ close(output_pipe[0]);
+ output_pipe[0] = -1;
+
+ while ( waitpid(pid, &status, 0) < 0 ) {
+ if (errno == EINTR) {
+ continue;
+ }
+ fprintf(stderr, "waitpid failed: %d (%s)\n", errno, strerror(errno));
+ goto done;
+ }
+
+ if ( verbose ) {
+ fprintf(stdout, "Command output:\n%s\n", command_out);
+ }
+
+ if ( WIFEXITED(status) ) {
+ int exit_status = WEXITSTATUS(status);
+ if (rc) *rc = exit_status;
+ if (signal_no) *signal_no = 0;
+
+ if (exit_status != 0) {
+ error = 1;
+ fprintf(stderr, "Command failed: %d\n", exit_status);
+ goto done;
+ }
+ }
+
+ if ( WIFSIGNALED(status) ) {
+ if (rc) *rc = 0;
+ if (signal_no) *signal_no = WTERMSIG(status);
+
+ error = 1;
+ fprintf(stderr, "Command signaled: %d\n", WTERMSIG(status));
+ goto done;
+ }
+
+ error = 0;
+done:
+ // we don't care much about the errno set by the clean up routine
+ // so save the errno here and return to caller
+ faulting_errno = errno;
+
+ if ( actions ) {
+ posix_spawn_file_actions_destroy(&actions);
+ }
+
+ if ( stream ) {
+ fclose(stream);
+ }
+
+ if ( output_pipe[0] >= 0 ) {
+ close(output_pipe[0]);
+ }
+
+ if ( output_pipe[1] >= 0 ) {
+ close(output_pipe[1]);
+ }
+
+ if ( !output && command_out ) {
+ free(command_out);
+ }
+
+ errno = faulting_errno;
+ return error;
+}
+
+#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
+// Helper function that truncates whitespaces
+void
+truncate_whitespace(char* str)
+{
+ size_t idx = strcspn(str, " \n");
+ if ( idx != 0 ) {
+ str[idx] = '\0';
+ }
+}
+
+// Creates a new unmounted ramdisk of size device_size
+int
+attach_device(size_t device_size , char* deviceOut)
+{
+ int return_val = -1;
+ char ram_define [PATH_MAX];
+ snprintf(ram_define, sizeof(ram_define), "ram://%zu", device_size);
+
+ char *command[4] = { HDIK_PATH, "-nomount", ram_define, NULL };
+
+ int status = run_command(command, deviceOut, &return_val, NULL);
+ if ( status == 1 ) {
+ fprintf(stderr, "Failed to create ramdisk. HDIK returned %d.\n", return_val);
+ exit(errno_or_sysexit(errno, -1));
+ } else if (status != 0) {
+ fprintf(stderr, "Failed to execute command %s\n", command[0]);
+ exit(errno_or_sysexit(errno, -1));
+ }
+
+ truncate_whitespace(deviceOut);
+ return return_val;
+}
+
+// Creates the partition table directly through MediaKit.
+int
+create_partition_table(size_t partition_size, char *device)
+{
+
+ MKStatus err = -1;
+ MKMediaRef gpt_ref = NULL;
+ CFMutableArrayRef schemes = NULL;
+ CFMutableArrayRef partitionArray = NULL;
+ CFDictionaryRef partition = NULL;
+ CFMutableDictionaryRef options = NULL;
+ CFMutableDictionaryRef layout = NULL;
+ CFMutableDictionaryRef media = NULL;
+ CFMutableDictionaryRef map = NULL;
+
+ layout = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ options = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ if (!layout || !options) {
+ fprintf(stderr, "Failed to create necessary CFDictionaries\n");
+ err = errno;
+ goto done;
+ }
+
+ CFDictionarySetValue(options, kMKMediaPropertyWritableKey, kCFBooleanTrue);
+
+ gpt_ref = MKMediaCreateWithPath(kCFAllocatorDefault, device, options, &err);
+ CFRelease(options);
+
+ if (gpt_ref) {
+ MKStatus mediaErr = 0;
+ partition = MKCFBuildPartition(PMGPTTYPE, apple_apfs, CFSTR(EDTVolumeFSType), CFSTR(RAMDISK_FS_SPEC), 0, RAMDISK_BLCK_OFFSET, &mediaErr, NULL);
+
+ if (!partition) {
+ fprintf(stderr, "Failed to create partition with err %d\n", mediaErr);
+ err = mediaErr;
+ goto done;
+ }
+
+ partitionArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+ if (!partitionArray) {
+ fprintf(stderr, "Failed to create partitionArray\n");
+ err = errno;
+ CFRelease(partition);
+ goto done;
+ }
+
+ CFArrayAppendValue(partitionArray, partition);
+ CFRelease(partition);
+
+ CFDictionaryAddValue(layout, CFSTR(MK_PARTITIONS_KEY), partitionArray);
+ CFRelease(partitionArray);
+
+
+ media = MKCFCreateMedia(&schemes, &mediaErr);
+
+ if (!media) {
+ fprintf(stderr, "Failed to create Schemes with error %d\n", mediaErr);
+ err = mediaErr;
+ goto done;
+ }
+
+ map = MKCFCreateMap(PMGPTTYPE, schemes, layout, NULL, NULL, NULL, NULL, NULL, gpt_ref, &mediaErr);
+ if (!map) {
+ fprintf(stderr, "Failed to create map with error %d\n", mediaErr);
+ err = mediaErr;
+ goto done;
+ }
+
+ err = MKCFWriteMedia(media, layout, NULL, NULL, gpt_ref);
+ if (err) {
+ fprintf(stderr, "Failed to WriteMedia with error %d\n", err);
+ goto done;
+ }
+
+ } else {
+ fprintf(stderr, "Failed to create gpt_ref with error %d\n", err);
+ goto done;
+ }
+
+ if (verbose) {
+ fprintf(stderr, "Releasing MediaKit objects\n");
+ }
+ err = 0;
+
+done:
+ if (media) {
+ MKCFDisposeMedia(media);
+ }
+
+ if (layout) {
+ CFRelease(layout);
+ }
+
+ if (gpt_ref) {
+ CFRelease(gpt_ref);
+ }
+
+ return err;
+}
+
+// Triggers newfs_apfs for the target device
+int
+construct_apfs_volume(char *mounted_device_name)
+{
+ int return_val = -1;
+ int status = -1;
+ char *command[5] = { "/sbin/newfs_apfs", "-v", "Var", mounted_device_name, NULL };
+
+ status = run_command(command, NULL, &return_val, NULL);
+ if ( status >= 0 ) {
+ return return_val;
+ } else {
+ fprintf(stderr, "Failed to execute command %s\n", command[0]);
+ errno_or_sysexit(errno, -1);
+ }
+
+ // shouldn't reach here. This is to satisfy the compiler
+ return -1;
+}
+
+// unmounts device at location
+int
+unmount_location(char *mount_point)
+{
+ int return_val = -1;
+ int status = -1;
+ char *command[4] = { "/sbin/umount", "-f", mount_point, NULL };
+
+ status = run_command(command, NULL, &return_val, NULL);
+ if ( status >= 0 ) {
+ return return_val;
+ } else {
+ fprintf(stderr, "Failed to execute command %s\n", command[0]);
+ return errno_or_sysexit(errno, -1);
+ }
+}
+
+// The mnt_opts for fstab are standard across the different
+// mount_fs implementations. To create and mount an ephemeral
+// filesystem, it is necessary to provide additional non-standard
+// values in filesystem definition - mainly size and location of
+// the seed files.
+// The fstab definition for a ramdisk fs requires two new parameters:
+// 'size=%zu' and 'template=%s'. To keep the fstab structure
+// consistent with that of other filesystem types, these
+// parameters are appended at the end of the mnt_opts string.
+// It is necessary to split the mnt_opts into two strings, the
+// standard mountfs parameters that are used in the fs-specifnc mount
+// and the ramdisk definition parameters.
+char*
+split_ramdisk_params(char *opts)
+{
+ char* opt = NULL;
+ char* target_str = NULL;
+ char* size_tok = RAMDISK_SIZE_TOK;
+ char* tplt_tok = RAMDISK_TPLT_TOK;
+ char* optbuf = NULL;
+ size_t size_tok_len = strlen(size_tok);
+ size_t tplt_tok_len = strlen(tplt_tok);
+
+ optbuf = strdup(opts);
+ for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
+ size_t opt_len = strlen(opt);
+ if ( (opt_len > size_tok_len && !strncmp(size_tok, opt, size_tok_len) ) ||
+ (opt_len > tplt_tok_len && !strncmp(tplt_tok, opt, tplt_tok_len) ) ) {
+ size_t start_index = opt - optbuf;
+ target_str = opts + start_index;
+ opts[start_index - 1 ] = '\0'; // Break original into two strings.
+ break;
+ }
+ }
+ free(optbuf);
+ return target_str;
+}
+
+// returns string for the parameter after the '=' in the search_string
+// part of the special ramdisk parameters
+char*
+parse_parameter_for_token(char * opts, char * search_string)
+{
+ char *return_str = NULL;
+ char *tmp_str = NULL;
+ char *target_str = strstr(opts, search_string);
+ size_t len = strlen(search_string);
+
+ if ( target_str && strlen(target_str) > len ) {
+ tmp_str = target_str + len;
+ size_t idx = strcspn(tmp_str, ",\0");
+ if ( idx != 0 && (idx < MAXPATHLEN) ) {
+ return_str = calloc(1, idx+1); //for null terminator
+ strncpy(return_str, tmp_str, idx);
+ }
+ }
+ return return_str;
+}
+
+
+static int _copyfile_status(int what, int stage, copyfile_state_t state, const char * src, const char * dst, void * ctx) {
+
+ if (verbose && stage == COPYFILE_START) {
+ if (what == COPYFILE_RECURSE_FILE) {
+ fprintf(stderr, "Copying %s -> %s\n", src, dst);
+ } else if (what == COPYFILE_RECURSE_DIR) {
+ fprintf(stderr, "Creating %s/\n", dst);
+ }
+ }
+
+ return COPYFILE_CONTINUE;
+}
+
+
+// returns 0 upon success and a valid sysexit or errno code upon failure
+int
+create_mount_ramdisk(struct fstab *fs, int init_flags, char *options)
+{
+ int default_flags = init_flags;
+ int mount_return = 0;
+ char ramdisk_partition [PATH_MAX] = { 0 };
+ char ramdisk_volume [PATH_MAX] = { 0 };
+ char ramdisk_container [PATH_MAX] = { 0 };
+ char seed_location [PATH_MAX] = { 0 };
+ char *mnt_point = RAMDISK_TMP_MOUNT; // intermediate
+ char *target_dir = fs->fs_file; // target
+ size_t ram_size = 0;
+
+ if ( verify_file_existence(mnt_point) != 0 ) {
+ if (verbose) {
+ fprintf(stderr, "Default mount %s is not available. Using backup %s.\n", mnt_point, RAMDISK_BCK_MOUNT);
+ }
+ mnt_point = RAMDISK_BCK_MOUNT;
+ if ( verify_file_existence(mnt_point) != 0 ) {
+ fprintf(stderr, "Mountpoints not available. Exiting.\n");
+ return ENOENT;
+ }
+ }
+
+ if ( preflight_create_mount_ramdisk(fs->fs_mntops, &ram_size, seed_location) != 0 ) {
+ fprintf(stderr, "Failed ramdisk preflight. Exiting.\n");
+ return EINVAL;
+ }
+
+ if ( verbose ) {
+ fprintf(stdout, "Attaching device of size %zu\n", ram_size);
+ }
+
+ if( attach_device(ram_size, ramdisk_partition) != 0 ){
+ fprintf(stderr, "Failed to attach the ramdisk.\n");
+ exit(errno_or_sysexit(ECHILD, -1));
+ }
+
+ if ( verbose ) {
+ fprintf(stdout, "Creating partition table for device %s \n", ramdisk_partition);
+ }
+
+ if ( create_partition_table(ram_size, ramdisk_partition) !=0 ) {
+ fprintf(stderr, "Failed to create partition table.\n");
+ exit(errno_or_sysexit(ECHILD, -1));
+ }
+
+ snprintf(ramdisk_container, sizeof(ramdisk_container), "%ss1", ramdisk_partition);
+
+ if ( verbose ) {
+ fprintf(stdout, "Creating apfs volume on partition %s\n", ramdisk_container);
+ }
+
+ if ( construct_apfs_volume(ramdisk_container) != 0 ) {
+ fprintf(stderr, "Failed to construct the apfs volume on the ramdisk.\n");
+ exit(errno_or_sysexit(ECHILD, -1));
+ }
+
+ snprintf(ramdisk_volume, sizeof(ramdisk_volume), "%ss1", ramdisk_container);
+
+ if ( verify_file_existence(ramdisk_volume) != 0 ) {
+ fprintf(stderr, "Failed to verify %s with issue %s\n", ramdisk_volume, strerror(errno));
+ exit(errno_or_sysexit(errno, -1));
+ }
+
+ // Mount volume to RAMDISK_TMP_MOUNT
+ if ( verbose ) {
+ fprintf(stdout, "Mounting to tmp location %s\n", mnt_point);
+ }
+
+ mount_return = mountfs(EDTVolumeFSType, ramdisk_volume, mnt_point, default_flags, NULL, fs->fs_mntops);
+ if ( mount_return > 0 ) {
+ fprintf(stderr, "Initial mount to %s failed with %d\n", mnt_point, mount_return);
+ exit(errno_or_sysexit(errno, -1));
+ }
+
+ // ditto contents of RAMDISK_TMP_MOUNT to /private/var
+ copyfile_state_t state = copyfile_state_alloc();
+ copyfile_state_set(state, COPYFILE_STATE_STATUS_CB, _copyfile_status);
+ if( copyfile(seed_location, mnt_point, state, COPYFILE_ALL | COPYFILE_RECURSIVE) < 0 ) {
+ fprintf(stderr, "Failed to copy contents from %s to %s with error: %s\n", seed_location, mnt_point, strerror(errno));
+ exit(errno_or_sysexit(errno, -1));
+ }
+ copyfile_state_free(state);
+
+ // unount RAMDISK_TMP_MOUNT
+ if( unmount_location(mnt_point) != 0 ){
+ fprintf(stderr, "Failed to unmount device mounted at %s.\n", mnt_point);
+ exit(errno_or_sysexit(ECHILD, -1));
+ }
+
+ if( verbose ) {
+ fprintf(stdout, "Mounting apfs volume %s to %s\n", ramdisk_volume, target_dir);
+ }
+
+ mount_return = mountfs(EDTVolumeFSType, ramdisk_volume, target_dir, default_flags, options, fs->fs_mntops);
+ if ( mount_return > 0 ) {
+ fprintf(stderr, "Followup mount to %s failed with %d\n", target_dir, mount_return);
+ exit(errno_or_sysexit(errno, -1));
+ }
+
+ // Verify contents in stdout
+ if ( verbose ) {
+ print_mount(NULL);
+ }
+
+ return mount_return;
+}
+#endif // (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
+
+// returns 0 upon success and a valid sysexit or errno code upon failure
+int
+mountfs(const char *vfstype, const char *fs_spec, const char *fs_file, int flags,
+ const char *options, const char *mntopts)
+{
+ /* List of directories containing mount_xxx subcommands. */
+ static const char *edirs[] = {
+ _PATH_SBIN,
+ _PATH_USRSBIN,
+ NULL
+ };
+ static const char *bdirs[] = {
+ _PATH_FSBNDL,
+ _PATH_USRFSBNDL,
+ NULL
+ };
+ const char *argv[100], **edir, **bdir;
+ struct statfs sf;
+ pid_t pid;
+ int argc, i, status;
+ char *optbuf, execname[MAXPATHLEN + 1], mntpath[MAXPATHLEN];
+
+ if (realpath(fs_file, mntpath) == NULL) {
+ /* Attempt to create missing mountpoints on Data volume */
+ if ((passno == NONROOT_PASSNO) &&
+ (!strncmp(mntpath, PLATFORM_DATA_VOLUME_MOUNT_POINT,
+ MIN(strlen(mntpath), strlen(PLATFORM_DATA_VOLUME_MOUNT_POINT))))) {
+ if (mkdir(mntpath, S_IRWXU)) {
+ warn("mkdir %s", mntpath);
+ return (errno_or_sysexit(errno , -1));
+ }
+ } else {
+ warn("realpath %s", mntpath);
+ return (errno_or_sysexit(errno , -1));
+ }
+ }
+
+ fs_file = mntpath;
+
+ if (mntopts == NULL)
+ mntopts = "";
+ if (options == NULL) {
+ if (*mntopts == '\0') {
+ options = "";
+ } else {
+ options = mntopts;
+ mntopts = "";
+ }
+ }
+ optbuf = catopt(strdup(mntopts), options);
+
+ if ((strcmp(fs_file, "/") == 0) && !(flags & MNT_UNION))
+ flags |= MNT_UPDATE;
+ if (flags & MNT_FORCE)
+ optbuf = catopt(optbuf, "force");
+ if (flags & MNT_RDONLY)
+ optbuf = catopt(optbuf, "ro");
+ if (flags & MNT_UPDATE)
+ optbuf = catopt(optbuf, "update");
+ if (flags & MNT_DONTBROWSE)
+ optbuf = catopt(optbuf, "nobrowse");
+ if (flags & MNT_CPROTECT)
+ optbuf = catopt(optbuf, "protect");
+
+
+ argc = 0;
+ argv[argc++] = vfstype;
+ mangle(optbuf, &argc, argv);
+ argv[argc++] = fs_spec;
+ argv[argc++] = fs_file;
+ argv[argc] = NULL;
+
+ if (debug) {
+ (void)printf("exec: mount_%s", vfstype);
+ for (i = 1; i < argc; i++)
+ (void)printf(" %s", argv[i]);
+ (void)printf("\n");
+ return (0);
+ }
+
+ switch (pid = fork()) {
+ case -1: /* Error. */
+ warn("fork");
+ free(optbuf);
+ return (errno_or_sysexit(errno, EX_OSERR));
+ case 0: /* Child. */
+ /* Go find an executable. */
+ edir = edirs;
+ do {
+ (void)snprintf(execname, sizeof(execname),
+ "%s/mount_%s", *edir, vfstype);
+
+ argv[0] = execname;
+ execv(execname, (char * const *)argv);
+ if (errno != ENOENT)
+ warn("exec %s for %s", execname, fs_file);
+ } while (*++edir != NULL);
+
+ bdir = bdirs;
+ do {
+ /* Special case file system bundle executable path */
+ (void)snprintf(execname, sizeof(execname),
+ "%s/%s.fs/%s/mount_%s", *bdir,
+ vfstype, _PATH_FSBNDLBIN, vfstype);
+
+ argv[0] = execname;
+ execv(execname, (char * const *)argv);
+ if (errno != ENOENT)
+ warn("exec %s for %s", execname, fs_file);
+ } while (*++bdir != NULL);
+
+ if (errno == ENOENT) {
+ warn("exec %s for %s", execname, fs_file);
+ return (errno_or_sysexit(errno, EX_OSFILE));
+ }
+ exit(errno_or_sysexit(errno , -1));
+ /* NOTREACHED */
+ default: /* Parent. */
+ free(optbuf);
+
+ if (waitpid(pid, &status, 0) < 0) {
+ warn("waitpid");
+ return (errno_or_sysexit(errno , -1));
+ }
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0) {
+ warnx("%s failed with %d", fs_file, WEXITSTATUS(status));
+ return (errno_or_sysexit(EINTR, WEXITSTATUS(status)));
+ }
+ } else if (WIFSIGNALED(status)) {
+ warnx("%s: %s", fs_file, sys_siglist[WTERMSIG(status)]);
+ return (errno_or_sysexit(EINTR, EX_UNAVAILABLE));
+ }
+
+ if (verbose) {
+ if (statfs(fs_file, &sf) < 0) {
+ warn("statfs %s", fs_file);
+ return (errno_or_sysexit(errno , -1));
+ }
+ prmount(&sf);
+ }
+ break;
+ }
+
+ return (EX_OK);
+}
+
+static bool
+is_sealed(const char *mntpt)
+{
+ struct vol_attr {
+ uint32_t len;
+ vol_capabilities_attr_t vol_cap;
+ } vol_attrs = {};
+
+ struct attrlist vol_attr_list = {
+ .bitmapcount = ATTR_BIT_MAP_COUNT,
+ .volattr = ATTR_VOL_CAPABILITIES
+ };
+
+ if (!getattrlist(mntpt, &vol_attr_list, &vol_attrs, sizeof(vol_attrs), 0) &&
+ vol_attrs.vol_cap.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_SEALED) {
+ return (vol_attrs.vol_cap.capabilities[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_SEALED);
+ }
+ return false;
+}
+
+void
+prmount(sfp)
+ struct statfs *sfp;
+{
+ int flags;
+ mountopt_t *o;
+ struct passwd *pw;
+
+ (void)printf("%s on %s (%s", sfp->f_mntfromname, sfp->f_mntonname,
+ sfp->f_fstypename);
+
+ if (is_sealed(sfp->f_mntonname))
+ (void)printf(", sealed");
+ flags = sfp->f_flags & MNT_VISFLAGMASK;
+ for (o = optnames; flags && o->o_opt; o++)
+ if (flags & o->o_opt) {
+ (void)printf(", %s", o->o_name);
+ flags &= ~o->o_opt;
+ }
+ if (sfp->f_owner) {
+ (void)printf(", mounted by ");
+ if ((pw = getpwuid(sfp->f_owner)) != NULL)
+ (void)printf("%s", pw->pw_name);
+ else
+ (void)printf("%d", sfp->f_owner);
+ }
+ (void)printf(")\n");
+}
+
+struct statfs *
+getmntpt(name)
+ const char *name;
+{
+ struct statfs *mntbuf;
+ int i, mntsize;
+
+ mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
+ for (i = 0; i < mntsize; i++) {
+ if (strcmp(mntbuf[i].f_mntfromname, name) == 0 ||
+ strcmp(mntbuf[i].f_mntonname, name) == 0)
+ return (&mntbuf[i]);
+ }
+ return (NULL);
+}
+
+char *
+catopt(s0, s1)
+ char *s0;
+ const char *s1;
+{
+ size_t i;
+ char *cp;
+
+ if (s0 && *s0) {
+ i = strlen(s0) + strlen(s1) + 1 + 1;
+ if ((cp = malloc(i)) == NULL)
+ err(errno_or_sysexit(errno, EX_TEMPFAIL),
+ "failed to allocate memory for arguments");
+ (void)snprintf(cp, i, "%s,%s", s0, s1);
+ } else
+ cp = strdup(s1);
+
+ if (s0)
+ free(s0);
+ return (cp);
+}
+
+void
+mangle(options, argcp, argv)
+ char *options;
+ int *argcp;
+ const char **argv;
+{
+ char *p, *s;
+ int argc;
+
+ argc = *argcp;
+ for (s = options; (p = strsep(&s, ",")) != NULL;)
+ if (*p != '\0') {
+ if (*p == '-') {
+ argv[argc++] = p;
+ p = strchr(p, '=');
+ if (p) {
+ *p = '\0';
+ argv[argc++] = p+1;
+ }
+ } else {
+ argv[argc++] = "-o";
+ argv[argc++] = p;
+ }
+ }
+
+ *argcp = argc;
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr,
+ "usage: mount %s %s\n mount %s\n mount %s\n",
+ "[-dfruvw] [-o options] [-t external_type]",
+ "special mount_point",
+ "[-adfruvw] [-t external_type]",
+ "[-dfruvw] special | mount_point");
+ exit(errno_or_sysexit(EINVAL, EX_USAGE));
+}