2 * Copyright (c) 1999-2020 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 * Copyright (c) 1980, 1989, 1993, 1994
25 * The Regents of the University of California. All rights reserved.
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
35 * 3. All advertising materials mentioning features or use of this software
36 * must display the following acknowledgement:
37 * This product includes software developed by the University of
38 * California, Berkeley and its contributors.
39 * 4. Neither the name of the University nor the names of its contributors
40 * may be used to endorse or promote products derived from this software
41 * without specific prior written permission.
43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 #include <sys/param.h>
57 #include <sys/mount.h>
61 #include <System/sys/reason.h>
65 #include <os/variant_private.h>
73 #include <TargetConditionals.h>
75 #include <sys/sysctl.h>
76 #include <APFS/APFS.h>
77 #include <APFS/APFSConstants.h>
80 #include <crt_externs.h>
82 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
84 #include <sys/socket.h>
85 #include <sys/queue.h>
87 #include <sys/param.h>
88 #include <sys/cdefs.h>
90 /* Some APFS specific goop */
92 #include <MediaKit/MKMedia.h>
93 #include <MediaKit/MKMediaAccess.h>
94 #include <MediaKit/GPTTypes.h>
98 // To unmount the BaseSystem disk image.
100 #include <IOKit/IOBSD.h>
101 #include <IOKit/IOKitLib.h>
102 #include <IOKit/storage/IOMedia.h>
105 #include "mount_flags.h"
106 #include "edt_fstab.h"
107 #include "pathnames.h"
111 #define APFS_BOOT_UTIL_PATH "/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs_boot_util"
112 #define PLATFORM_DATA_VOLUME_MOUNT_POINT "/System/Volumes/Data"
113 #define BASE_SYSTEM_PATH "/System/Volumes/BaseSystem"
114 #define RECOVERY_PATH "/System/Volumes/Recovery"
116 #define APFS_BOOT_UTIL_PATH "/System/Library/Filesystems/apfs.fs/apfs_boot_util"
117 #define PLATFORM_DATA_VOLUME_MOUNT_POINT "/private/var"
120 #define environ (*_NSGetEnviron())
121 #define COMMAND_OUTPUT_MAX 1024
125 int bootstrap_macos
= 0;
128 int checkvfsname
__P((const char *, const char **));
129 char *catopt
__P((char *, const char *));
130 struct statfs
*getmntpt
__P((const char *));
131 int hasopt
__P((const char *, const char *));
133 **makevfslist
__P((char *));
134 void mangle
__P((char *, int *, const char **));
135 void prmount
__P((struct statfs
*));
136 void usage
__P((void));
138 int run_command(char **command_argv
, char *output
, int *rc
, int *signal_no
);
139 void print_mount(const char **vfslist
);
140 int ismounted(const char *fs_spec
, const char *fs_file
, long *flags
);
141 int mountfs(const char *vfstype
, const char *fs_spec
, const char *fs_file
, int flags
, const char *options
, const char *mntopts
);
142 int unmount_location(char *mount_point
);
144 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
146 char* parse_parameter_for_token(char * opts
, char * search_string
);
147 int verify_executable_file_existence (char *path
);
148 int verify_file_existence (char *path
);
149 int _verify_file_flags (char *path
, int flags
);
150 int preflight_create_mount_ramdisk (char *mnt_opts
, size_t *ramdisk_size
, char *template);
151 char* split_ramdisk_params(char *opts
);
152 int create_mount_ramdisk(struct fstab
*fs
, int init_flags
, char *options
);
153 int construct_apfs_volume(char *mounted_device_name
);
154 int create_partition_table(size_t partition_size
, char *device
);
155 int attach_device(size_t device_size
, char* deviceOut
);
156 void truncate_whitespace(char* str
);
158 #define RAMDISK_BLCK_OFFSET 34
159 #define RAMDISK_TMP_MOUNT "/mnt2"
160 #define RAMDISK_BCK_MOUNT "/.mb"
161 #define RAMDISK_SIZE_TOK "size="
162 #define RAMDISK_TPLT_TOK "template="
163 #define HDIK_PATH "/usr/sbin/hdik"
165 #endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */
167 //pull in the optnames array from the mount_flags.c file
168 extern mountopt_t optnames
[];
170 static int booted_rosv(void);
171 static int booted_apfs(void);
172 #endif /* TARGET_OS_OSX */
173 static int upgrade_mount(const char *mountpt
, int init_flags
, char *options
);
176 * Map a POSIX error code to a representative sysexits(3) code. Can be disabled
177 * to exit with errno error code by passing -e as a command line argument to mount
179 static int ret_errno
= 0;
181 errno_or_sysexit(int err
, int sysexit
)
184 sysexit
= sysexit_np(err
);
186 return (ret_errno
? err
: sysexit
);
189 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
190 #define BINDFS_MOUNT_TYPE "bindfs"
191 #define PREBOOT_VOL_MOUNTPOINT "/private/preboot"
192 #define HARDWARE_VOL_MOUNTPOINT "/private/var/hardware"
194 #define BOOT_MANIFEST_HASH_LEN 256
196 typedef struct bind_mount
{
203 do_bindfs_mount(char *from
, const char *to
, bool required
)
206 uint32_t mnt_flags
= MNT_RDONLY
| MNT_NODEV
| MNT_NOSUID
| MNT_DONTBROWSE
;
210 printf("call: mount %s %s %x %s", BINDFS_MOUNT_TYPE
, to
, mnt_flags
, from
);
214 if ((statfs(from
, &sfs
) == 0) &&
215 (strncmp(sfs
.f_fstypename
, BINDFS_MOUNT_TYPE
, sizeof(BINDFS_MOUNT_TYPE
)) == 0)) {
219 err
= mount(BINDFS_MOUNT_TYPE
, to
, mnt_flags
, from
);
221 if ((errno
== ENOENT
) && !required
) {
224 errx(errno_or_sysexit(errno
, -1), "failed to mount %s -> %s - %s(%d)",
225 from
, to
, strerror(errno
), errno
);
231 setup_preboot_mounts(int pass
)
235 char mnt_from
[MAXPATHLEN
], boot_manifest_hash
[BOOT_MANIFEST_HASH_LEN
];
237 err
= get_boot_manifest_hash(boot_manifest_hash
, sizeof(boot_manifest_hash
));
239 errx(errno_or_sysexit(err
, -1), "failed to get boot manifest hash - %s",
243 const bind_mount_t preboot_mnts
[] = {
244 {.bm_mnt_prefix
= PREBOOT_VOL_MOUNTPOINT
,
245 .bm_mnt_to
= "/usr/standalone/firmware",
246 .bm_mandatory
= true},
247 {.bm_mnt_prefix
= PREBOOT_VOL_MOUNTPOINT
,
248 .bm_mnt_to
= "/usr/local/standalone/firmware",
249 .bm_mandatory
= false}
252 const bind_mount_t hw_mnts
[] = {
253 {.bm_mnt_prefix
= HARDWARE_VOL_MOUNTPOINT
"/Pearl",
254 .bm_mnt_to
= "/System/Library/Pearl/ReferenceFrames",
255 .bm_mandatory
= false},
256 {.bm_mnt_prefix
= HARDWARE_VOL_MOUNTPOINT
"/FactoryData",
257 .bm_mnt_to
= "/System/Library/Caches/com.apple.factorydata",
258 .bm_mandatory
= true}
261 if (pass
== ROOT_PASSNO
) {
262 for (int i
= 0; i
< (sizeof(preboot_mnts
) / sizeof(preboot_mnts
[0])); i
++) {
263 mnt_to
= preboot_mnts
[i
].bm_mnt_to
;
264 snprintf(mnt_from
, sizeof(mnt_from
), "%s/%s%s",
265 preboot_mnts
[i
].bm_mnt_prefix
, boot_manifest_hash
, mnt_to
);
267 do_bindfs_mount(mnt_from
, mnt_to
, preboot_mnts
[i
].bm_mandatory
);
270 for (int i
= 0; i
< (sizeof(hw_mnts
) / sizeof(hw_mnts
[0])); i
++) {
271 mnt_to
= hw_mnts
[i
].bm_mnt_to
;
272 snprintf(mnt_from
, sizeof(mnt_from
), "%s%s",
273 hw_mnts
[i
].bm_mnt_prefix
, mnt_to
);
275 do_bindfs_mount(mnt_from
, mnt_to
, hw_mnts
[i
].bm_mandatory
);
279 #endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */
282 * mount phases to be used during boot to perform the following operations:
284 * TARGET_OS_OSX: (call `apfs_boot_util 1`
285 * TARGET_OS_IPHONE: mount System, Preboot and xART volumes (if present)
288 * TARGET_OS_OSX: unmount base system DMG if needed, upgrade System
289 * volume to RW, and call `apfs_boot_util 2` (on ROSV config)
290 * TARGET_OS_IPHONE: mount remaining volumes
292 * For more info on mount phases, see apfs_boot_util.
294 #define MOUNT_PHASE_1 1 /* first phase */
295 #define MOUNT_PHASE_2 2 /* second phase */
297 #define NONFS "nonfs"
300 bootstrap_apfs(int phase
)
302 char * const apfs_util_argv
[] = {
304 ((phase
== MOUNT_PHASE_1
) ? "1" : ((phase
== MOUNT_PHASE_2
) ? "2" : NULL
)),
307 execv(APFS_BOOT_UTIL_PATH
, apfs_util_argv
);
308 errx(errno_or_sysexit(errno
, -1), "apfs_boot_util exec failed");
316 const char **vfslist
, *vfstype
;
318 struct statfs
*mntbuf
;
319 int all
, ch
, init_flags
, rval
;
323 all
= init_flags
= 0;
328 while ((ch
= getopt(argc
, argv
, "headfo:rwt:uvP:")) != EOF
)
337 init_flags
|= MNT_FORCE
;
341 options
= catopt(options
, optarg
);
342 if (strstr(optarg
, "union"))
343 init_flags
|= MNT_UNION
;
347 init_flags
|= MNT_RDONLY
;
351 errx(errno_or_sysexit(EINVAL
, EX_USAGE
),
352 "only one -t option may be specified.");
353 vfslist
= makevfslist(optarg
);
357 init_flags
|= MNT_UPDATE
;
363 init_flags
&= ~MNT_RDONLY
;
369 /* only allowed to specify 1 or 2 as argument here */
370 mount_phase
= (int)strtol(optarg
, &ep
, 10);
371 if ((ep
== optarg
) || (*ep
) ||
372 (mount_phase
< MOUNT_PHASE_1
) ||
373 (mount_phase
> MOUNT_PHASE_2
)) {
374 errx(errno_or_sysexit(EINVAL
, EX_USAGE
),
375 "-P flag requires a valid mount phase number");
387 #define BADTYPE(type) \
388 (strcmp(type, FSTAB_RO) && \
389 strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
392 if (mount_phase
!= 0) {
395 #else /* !TARGET_OS_OSX */
396 if (mount_phase
== MOUNT_PHASE_1
) {
397 /* mount -vat -nonfs -P 1 */
398 passno
= ROOT_PASSNO
;
399 } else if (mount_phase
== MOUNT_PHASE_2
) {
400 /* mount -vat -nonfs -R 2 */
401 passno
= NONROOT_PASSNO
;
405 vfslist
= makevfslist(NONFS
);
407 #endif /* !TARGET_OS_OSX */
414 * Note - mount should never be called with "-a" on OSX
415 * as per fstab(5) - you may insert entries with UUID=,LABEL=
416 * and mount(8) has no knowledge of these entries
422 if ((setfsent() == 0)) {
423 errx(errno_or_sysexit(errno
? errno
: ENXIO
, -1),
424 "mount: can't get filesystem checklist");
427 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
429 const char *container_dev
= get_boot_container(&os_env
);
430 const char *data_vol_dev
= get_data_volume();
432 * It is a given that the boot container must be present
433 * in order to locate the data volume.
435 * The data volume is required if it is present in the EDT.
436 * This is always the case when booting the main OS (EDT_OS_ENV_MAIN),
437 * however there are some exceptions.
439 * When the data volume is required,
440 * we defer checking it is present until the second mount-phase,
441 * when the data volume is actually mounted, in order to allow
442 * MobileObliteration the opportunity to fix the container or fail gracefully.
445 fprintf(stdout
, "mount: found boot container: %s, data volume: %s env: %u\n",
446 container_dev
, data_vol_dev
, os_env
);
447 } else if ((os_env
== EDT_OS_ENV_MAIN
) &&
448 (passno
== MOUNT_PHASE_2
)) {
449 errx(errno_or_sysexit(errno
? errno
: ENXIO
, -1),
450 "mount: missing data volume");
452 fprintf(stdout
, "mount: data volume missing, but not required in env: %u\n",
457 while ((fs
= getfsent()) != NULL
) {
458 int ro_mount
= !strcmp(fs
->fs_type
, FSTAB_RO
);
460 if (BADTYPE(fs
->fs_type
))
462 if (checkvfsname(fs
->fs_vfstype
, vfslist
))
464 if (hasopt(fs
->fs_mntops
, "noauto"))
466 if (!strcmp(fs
->fs_vfstype
, "nfs")) {
467 if (hasopt(fs
->fs_mntops
, "net"))
469 /* check if already mounted */
470 if (fs
->fs_spec
== NULL
||
471 fs
->fs_file
== NULL
||
472 ismounted(fs
->fs_spec
, fs
->fs_file
, NULL
))
476 /* If this volume is not needed for this pass, skip it. */
477 if (passno
&& fs
->fs_passno
!= passno
)
481 * Check if already mounted:
482 * 1) If mounted RW this is either an attempt to
483 * downgrade (RW -> RO) or someone else already
484 * mounted this volume as RW.
485 * 2) If mounted RO and not upgrading to RW nothing
486 * nothing need to be done so we should skip this entry.
487 * Skip this entry in both cases (basically only keep going
488 * if this is an acctual mount RW upgrade).
490 if (ismounted(fs
->fs_spec
, fs
->fs_file
, &fs_flags
) &&
491 (!(fs_flags
& MNT_RDONLY
) || ro_mount
))
494 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
495 if (!strcmp(fs
->fs_spec
, RAMDISK_FS_SPEC
)) {
497 fprintf(stdout
, "mount: encountered ramdisk\n");
499 rval
= create_mount_ramdisk(fs
, init_flags
, options
);
501 } else if (fs
->fs_passno
> ROOT_PASSNO
&&
502 !strcmp(fs
->fs_vfstype
, EDTVolumeFSType
) &&
503 !strcmp(fs
->fs_type
, FSTAB_RW
)) {
506 * Perform media keys migration if this is the data volume
507 * of the main OS environment
509 if (!debug
&& data_vol_dev
&&
510 (os_env
== EDT_OS_ENV_MAIN
) &&
511 (strcmp(data_vol_dev
, fs
->fs_spec
) == 0)) {
512 kern_return_t mig_err
= APFSContainerMigrateMediaKeys(container_dev
);
514 fprintf(stderr
, "mount: failed to migrate Media Keys, error = %x\n", mig_err
);
516 fprintf(stdout
, "mount: successfully migrated Media Keys\n");
520 #endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */
522 if ((err
= mountfs(fs
->fs_vfstype
, fs
->fs_spec
,
523 fs
->fs_file
, init_flags
, options
,
529 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
530 /* Setup bindfs mounts */
531 if (os_env
!= EDT_OS_ENV_OTHER
) {
532 setup_preboot_mounts(passno
);
534 /* Hand the rest of the process over to apfs_boot_util */
535 if (os_variant_has_internal_content(APFS_BUNDLE_ID
) &&
536 (mount_phase
== MOUNT_PHASE_2
)) {
537 bootstrap_apfs(MOUNT_PHASE_2
);
541 else if (bootstrap_macos
) {
543 if (mount_phase
== MOUNT_PHASE_1
) {
545 bootstrap_apfs(MOUNT_PHASE_1
);
547 fprintf(stdout
, "Not booted from APFS, skipping apfs_boot_util\n");
550 } else if (mount_phase
== MOUNT_PHASE_2
) {
552 * We centralize the logic for dealing with a read-only system (ROSV) volume here.
553 * If it is not set up, then default to the old logic of a `mount -uw /`
555 * Note: We can safely do this boot-task even if we are
556 * already mounted RW (e.g. boot from single user mode).
557 * In that case this will effectively be a no-op.
560 /* Hand the rest of the process over to apfs_boot_util */
561 bootstrap_apfs(MOUNT_PHASE_2
);
563 /* upgrade mount "/" read-write */
564 rval
= upgrade_mount("/", MNT_UPDATE
, options
);
567 #endif /* TARGET_OS_OSX */
569 print_mount(vfslist
);
576 if (init_flags
& MNT_UPDATE
) {
578 rval
= upgrade_mount (*argv
, init_flags
, options
);
583 if ((fs
= getfsfile(*argv
)) == NULL
&&
584 (fs
= getfsspec(*argv
)) == NULL
)
585 errx(errno_or_sysexit(errno
, -1),
586 "%s: unknown special file or file system.",
588 if (BADTYPE(fs
->fs_type
))
589 errx(errno_or_sysexit(EINVAL
, EX_DATAERR
),
590 "%s has unknown file system type.",
592 if (!strcmp(fs
->fs_vfstype
, "nfs")) {
593 if (hasopt(fs
->fs_mntops
, "net"))
594 errx(errno_or_sysexit(EINVAL
, EX_DATAERR
),
595 "%s is owned by the automounter.",
597 if (ismounted(fs
->fs_spec
, fs
->fs_file
, NULL
))
598 errx(errno_or_sysexit(EALREADY
, EX_CONFIG
),
599 "%s is already mounted at %s.",
600 fs
->fs_spec
, fs
->fs_file
);
603 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
604 if (!strcmp(fs
->fs_spec
, RAMDISK_FS_SPEC
)) {
606 fprintf(stdout
, "Found a ramdisk entry\n");
608 rval
= create_mount_ramdisk(fs
, init_flags
, options
);
612 rval
= mountfs(fs
->fs_vfstype
, fs
->fs_spec
, fs
->fs_file
,
613 init_flags
, options
, fs
->fs_mntops
);
617 * If -t flag has not been specified, and spec contains a ':'
618 * then assume that an NFS filesystem is being specified.
620 if (vfslist
== NULL
&& strchr(argv
[0], ':') != NULL
) {
622 /* check if already mounted */
623 if (ismounted(argv
[0], argv
[1], NULL
))
624 errx(errno_or_sysexit(EALREADY
, EX_CONFIG
),
625 "%s is already mounted at %s.",
629 /* If we have both a devnode and a pathname, and an update mount was requested,
630 * then figure out where the devnode is mounted. We will need to run
631 * an update mount on its path. It wouldn't make sense to do an
632 * update mount on a path other than the one it's already using.
634 * XXX: Should we implement the same workaround for updating the
635 * root file system at boot time?
637 if (init_flags
& MNT_UPDATE
) {
638 if ((mntbuf
= getmntpt(*argv
)) == NULL
)
639 errx(errno_or_sysexit(errno
? errno
: ENOENT
, -1),
640 "unknown special file or file system %s.",
642 rval
= mountfs(mntbuf
->f_fstypename
, mntbuf
->f_mntfromname
,
643 mntbuf
->f_mntonname
, init_flags
, options
, 0);
647 * If update mount not requested, then go with the vfstype and arguments
648 * specified. If no vfstype specified, then error out.
650 if (vfstype
== NULL
) {
651 errx (errno_or_sysexit(EINVAL
, EX_USAGE
),
652 "You must specify a filesystem type with -t.");
654 rval
= mountfs(vfstype
,
655 argv
[0], argv
[1], init_flags
, options
, NULL
);
667 upgrade_mount (const char *mountpt
, int init_flags
, char *options
) {
669 struct statfs
*mntbuf
= NULL
;
670 const char *mntfromname
= NULL
;
671 struct fstab
*fs
= NULL
;
673 if ((mntbuf
= getmntpt(mountpt
)) == NULL
) {
674 errx(errno_or_sysexit(errno
, -1),
675 "unknown special file or file system %s.",
680 * Handle the special case of upgrading the root file
681 * system from read-only to read/write. The root file
682 * system was originally mounted with a "mount from" name
683 * of "root_device". The getfsfile("/") returns non-
684 * NULL at this point, with fs_spec set to the true
685 * path to the root device (regardless of what either the real
686 * or synthesized /etc/fstab contained).
688 mntfromname
= mntbuf
->f_mntfromname
;
689 if (strchr(mntfromname
, '/') == NULL
) {
690 fs
= getfsfile(mntbuf
->f_mntonname
);
692 mntfromname
= fs
->fs_spec
;
696 * Handle the special case of upgrading a content protected
697 * file system from read-only to read/write. While our caller
698 * is nominally required to pass the protect option to maintain
699 * content protection, the kernel requires it anyway, so just add it
702 if (mntbuf
->f_flags
& MNT_CPROTECT
) {
703 init_flags
|= MNT_CPROTECT
;
707 /* Do the update mount */
708 return mountfs(mntbuf
->f_fstypename
, mntfromname
,
709 mntbuf
->f_mntonname
, init_flags
, options
, 0);
714 hasopt(mntopts
, option
)
715 const char *mntopts
, *option
;
720 if (option
[0] == 'n' && option
[1] == 'o') {
725 optbuf
= strdup(mntopts
);
727 for (opt
= optbuf
; (opt
= strtok(opt
, ",")) != NULL
; opt
= NULL
) {
728 if (opt
[0] == 'n' && opt
[1] == 'o') {
729 if (!strcasecmp(opt
+ 2, option
))
731 } else if (!strcasecmp(opt
, option
))
739 ismounted(const char *fs_spec
, const char *fs_file
, long *flags
)
742 struct statfs
*mntbuf
;
744 if ((mntsize
= getmntinfo(&mntbuf
, MNT_NOWAIT
)) == 0)
745 err(errno_or_sysexit(errno
, -1), "getmntinfo");
746 for (i
= 0; i
< mntsize
; i
++) {
747 if (strcmp(mntbuf
[i
].f_mntfromname
, fs_spec
))
749 if (strcmp(mntbuf
[i
].f_mntonname
, fs_file
))
752 *flags
= mntbuf
[i
].f_flags
;
761 struct statfs
*mntbuf
;
763 if ((mntbuf
= getmntpt("/")) == NULL
) {
764 errx(errno_or_sysexit(errno
, -1),
765 "failed to lookup root file system");
768 return (strcmp(mntbuf
->f_fstypename
, "apfs") == 0);
773 /* use sysctl to query kernel */
774 uint32_t is_rosp
= 0;
775 size_t rospsize
= sizeof(is_rosp
);
776 int err
= sysctlbyname ("vfs.generic.apfs.rosp", &is_rosp
, &rospsize
, NULL
, NULL
);
778 if (!err
&& is_rosp
) {
784 #endif /* TARGET_OS_OSX */
786 // prints currently mounted filesystems
788 print_mount(const char **vfslist
)
790 struct statfs
*mntbuf
;
793 if ( (mntsize
= getmntinfo(&mntbuf
, MNT_NOWAIT
)) == 0 )
794 err(errno_or_sysexit(errno
, -1), "getmntinfo");
795 for (int i
= 0; i
< mntsize
; i
++) {
796 if ( checkvfsname(mntbuf
[i
].f_fstypename
, vfslist
) )
802 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
804 verify_executable_file_existence (char *path
)
806 return _verify_file_flags(path
, F_OK
| X_OK
);
810 verify_file_existence (char *path
)
812 return _verify_file_flags(path
, F_OK
);
816 _verify_file_flags (char *path
, int flags
)
819 if ( access(path
, flags
) ) {
820 fprintf(stderr
, "Failed access check for %s with issue %s\n", path
, strerror(errno
));
827 preflight_create_mount_ramdisk (char *mnt_opts
, size_t *ramdisk_size
, char *template)
829 char *special_ramdisk_params
;
831 if ( mnt_opts
== NULL
) {
832 fprintf(stderr
, "No mnt_opts provided to ramdisk preflight.\n");
836 if ( verify_executable_file_existence(HDIK_PATH
) != 0 ) {
837 fprintf(stderr
, "Failed to find executable hdik at location %s \n", HDIK_PATH
);
841 special_ramdisk_params
= split_ramdisk_params(mnt_opts
);
842 if ( special_ramdisk_params
== NULL
) {
843 fprintf(stderr
, "Ramdisk fstab not in expected format.\n");
847 if ( ramdisk_size
) {
848 char *ramdisk_size_str
= parse_parameter_for_token(special_ramdisk_params
, RAMDISK_SIZE_TOK
);
850 if ( ramdisk_size_str
!= NULL
) {
851 *ramdisk_size
= atoi(ramdisk_size_str
);
852 free(ramdisk_size_str
);
855 if ( *ramdisk_size
== 0 ) {
856 fprintf(stderr
, "Unexpected ramdisk size %zu\n", *ramdisk_size
);
862 char *template_str
= parse_parameter_for_token(special_ramdisk_params
, RAMDISK_TPLT_TOK
);
863 if (template_str
!= NULL
) {
864 strlcpy(template, template_str
, PATH_MAX
);
868 if ( template == NULL
) {
869 fprintf(stderr
, "Ramdisk template path not found\n");
877 #endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */
880 * Helper function that posix_spawn a child process
881 * as defined by command_argv[0].
882 * If `output` is non-null, then the command's stdout will be read
884 * If `rc` is non-null, then the command's return code will be set
886 * If `signal_no` is non-null, then if the command is signaled, the
887 * signal number will be set there.
890 * This function returns
891 * -1, if there's an internal error. errno will be set
892 * 0, if command exit normally with 0 as return code
893 * 1, if command exit abnormally or with a non-zero return code
896 run_command(char **command_argv
, char *output
, int *rc
, int *signal_no
)
899 int faulting_errno
= 0;
901 int internal_result
= -1;
903 posix_spawn_file_actions_t actions
= NULL
;
904 int output_pipe
[2] = {-1, -1};
905 char *command_out
= NULL
;
908 if ( !command_argv
) {
909 fprintf(stderr
, "command_argv is NULL\n");
914 if ( pipe(output_pipe
) ) {
915 fprintf(stderr
, "Failed to create pipe for command output: %d (%s)\n", errno
, strerror(errno
));
919 if ( (internal_result
= posix_spawn_file_actions_init(&actions
)) != 0 ) {
920 errno
= internal_result
;
921 fprintf(stderr
, "posix_spawn_file_actions_init failed: %d (%s)\n", errno
, strerror(errno
));
925 if ( (internal_result
= posix_spawn_file_actions_addclose(&actions
, output_pipe
[0])) != 0 ) {
926 errno
= internal_result
;
927 fprintf(stderr
, "posix_spawn_file_actions_addclose output_pipe[0] failed: %d (%s)\n", errno
, strerror(errno
));
931 if ( (internal_result
= posix_spawn_file_actions_adddup2(&actions
, output_pipe
[1], STDOUT_FILENO
)) != 0 ) {
932 errno
= internal_result
;
933 fprintf(stderr
, "posix_spawn_file_actions_adddup2 output_pipe[1] failed: %d (%s)\n", errno
, strerror(errno
));
937 if ( (internal_result
= posix_spawn_file_actions_addclose(&actions
, output_pipe
[1])) != 0 ) {
938 errno
= internal_result
;
939 fprintf(stderr
, "posix_spawn_file_actions_addclose output_pipe[1] failed: %d (%s)\n", errno
, strerror(errno
));
944 fprintf(stdout
, "Executing command: ");
945 for (char **command_segment
= command_argv
; *command_segment
; command_segment
++) {
946 fprintf(stdout
, "%s ", *command_segment
);
948 fprintf(stdout
, "\n");
951 if ( (internal_result
= posix_spawn(&pid
, command_argv
[0], &actions
, NULL
, command_argv
, environ
)) != 0 ) {
952 errno
= internal_result
;
953 fprintf(stderr
, "posix_spawn failed: %d (%s)\n", errno
, strerror(errno
));
957 // Close out our side of the pipe
958 close(output_pipe
[1]);
961 // If caller specified the output buffer, we'll use that
962 // Otherwise allocate a buffer and capture the output ourselves for verbose logging
963 if ( output
!= NULL
) {
964 command_out
= output
;
966 command_out
= calloc(COMMAND_OUTPUT_MAX
, sizeof(char));
968 fprintf(stderr
, "calloc failed: %d (%s)\n", errno
, strerror(errno
));
973 stream
= fdopen(output_pipe
[0], "r");
975 fprintf(stderr
, "fdopen failed: %d (%s)\n", errno
, strerror(errno
));
982 while ( (line
= fgetln(stream
, &length
)) && (count
< COMMAND_OUTPUT_MAX
- length
- 1) ) {
983 strncat(command_out
, line
, length
);
987 if ( ferror(stream
) ) {
988 fprintf(stderr
, "fgetln failed: %d (%s)\n", errno
, strerror(errno
));
992 if ( fclose(stream
) ) {
993 fprintf(stderr
, "fclose failed: %d (%s)\n", errno
, strerror(errno
));
998 close(output_pipe
[0]);
1001 while ( waitpid(pid
, &status
, 0) < 0 ) {
1002 if (errno
== EINTR
) {
1005 fprintf(stderr
, "waitpid failed: %d (%s)\n", errno
, strerror(errno
));
1010 fprintf(stdout
, "Command output:\n%s\n", command_out
);
1013 if ( WIFEXITED(status
) ) {
1014 int exit_status
= WEXITSTATUS(status
);
1015 if (rc
) *rc
= exit_status
;
1016 if (signal_no
) *signal_no
= 0;
1018 if (exit_status
!= 0) {
1020 fprintf(stderr
, "Command failed: %d\n", exit_status
);
1025 if ( WIFSIGNALED(status
) ) {
1027 if (signal_no
) *signal_no
= WTERMSIG(status
);
1030 fprintf(stderr
, "Command signaled: %d\n", WTERMSIG(status
));
1036 // we don't care much about the errno set by the clean up routine
1037 // so save the errno here and return to caller
1038 faulting_errno
= errno
;
1041 posix_spawn_file_actions_destroy(&actions
);
1048 if ( output_pipe
[0] >= 0 ) {
1049 close(output_pipe
[0]);
1052 if ( output_pipe
[1] >= 0 ) {
1053 close(output_pipe
[1]);
1056 if ( !output
&& command_out
) {
1060 errno
= faulting_errno
;
1064 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1065 // Helper function that truncates whitespaces
1067 truncate_whitespace(char* str
)
1069 size_t idx
= strcspn(str
, " \n");
1075 // Creates a new unmounted ramdisk of size device_size
1077 attach_device(size_t device_size
, char* deviceOut
)
1079 int return_val
= -1;
1080 char ram_define
[PATH_MAX
];
1081 snprintf(ram_define
, sizeof(ram_define
), "ram://%zu", device_size
);
1083 char *command
[4] = { HDIK_PATH
, "-nomount", ram_define
, NULL
};
1085 int status
= run_command(command
, deviceOut
, &return_val
, NULL
);
1086 if ( status
== 1 ) {
1087 fprintf(stderr
, "Failed to create ramdisk. HDIK returned %d.\n", return_val
);
1088 exit(errno_or_sysexit(errno
, -1));
1089 } else if (status
!= 0) {
1090 fprintf(stderr
, "Failed to execute command %s\n", command
[0]);
1091 exit(errno_or_sysexit(errno
, -1));
1094 truncate_whitespace(deviceOut
);
1098 // Creates the partition table directly through MediaKit.
1100 create_partition_table(size_t partition_size
, char *device
)
1104 MKMediaRef gpt_ref
= NULL
;
1105 CFMutableArrayRef schemes
= NULL
;
1106 CFMutableArrayRef partitionArray
= NULL
;
1107 CFDictionaryRef partition
= NULL
;
1108 CFMutableDictionaryRef options
= NULL
;
1109 CFMutableDictionaryRef layout
= NULL
;
1110 CFMutableDictionaryRef media
= NULL
;
1111 CFMutableDictionaryRef map
= NULL
;
1113 layout
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1114 options
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1116 if (!layout
|| !options
) {
1117 fprintf(stderr
, "Failed to create necessary CFDictionaries\n");
1122 CFDictionarySetValue(options
, kMKMediaPropertyWritableKey
, kCFBooleanTrue
);
1124 gpt_ref
= MKMediaCreateWithPath(kCFAllocatorDefault
, device
, options
, &err
);
1128 MKStatus mediaErr
= 0;
1129 partition
= MKCFBuildPartition(PMGPTTYPE
, apple_apfs
, CFSTR(EDTVolumeFSType
), CFSTR(RAMDISK_FS_SPEC
), 0, RAMDISK_BLCK_OFFSET
, &mediaErr
, NULL
);
1132 fprintf(stderr
, "Failed to create partition with err %d\n", mediaErr
);
1137 partitionArray
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1139 if (!partitionArray
) {
1140 fprintf(stderr
, "Failed to create partitionArray\n");
1142 CFRelease(partition
);
1146 CFArrayAppendValue(partitionArray
, partition
);
1147 CFRelease(partition
);
1149 CFDictionaryAddValue(layout
, CFSTR(MK_PARTITIONS_KEY
), partitionArray
);
1150 CFRelease(partitionArray
);
1153 media
= MKCFCreateMedia(&schemes
, &mediaErr
);
1156 fprintf(stderr
, "Failed to create Schemes with error %d\n", mediaErr
);
1161 map
= MKCFCreateMap(PMGPTTYPE
, schemes
, layout
, NULL
, NULL
, NULL
, NULL
, NULL
, gpt_ref
, &mediaErr
);
1163 fprintf(stderr
, "Failed to create map with error %d\n", mediaErr
);
1168 err
= MKCFWriteMedia(media
, layout
, NULL
, NULL
, gpt_ref
);
1170 fprintf(stderr
, "Failed to WriteMedia with error %d\n", err
);
1175 fprintf(stderr
, "Failed to create gpt_ref with error %d\n", err
);
1180 fprintf(stderr
, "Releasing MediaKit objects\n");
1186 MKCFDisposeMedia(media
);
1200 // Triggers newfs_apfs for the target device
1202 construct_apfs_volume(char *mounted_device_name
)
1204 int return_val
= -1;
1206 char *command
[5] = { "/sbin/newfs_apfs", "-v", "Var", mounted_device_name
, NULL
};
1208 status
= run_command(command
, NULL
, &return_val
, NULL
);
1209 if ( status
>= 0 ) {
1212 fprintf(stderr
, "Failed to execute command %s\n", command
[0]);
1213 errno_or_sysexit(errno
, -1);
1216 // shouldn't reach here. This is to satisfy the compiler
1220 // unmounts device at location
1222 unmount_location(char *mount_point
)
1224 int return_val
= -1;
1226 char *command
[4] = { "/sbin/umount", "-f", mount_point
, NULL
};
1228 status
= run_command(command
, NULL
, &return_val
, NULL
);
1229 if ( status
>= 0 ) {
1232 fprintf(stderr
, "Failed to execute command %s\n", command
[0]);
1233 return errno_or_sysexit(errno
, -1);
1237 // The mnt_opts for fstab are standard across the different
1238 // mount_fs implementations. To create and mount an ephemeral
1239 // filesystem, it is necessary to provide additional non-standard
1240 // values in filesystem definition - mainly size and location of
1242 // The fstab definition for a ramdisk fs requires two new parameters:
1243 // 'size=%zu' and 'template=%s'. To keep the fstab structure
1244 // consistent with that of other filesystem types, these
1245 // parameters are appended at the end of the mnt_opts string.
1246 // It is necessary to split the mnt_opts into two strings, the
1247 // standard mountfs parameters that are used in the fs-specifnc mount
1248 // and the ramdisk definition parameters.
1250 split_ramdisk_params(char *opts
)
1253 char* target_str
= NULL
;
1254 char* size_tok
= RAMDISK_SIZE_TOK
;
1255 char* tplt_tok
= RAMDISK_TPLT_TOK
;
1256 char* optbuf
= NULL
;
1257 size_t size_tok_len
= strlen(size_tok
);
1258 size_t tplt_tok_len
= strlen(tplt_tok
);
1260 optbuf
= strdup(opts
);
1261 for (opt
= optbuf
; (opt
= strtok(opt
, ",")) != NULL
; opt
= NULL
) {
1262 size_t opt_len
= strlen(opt
);
1263 if ( (opt_len
> size_tok_len
&& !strncmp(size_tok
, opt
, size_tok_len
) ) ||
1264 (opt_len
> tplt_tok_len
&& !strncmp(tplt_tok
, opt
, tplt_tok_len
) ) ) {
1265 size_t start_index
= opt
- optbuf
;
1266 target_str
= opts
+ start_index
;
1267 opts
[start_index
- 1 ] = '\0'; // Break original into two strings.
1275 // returns string for the parameter after the '=' in the search_string
1276 // part of the special ramdisk parameters
1278 parse_parameter_for_token(char * opts
, char * search_string
)
1280 char *return_str
= NULL
;
1281 char *tmp_str
= NULL
;
1282 char *target_str
= strstr(opts
, search_string
);
1283 size_t len
= strlen(search_string
);
1285 if ( target_str
&& strlen(target_str
) > len
) {
1286 tmp_str
= target_str
+ len
;
1287 size_t idx
= strcspn(tmp_str
, ",\0");
1288 if ( idx
!= 0 && (idx
< MAXPATHLEN
) ) {
1289 return_str
= calloc(1, idx
+1); //for null terminator
1290 strncpy(return_str
, tmp_str
, idx
);
1297 static int _copyfile_status(int what
, int stage
, copyfile_state_t state
, const char * src
, const char * dst
, void * ctx
) {
1299 if (verbose
&& stage
== COPYFILE_START
) {
1300 if (what
== COPYFILE_RECURSE_FILE
) {
1301 fprintf(stderr
, "Copying %s -> %s\n", src
, dst
);
1302 } else if (what
== COPYFILE_RECURSE_DIR
) {
1303 fprintf(stderr
, "Creating %s/\n", dst
);
1307 return COPYFILE_CONTINUE
;
1311 // returns 0 upon success and a valid sysexit or errno code upon failure
1313 create_mount_ramdisk(struct fstab
*fs
, int init_flags
, char *options
)
1315 int default_flags
= init_flags
;
1316 int mount_return
= 0;
1317 char ramdisk_partition
[PATH_MAX
] = { 0 };
1318 char ramdisk_volume
[PATH_MAX
] = { 0 };
1319 char ramdisk_container
[PATH_MAX
] = { 0 };
1320 char seed_location
[PATH_MAX
] = { 0 };
1321 char *mnt_point
= RAMDISK_TMP_MOUNT
; // intermediate
1322 char *target_dir
= fs
->fs_file
; // target
1323 size_t ram_size
= 0;
1325 if ( verify_file_existence(mnt_point
) != 0 ) {
1327 fprintf(stderr
, "Default mount %s is not available. Using backup %s.\n", mnt_point
, RAMDISK_BCK_MOUNT
);
1329 mnt_point
= RAMDISK_BCK_MOUNT
;
1330 if ( verify_file_existence(mnt_point
) != 0 ) {
1331 fprintf(stderr
, "Mountpoints not available. Exiting.\n");
1336 if ( preflight_create_mount_ramdisk(fs
->fs_mntops
, &ram_size
, seed_location
) != 0 ) {
1337 fprintf(stderr
, "Failed ramdisk preflight. Exiting.\n");
1342 fprintf(stdout
, "Attaching device of size %zu\n", ram_size
);
1345 if( attach_device(ram_size
, ramdisk_partition
) != 0 ){
1346 fprintf(stderr
, "Failed to attach the ramdisk.\n");
1347 exit(errno_or_sysexit(ECHILD
, -1));
1351 fprintf(stdout
, "Creating partition table for device %s \n", ramdisk_partition
);
1354 if ( create_partition_table(ram_size
, ramdisk_partition
) !=0 ) {
1355 fprintf(stderr
, "Failed to create partition table.\n");
1356 exit(errno_or_sysexit(ECHILD
, -1));
1359 snprintf(ramdisk_container
, sizeof(ramdisk_container
), "%ss1", ramdisk_partition
);
1362 fprintf(stdout
, "Creating apfs volume on partition %s\n", ramdisk_container
);
1365 if ( construct_apfs_volume(ramdisk_container
) != 0 ) {
1366 fprintf(stderr
, "Failed to construct the apfs volume on the ramdisk.\n");
1367 exit(errno_or_sysexit(ECHILD
, -1));
1370 snprintf(ramdisk_volume
, sizeof(ramdisk_volume
), "%ss1", ramdisk_container
);
1372 if ( verify_file_existence(ramdisk_volume
) != 0 ) {
1373 fprintf(stderr
, "Failed to verify %s with issue %s\n", ramdisk_volume
, strerror(errno
));
1374 exit(errno_or_sysexit(errno
, -1));
1377 // Mount volume to RAMDISK_TMP_MOUNT
1379 fprintf(stdout
, "Mounting to tmp location %s\n", mnt_point
);
1382 mount_return
= mountfs(EDTVolumeFSType
, ramdisk_volume
, mnt_point
, default_flags
, NULL
, fs
->fs_mntops
);
1383 if ( mount_return
> 0 ) {
1384 fprintf(stderr
, "Initial mount to %s failed with %d\n", mnt_point
, mount_return
);
1385 exit(errno_or_sysexit(errno
, -1));
1388 // ditto contents of RAMDISK_TMP_MOUNT to /private/var
1389 copyfile_state_t state
= copyfile_state_alloc();
1390 copyfile_state_set(state
, COPYFILE_STATE_STATUS_CB
, _copyfile_status
);
1391 if( copyfile(seed_location
, mnt_point
, state
, COPYFILE_ALL
| COPYFILE_RECURSIVE
) < 0 ) {
1392 fprintf(stderr
, "Failed to copy contents from %s to %s with error: %s\n", seed_location
, mnt_point
, strerror(errno
));
1393 exit(errno_or_sysexit(errno
, -1));
1395 copyfile_state_free(state
);
1397 // unount RAMDISK_TMP_MOUNT
1398 if( unmount_location(mnt_point
) != 0 ){
1399 fprintf(stderr
, "Failed to unmount device mounted at %s.\n", mnt_point
);
1400 exit(errno_or_sysexit(ECHILD
, -1));
1404 fprintf(stdout
, "Mounting apfs volume %s to %s\n", ramdisk_volume
, target_dir
);
1407 mount_return
= mountfs(EDTVolumeFSType
, ramdisk_volume
, target_dir
, default_flags
, options
, fs
->fs_mntops
);
1408 if ( mount_return
> 0 ) {
1409 fprintf(stderr
, "Followup mount to %s failed with %d\n", target_dir
, mount_return
);
1410 exit(errno_or_sysexit(errno
, -1));
1413 // Verify contents in stdout
1418 return mount_return
;
1420 #endif // (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1422 // returns 0 upon success and a valid sysexit or errno code upon failure
1424 mountfs(const char *vfstype
, const char *fs_spec
, const char *fs_file
, int flags
,
1425 const char *options
, const char *mntopts
)
1427 /* List of directories containing mount_xxx subcommands. */
1428 static const char *edirs
[] = {
1433 static const char *bdirs
[] = {
1438 const char *argv
[100], **edir
, **bdir
;
1441 int argc
, i
, status
;
1442 char *optbuf
, execname
[MAXPATHLEN
+ 1], mntpath
[MAXPATHLEN
];
1444 if (realpath(fs_file
, mntpath
) == NULL
) {
1445 /* Attempt to create missing mountpoints on Data volume */
1446 if ((passno
== NONROOT_PASSNO
) &&
1447 (!strncmp(mntpath
, PLATFORM_DATA_VOLUME_MOUNT_POINT
,
1448 MIN(strlen(mntpath
), strlen(PLATFORM_DATA_VOLUME_MOUNT_POINT
))))) {
1449 if (mkdir(mntpath
, S_IRWXU
)) {
1450 warn("mkdir %s", mntpath
);
1451 return (errno_or_sysexit(errno
, -1));
1454 warn("realpath %s", mntpath
);
1455 return (errno_or_sysexit(errno
, -1));
1461 if (mntopts
== NULL
)
1463 if (options
== NULL
) {
1464 if (*mntopts
== '\0') {
1471 optbuf
= catopt(strdup(mntopts
), options
);
1473 if ((strcmp(fs_file
, "/") == 0) && !(flags
& MNT_UNION
))
1474 flags
|= MNT_UPDATE
;
1475 if (flags
& MNT_FORCE
)
1476 optbuf
= catopt(optbuf
, "force");
1477 if (flags
& MNT_RDONLY
)
1478 optbuf
= catopt(optbuf
, "ro");
1479 if (flags
& MNT_UPDATE
)
1480 optbuf
= catopt(optbuf
, "update");
1481 if (flags
& MNT_DONTBROWSE
)
1482 optbuf
= catopt(optbuf
, "nobrowse");
1483 if (flags
& MNT_CPROTECT
)
1484 optbuf
= catopt(optbuf
, "protect");
1488 argv
[argc
++] = vfstype
;
1489 mangle(optbuf
, &argc
, argv
);
1490 argv
[argc
++] = fs_spec
;
1491 argv
[argc
++] = fs_file
;
1495 (void)printf("exec: mount_%s", vfstype
);
1496 for (i
= 1; i
< argc
; i
++)
1497 (void)printf(" %s", argv
[i
]);
1502 switch (pid
= fork()) {
1503 case -1: /* Error. */
1506 return (errno_or_sysexit(errno
, EX_OSERR
));
1507 case 0: /* Child. */
1508 /* Go find an executable. */
1511 (void)snprintf(execname
, sizeof(execname
),
1512 "%s/mount_%s", *edir
, vfstype
);
1515 execv(execname
, (char * const *)argv
);
1516 if (errno
!= ENOENT
)
1517 warn("exec %s for %s", execname
, fs_file
);
1518 } while (*++edir
!= NULL
);
1522 /* Special case file system bundle executable path */
1523 (void)snprintf(execname
, sizeof(execname
),
1524 "%s/%s.fs/%s/mount_%s", *bdir
,
1525 vfstype
, _PATH_FSBNDLBIN
, vfstype
);
1528 execv(execname
, (char * const *)argv
);
1529 if (errno
!= ENOENT
)
1530 warn("exec %s for %s", execname
, fs_file
);
1531 } while (*++bdir
!= NULL
);
1533 if (errno
== ENOENT
) {
1534 warn("exec %s for %s", execname
, fs_file
);
1535 return (errno_or_sysexit(errno
, EX_OSFILE
));
1537 exit(errno_or_sysexit(errno
, -1));
1539 default: /* Parent. */
1542 if (waitpid(pid
, &status
, 0) < 0) {
1544 return (errno_or_sysexit(errno
, -1));
1547 if (WIFEXITED(status
)) {
1548 if (WEXITSTATUS(status
) != 0) {
1549 warnx("%s failed with %d", fs_file
, WEXITSTATUS(status
));
1550 return (errno_or_sysexit(EINTR
, WEXITSTATUS(status
)));
1552 } else if (WIFSIGNALED(status
)) {
1553 warnx("%s: %s", fs_file
, sys_siglist
[WTERMSIG(status
)]);
1554 return (errno_or_sysexit(EINTR
, EX_UNAVAILABLE
));
1558 if (statfs(fs_file
, &sf
) < 0) {
1559 warn("statfs %s", fs_file
);
1560 return (errno_or_sysexit(errno
, -1));
1571 is_sealed(const char *mntpt
)
1575 vol_capabilities_attr_t vol_cap
;
1578 struct attrlist vol_attr_list
= {
1579 .bitmapcount
= ATTR_BIT_MAP_COUNT
,
1580 .volattr
= ATTR_VOL_CAPABILITIES
1583 if (!getattrlist(mntpt
, &vol_attr_list
, &vol_attrs
, sizeof(vol_attrs
), 0) &&
1584 vol_attrs
.vol_cap
.valid
[VOL_CAPABILITIES_FORMAT
] & VOL_CAP_FMT_SEALED
) {
1585 return (vol_attrs
.vol_cap
.capabilities
[VOL_CAPABILITIES_FORMAT
] & VOL_CAP_FMT_SEALED
);
1598 (void)printf("%s on %s (%s", sfp
->f_mntfromname
, sfp
->f_mntonname
,
1601 if (is_sealed(sfp
->f_mntonname
))
1602 (void)printf(", sealed");
1603 flags
= sfp
->f_flags
& MNT_VISFLAGMASK
;
1604 for (o
= optnames
; flags
&& o
->o_opt
; o
++)
1605 if (flags
& o
->o_opt
) {
1606 (void)printf(", %s", o
->o_name
);
1610 (void)printf(", mounted by ");
1611 if ((pw
= getpwuid(sfp
->f_owner
)) != NULL
)
1612 (void)printf("%s", pw
->pw_name
);
1614 (void)printf("%d", sfp
->f_owner
);
1616 (void)printf(")\n");
1623 struct statfs
*mntbuf
;
1626 mntsize
= getmntinfo(&mntbuf
, MNT_NOWAIT
);
1627 for (i
= 0; i
< mntsize
; i
++) {
1628 if (strcmp(mntbuf
[i
].f_mntfromname
, name
) == 0 ||
1629 strcmp(mntbuf
[i
].f_mntonname
, name
) == 0)
1630 return (&mntbuf
[i
]);
1644 i
= strlen(s0
) + strlen(s1
) + 1 + 1;
1645 if ((cp
= malloc(i
)) == NULL
)
1646 err(errno_or_sysexit(errno
, EX_TEMPFAIL
),
1647 "failed to allocate memory for arguments");
1648 (void)snprintf(cp
, i
, "%s,%s", s0
, s1
);
1658 mangle(options
, argcp
, argv
)
1667 for (s
= options
; (p
= strsep(&s
, ",")) != NULL
;)
1677 argv
[argc
++] = "-o";
1689 (void)fprintf(stderr
,
1690 "usage: mount %s %s\n mount %s\n mount %s\n",
1691 "[-dfruvw] [-o options] [-t external_type]",
1692 "special mount_point",
1693 "[-adfruvw] [-t external_type]",
1694 "[-dfruvw] special | mount_point");
1695 exit(errno_or_sysexit(EINVAL
, EX_USAGE
));