]> git.cameronkatri.com Git - apple_cmds.git/blob - diskdev_cmds/mount.tproj/mount.c
libtelnet-13
[apple_cmds.git] / diskdev_cmds / mount.tproj / mount.c
1 /*
2 * Copyright (c) 1999-2020 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*-
24 * Copyright (c) 1980, 1989, 1993, 1994
25 * The Regents of the University of California. All rights reserved.
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
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.
42 *
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
53 * SUCH DAMAGE.
54 */
55
56 #include <sys/param.h>
57 #include <sys/mount.h>
58 #include <sys/wait.h>
59 #include <sys/stat.h>
60 #include <fcntl.h>
61 #include <System/sys/reason.h>
62 #include <err.h>
63 #include <os/errno.h>
64 #include <os/bsd.h>
65 #include <os/variant_private.h>
66 #include <fstab.h>
67 #include <pwd.h>
68 #include <signal.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include <unistd.h>
73 #include <TargetConditionals.h>
74 #include <sysexits.h>
75 #include <sys/sysctl.h>
76 #include <APFS/APFS.h>
77 #include <APFS/APFSConstants.h>
78 #include <pthread.h>
79 #include <spawn.h>
80 #include <crt_externs.h>
81
82 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
83 #include <paths.h>
84 #include <sys/socket.h>
85 #include <sys/queue.h>
86 #include <sys/wait.h>
87 #include <sys/param.h>
88 #include <sys/cdefs.h>
89
90 /* Some APFS specific goop */
91 #include <copyfile.h>
92 #include <MediaKit/MKMedia.h>
93 #include <MediaKit/MKMediaAccess.h>
94 #include <MediaKit/GPTTypes.h>
95 #endif
96
97 #if TARGET_OS_OSX
98 // To unmount the BaseSystem disk image.
99 #include <paths.h>
100 #include <IOKit/IOBSD.h>
101 #include <IOKit/IOKitLib.h>
102 #include <IOKit/storage/IOMedia.h>
103 #endif
104
105 #include "mount_flags.h"
106 #include "edt_fstab.h"
107 #include "pathnames.h"
108 #include "fsck.h"
109
110 #if TARGET_OS_OSX
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"
115 #else
116 #define APFS_BOOT_UTIL_PATH "/System/Library/Filesystems/apfs.fs/apfs_boot_util"
117 #define PLATFORM_DATA_VOLUME_MOUNT_POINT "/private/var"
118 #endif
119
120 #define environ (*_NSGetEnviron())
121 #define COMMAND_OUTPUT_MAX 1024
122
123 int debug;
124 int verbose;
125 int bootstrap_macos = 0;
126 int passno = 0;
127
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 *));
132 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));
137
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);
143
144 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
145
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);
157
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"
164
165 #endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */
166
167 //pull in the optnames array from the mount_flags.c file
168 extern mountopt_t optnames[];
169 #if TARGET_OS_OSX
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);
174
175 /*
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
178 */
179 static int ret_errno = 0;
180 static inline int
181 errno_or_sysexit(int err, int sysexit)
182 {
183 if (sysexit == -1) {
184 sysexit = sysexit_np(err);
185 }
186 return (ret_errno ? err : sysexit);
187 }
188
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"
193
194 #define BOOT_MANIFEST_HASH_LEN 256
195
196 typedef struct bind_mount {
197 char *bm_mnt_prefix;
198 char *bm_mnt_to;
199 bool bm_mandatory;
200 } bind_mount_t;
201
202 static void
203 do_bindfs_mount(char *from, const char *to, bool required)
204 {
205 struct statfs sfs;
206 uint32_t mnt_flags = MNT_RDONLY | MNT_NODEV | MNT_NOSUID | MNT_DONTBROWSE;
207 int err = 0;
208
209 if (debug) {
210 printf("call: mount %s %s %x %s", BINDFS_MOUNT_TYPE, to, mnt_flags, from);
211 return;
212 }
213
214 if ((statfs(from, &sfs) == 0) &&
215 (strncmp(sfs.f_fstypename, BINDFS_MOUNT_TYPE, sizeof(BINDFS_MOUNT_TYPE)) == 0)) {
216 return;
217 }
218
219 err = mount(BINDFS_MOUNT_TYPE, to, mnt_flags, from);
220 if (err) {
221 if ((errno == ENOENT) && !required) {
222 err = 0;
223 } else {
224 errx(errno_or_sysexit(errno, -1), "failed to mount %s -> %s - %s(%d)",
225 from, to, strerror(errno), errno);
226 }
227 }
228 }
229
230 static void
231 setup_preboot_mounts(int pass)
232 {
233 int err = 0;
234 const char *mnt_to;
235 char mnt_from[MAXPATHLEN], boot_manifest_hash[BOOT_MANIFEST_HASH_LEN];
236
237 err = get_boot_manifest_hash(boot_manifest_hash, sizeof(boot_manifest_hash));
238 if (err) {
239 errx(errno_or_sysexit(err, -1), "failed to get boot manifest hash - %s",
240 strerror(err));
241 }
242
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}
250 };
251
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}
259 };
260
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);
266
267 do_bindfs_mount(mnt_from, mnt_to, preboot_mnts[i].bm_mandatory);
268 }
269 } else {
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);
274
275 do_bindfs_mount(mnt_from, mnt_to, hw_mnts[i].bm_mandatory);
276 }
277 }
278 }
279 #endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */
280
281 /*
282 * mount phases to be used during boot to perform the following operations:
283 * first phase:
284 * TARGET_OS_OSX: (call `apfs_boot_util 1`
285 * TARGET_OS_IPHONE: mount System, Preboot and xART volumes (if present)
286 *
287 * second phase:
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
291 *
292 * For more info on mount phases, see apfs_boot_util.
293 */
294 #define MOUNT_PHASE_1 1 /* first phase */
295 #define MOUNT_PHASE_2 2 /* second phase */
296
297 #define NONFS "nonfs"
298
299 static void
300 bootstrap_apfs(int phase)
301 {
302 char * const apfs_util_argv[] = {
303 APFS_BOOT_UTIL_PATH,
304 ((phase == MOUNT_PHASE_1) ? "1" : ((phase == MOUNT_PHASE_2) ? "2" : NULL)),
305 NULL,
306 };
307 execv(APFS_BOOT_UTIL_PATH, apfs_util_argv);
308 errx(errno_or_sysexit(errno, -1), "apfs_boot_util exec failed");
309 }
310
311 int
312 main(argc, argv)
313 int argc;
314 char * const argv[];
315 {
316 const char **vfslist, *vfstype;
317 struct fstab *fs;
318 struct statfs *mntbuf;
319 int all, ch, init_flags, rval;
320 char *options, *ep;
321 int mount_phase = 0;
322
323 all = init_flags = 0;
324 options = NULL;
325 vfslist = NULL;
326 vfstype = NULL;
327
328 while ((ch = getopt(argc, argv, "headfo:rwt:uvP:")) != EOF)
329 switch (ch) {
330 case 'a':
331 all = 1;
332 break;
333 case 'd':
334 debug = 1;
335 break;
336 case 'f':
337 init_flags |= MNT_FORCE;
338 break;
339 case 'o':
340 if (*optarg) {
341 options = catopt(options, optarg);
342 if (strstr(optarg, "union"))
343 init_flags |= MNT_UNION;
344 }
345 break;
346 case 'r':
347 init_flags |= MNT_RDONLY;
348 break;
349 case 't':
350 if (vfslist != NULL)
351 errx(errno_or_sysexit(EINVAL, EX_USAGE),
352 "only one -t option may be specified.");
353 vfslist = makevfslist(optarg);
354 vfstype = optarg;
355 break;
356 case 'u':
357 init_flags |= MNT_UPDATE;
358 break;
359 case 'v':
360 verbose = 1;
361 break;
362 case 'w':
363 init_flags &= ~MNT_RDONLY;
364 break;
365 case 'e':
366 ret_errno = 1;
367 break;
368 case 'P':
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");
376 }
377 break;
378 case 'h':
379 case '?':
380 default:
381 usage();
382 /* NOTREACHED */
383 }
384 argc -= optind;
385 argv += optind;
386
387 #define BADTYPE(type) \
388 (strcmp(type, FSTAB_RO) && \
389 strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
390
391 // mount boot tasks
392 if (mount_phase != 0) {
393 #if TARGET_OS_OSX
394 bootstrap_macos = 1;
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;
402 }
403 verbose = 1;
404 all = 1;
405 vfslist = makevfslist(NONFS);
406 vfstype = NONFS;
407 #endif /* !TARGET_OS_OSX */
408 }
409
410 rval = 0;
411 switch (argc) {
412 case 0:
413 /*
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
417 */
418 if (all) {
419 int err = 0;
420 long fs_flags = 0;
421
422 if ((setfsent() == 0)) {
423 errx(errno_or_sysexit(errno ? errno : ENXIO, -1),
424 "mount: can't get filesystem checklist");
425 }
426
427 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
428 uint32_t os_env;
429 const char *container_dev = get_boot_container(&os_env);
430 const char *data_vol_dev = get_data_volume();
431 /*
432 * It is a given that the boot container must be present
433 * in order to locate the data volume.
434 *
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.
438 *
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.
443 */
444 if (data_vol_dev) {
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");
451 } else {
452 fprintf(stdout, "mount: data volume missing, but not required in env: %u\n",
453 os_env);
454 }
455 #endif
456
457 while ((fs = getfsent()) != NULL) {
458 int ro_mount = !strcmp(fs->fs_type, FSTAB_RO);
459
460 if (BADTYPE(fs->fs_type))
461 continue;
462 if (checkvfsname(fs->fs_vfstype, vfslist))
463 continue;
464 if (hasopt(fs->fs_mntops, "noauto"))
465 continue;
466 if (!strcmp(fs->fs_vfstype, "nfs")) {
467 if (hasopt(fs->fs_mntops, "net"))
468 continue;
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))
473 continue;
474 }
475
476 /* If this volume is not needed for this pass, skip it. */
477 if (passno && fs->fs_passno != passno)
478 continue;
479
480 /*
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).
489 */
490 if (ismounted(fs->fs_spec, fs->fs_file, &fs_flags) &&
491 (!(fs_flags & MNT_RDONLY) || ro_mount))
492 continue;
493
494 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
495 if (!strcmp(fs->fs_spec, RAMDISK_FS_SPEC)) {
496 if (verbose) {
497 fprintf(stdout, "mount: encountered ramdisk\n");
498 }
499 rval = create_mount_ramdisk(fs, init_flags, options);
500 continue;
501 } else if (fs->fs_passno > ROOT_PASSNO &&
502 !strcmp(fs->fs_vfstype, EDTVolumeFSType) &&
503 !strcmp(fs->fs_type, FSTAB_RW)) {
504
505 /*
506 * Perform media keys migration if this is the data volume
507 * of the main OS environment
508 */
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);
513 if (mig_err) {
514 fprintf(stderr, "mount: failed to migrate Media Keys, error = %x\n", mig_err);
515 } else {
516 fprintf(stdout, "mount: successfully migrated Media Keys\n");
517 }
518 }
519 }
520 #endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */
521
522 if ((err = mountfs(fs->fs_vfstype, fs->fs_spec,
523 fs->fs_file, init_flags, options,
524 fs->fs_mntops)))
525 rval = err;
526 }
527 endfsent();
528
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);
533 }
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);
538 }
539 #endif
540 }
541 else if (bootstrap_macos) {
542 #if TARGET_OS_OSX
543 if (mount_phase == MOUNT_PHASE_1) {
544 if (booted_apfs()) {
545 bootstrap_apfs(MOUNT_PHASE_1);
546 } else {
547 fprintf(stdout, "Not booted from APFS, skipping apfs_boot_util\n");
548 exit(0);
549 }
550 } else if (mount_phase == MOUNT_PHASE_2) {
551 /*
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 /`
554 *
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.
558 */
559 if (booted_rosv()) {
560 /* Hand the rest of the process over to apfs_boot_util */
561 bootstrap_apfs(MOUNT_PHASE_2);
562 } else {
563 /* upgrade mount "/" read-write */
564 rval = upgrade_mount("/", MNT_UPDATE, options);
565 }
566 }
567 #endif /* TARGET_OS_OSX */
568 } else {
569 print_mount(vfslist);
570 }
571 exit(rval);
572 case 1:
573 if (vfslist != NULL)
574 usage();
575
576 if (init_flags & MNT_UPDATE) {
577
578 rval = upgrade_mount (*argv, init_flags, options);
579
580 break;
581 }
582
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.",
587 *argv);
588 if (BADTYPE(fs->fs_type))
589 errx(errno_or_sysexit(EINVAL, EX_DATAERR),
590 "%s has unknown file system type.",
591 *argv);
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.",
596 *argv);
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);
601 }
602
603 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
604 if (!strcmp(fs->fs_spec, RAMDISK_FS_SPEC)) {
605 if (verbose) {
606 fprintf(stdout, "Found a ramdisk entry\n");
607 }
608 rval = create_mount_ramdisk(fs, init_flags, options);
609 break;
610 }
611 #endif
612 rval = mountfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file,
613 init_flags, options, fs->fs_mntops);
614 break;
615 case 2:
616 /*
617 * If -t flag has not been specified, and spec contains a ':'
618 * then assume that an NFS filesystem is being specified.
619 */
620 if (vfslist == NULL && strchr(argv[0], ':') != NULL) {
621 vfstype = "nfs";
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.",
626 argv[0], argv[1]);
627 }
628
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.
633 *
634 * XXX: Should we implement the same workaround for updating the
635 * root file system at boot time?
636 */
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.",
641 *argv);
642 rval = mountfs(mntbuf->f_fstypename, mntbuf->f_mntfromname,
643 mntbuf->f_mntonname, init_flags, options, 0);
644 }
645 else {
646 /*
647 * If update mount not requested, then go with the vfstype and arguments
648 * specified. If no vfstype specified, then error out.
649 */
650 if (vfstype == NULL) {
651 errx (errno_or_sysexit(EINVAL, EX_USAGE),
652 "You must specify a filesystem type with -t.");
653 }
654 rval = mountfs(vfstype,
655 argv[0], argv[1], init_flags, options, NULL);
656 }
657 break;
658 default:
659 usage();
660 /* NOTREACHED */
661 }
662
663 exit(rval);
664 }
665
666 static int
667 upgrade_mount (const char *mountpt, int init_flags, char *options) {
668
669 struct statfs *mntbuf = NULL;
670 const char *mntfromname = NULL;
671 struct fstab *fs = NULL;
672
673 if ((mntbuf = getmntpt(mountpt)) == NULL) {
674 errx(errno_or_sysexit(errno, -1),
675 "unknown special file or file system %s.",
676 mountpt);
677 }
678
679 /*
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).
687 */
688 mntfromname = mntbuf->f_mntfromname;
689 if (strchr(mntfromname, '/') == NULL) {
690 fs = getfsfile(mntbuf->f_mntonname);
691 if (fs != NULL)
692 mntfromname = fs->fs_spec;
693 }
694
695 /*
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
700 * in.
701 */
702 if (mntbuf->f_flags & MNT_CPROTECT) {
703 init_flags |= MNT_CPROTECT;
704 }
705
706
707 /* Do the update mount */
708 return mountfs(mntbuf->f_fstypename, mntfromname,
709 mntbuf->f_mntonname, init_flags, options, 0);
710
711 }
712
713 int
714 hasopt(mntopts, option)
715 const char *mntopts, *option;
716 {
717 int negative, found;
718 char *opt, *optbuf;
719
720 if (option[0] == 'n' && option[1] == 'o') {
721 negative = 1;
722 option += 2;
723 } else
724 negative = 0;
725 optbuf = strdup(mntopts);
726 found = 0;
727 for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
728 if (opt[0] == 'n' && opt[1] == 'o') {
729 if (!strcasecmp(opt + 2, option))
730 found = negative;
731 } else if (!strcasecmp(opt, option))
732 found = !negative;
733 }
734 free(optbuf);
735 return (found);
736 }
737
738 int
739 ismounted(const char *fs_spec, const char *fs_file, long *flags)
740 {
741 int i, mntsize;
742 struct statfs *mntbuf;
743
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))
748 continue;
749 if (strcmp(mntbuf[i].f_mntonname, fs_file))
750 continue;
751 if (flags)
752 *flags = mntbuf[i].f_flags;
753 return 1;
754 }
755 return 0;
756 }
757
758 #if TARGET_OS_OSX
759 static int
760 booted_apfs(void) {
761 struct statfs *mntbuf;
762
763 if ((mntbuf = getmntpt("/")) == NULL) {
764 errx(errno_or_sysexit(errno, -1),
765 "failed to lookup root file system");
766 }
767
768 return (strcmp(mntbuf->f_fstypename, "apfs") == 0);
769 }
770
771 static int
772 booted_rosv(void) {
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);
777
778 if (!err && is_rosp) {
779 return 1;
780 }
781
782 return 0;
783 }
784 #endif /* TARGET_OS_OSX */
785
786 // prints currently mounted filesystems
787 void
788 print_mount(const char **vfslist)
789 {
790 struct statfs *mntbuf;
791 int mntsize;
792
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) )
797 continue;
798 prmount(&mntbuf[i]);
799 }
800 }
801
802 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
803 int
804 verify_executable_file_existence (char *path)
805 {
806 return _verify_file_flags(path, F_OK | X_OK);
807 }
808
809 int
810 verify_file_existence (char *path)
811 {
812 return _verify_file_flags(path, F_OK);
813 }
814
815 int
816 _verify_file_flags (char *path, int flags)
817 {
818
819 if ( access(path, flags) ) {
820 fprintf(stderr, "Failed access check for %s with issue %s\n", path, strerror(errno));
821 return errno;
822 }
823 return 0;
824 }
825
826 int
827 preflight_create_mount_ramdisk (char *mnt_opts, size_t *ramdisk_size, char *template)
828 {
829 char *special_ramdisk_params;
830
831 if ( mnt_opts == NULL ) {
832 fprintf(stderr, "No mnt_opts provided to ramdisk preflight.\n");
833 return EINVAL;
834 }
835
836 if ( verify_executable_file_existence(HDIK_PATH) != 0 ) {
837 fprintf(stderr, "Failed to find executable hdik at location %s \n", HDIK_PATH);
838 return ENOENT;
839 }
840
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");
844 return EINVAL;
845 }
846
847 if ( ramdisk_size ) {
848 char *ramdisk_size_str = parse_parameter_for_token(special_ramdisk_params, RAMDISK_SIZE_TOK);
849
850 if ( ramdisk_size_str != NULL ) {
851 *ramdisk_size = atoi(ramdisk_size_str);
852 free(ramdisk_size_str);
853 }
854
855 if ( *ramdisk_size == 0 ) {
856 fprintf(stderr, "Unexpected ramdisk size %zu\n", *ramdisk_size);
857 return EINVAL;
858 }
859 }
860
861 if ( template ) {
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);
865 free(template_str);
866 }
867
868 if ( template == NULL ) {
869 fprintf(stderr, "Ramdisk template path not found\n");
870 return EINVAL;
871 }
872
873 }
874
875 return 0;
876 }
877 #endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */
878
879 /*
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
883 * into that buffer.
884 * If `rc` is non-null, then the command's return code will be set
885 * there.
886 * If `signal_no` is non-null, then if the command is signaled, the
887 * signal number will be set there.
888 *
889 *
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
894 */
895 int
896 run_command(char **command_argv, char *output, int *rc, int *signal_no)
897 {
898 int error = -1;
899 int faulting_errno = 0;
900 int status = -1;
901 int internal_result = -1;
902 pid_t pid;
903 posix_spawn_file_actions_t actions = NULL;
904 int output_pipe[2] = {-1, -1};
905 char *command_out = NULL;
906 FILE *stream = NULL;
907
908 if ( !command_argv ) {
909 fprintf(stderr, "command_argv is NULL\n");
910 errno = EINVAL;
911 goto done;
912 }
913
914 if ( pipe(output_pipe) ) {
915 fprintf(stderr, "Failed to create pipe for command output: %d (%s)\n", errno, strerror(errno));
916 goto done;
917 }
918
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));
922 goto done;
923 }
924
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));
928 goto done;
929 }
930
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));
934 goto done;
935 }
936
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));
940 goto done;
941 }
942
943 if ( verbose ) {
944 fprintf(stdout, "Executing command: ");
945 for (char **command_segment = command_argv; *command_segment; command_segment++) {
946 fprintf(stdout, "%s ", *command_segment);
947 }
948 fprintf(stdout, "\n");
949 }
950
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));
954 goto done;
955 }
956
957 // Close out our side of the pipe
958 close(output_pipe[1]);
959 output_pipe[1] = -1;
960
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;
965 } else {
966 command_out = calloc(COMMAND_OUTPUT_MAX, sizeof(char));
967 if (!command_out) {
968 fprintf(stderr, "calloc failed: %d (%s)\n", errno, strerror(errno));
969 goto done;
970 }
971 }
972
973 stream = fdopen(output_pipe[0], "r");
974 if ( !stream ) {
975 fprintf(stderr, "fdopen failed: %d (%s)\n", errno, strerror(errno));
976 goto done;
977 }
978
979 size_t length;
980 size_t count = 0;
981 char *line;
982 while ( (line = fgetln(stream, &length)) && (count < COMMAND_OUTPUT_MAX - length - 1) ) {
983 strncat(command_out, line, length);
984 count += length;
985 }
986
987 if ( ferror(stream) ) {
988 fprintf(stderr, "fgetln failed: %d (%s)\n", errno, strerror(errno));
989 goto done;
990 }
991
992 if ( fclose(stream) ) {
993 fprintf(stderr, "fclose failed: %d (%s)\n", errno, strerror(errno));
994 stream = NULL;
995 goto done;
996 }
997 stream = NULL;
998 close(output_pipe[0]);
999 output_pipe[0] = -1;
1000
1001 while ( waitpid(pid, &status, 0) < 0 ) {
1002 if (errno == EINTR) {
1003 continue;
1004 }
1005 fprintf(stderr, "waitpid failed: %d (%s)\n", errno, strerror(errno));
1006 goto done;
1007 }
1008
1009 if ( verbose ) {
1010 fprintf(stdout, "Command output:\n%s\n", command_out);
1011 }
1012
1013 if ( WIFEXITED(status) ) {
1014 int exit_status = WEXITSTATUS(status);
1015 if (rc) *rc = exit_status;
1016 if (signal_no) *signal_no = 0;
1017
1018 if (exit_status != 0) {
1019 error = 1;
1020 fprintf(stderr, "Command failed: %d\n", exit_status);
1021 goto done;
1022 }
1023 }
1024
1025 if ( WIFSIGNALED(status) ) {
1026 if (rc) *rc = 0;
1027 if (signal_no) *signal_no = WTERMSIG(status);
1028
1029 error = 1;
1030 fprintf(stderr, "Command signaled: %d\n", WTERMSIG(status));
1031 goto done;
1032 }
1033
1034 error = 0;
1035 done:
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;
1039
1040 if ( actions ) {
1041 posix_spawn_file_actions_destroy(&actions);
1042 }
1043
1044 if ( stream ) {
1045 fclose(stream);
1046 }
1047
1048 if ( output_pipe[0] >= 0 ) {
1049 close(output_pipe[0]);
1050 }
1051
1052 if ( output_pipe[1] >= 0 ) {
1053 close(output_pipe[1]);
1054 }
1055
1056 if ( !output && command_out ) {
1057 free(command_out);
1058 }
1059
1060 errno = faulting_errno;
1061 return error;
1062 }
1063
1064 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1065 // Helper function that truncates whitespaces
1066 void
1067 truncate_whitespace(char* str)
1068 {
1069 size_t idx = strcspn(str, " \n");
1070 if ( idx != 0 ) {
1071 str[idx] = '\0';
1072 }
1073 }
1074
1075 // Creates a new unmounted ramdisk of size device_size
1076 int
1077 attach_device(size_t device_size , char* deviceOut)
1078 {
1079 int return_val = -1;
1080 char ram_define [PATH_MAX];
1081 snprintf(ram_define, sizeof(ram_define), "ram://%zu", device_size);
1082
1083 char *command[4] = { HDIK_PATH, "-nomount", ram_define, NULL };
1084
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));
1092 }
1093
1094 truncate_whitespace(deviceOut);
1095 return return_val;
1096 }
1097
1098 // Creates the partition table directly through MediaKit.
1099 int
1100 create_partition_table(size_t partition_size, char *device)
1101 {
1102
1103 MKStatus err = -1;
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;
1112
1113 layout = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1114 options = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1115
1116 if (!layout || !options) {
1117 fprintf(stderr, "Failed to create necessary CFDictionaries\n");
1118 err = errno;
1119 goto done;
1120 }
1121
1122 CFDictionarySetValue(options, kMKMediaPropertyWritableKey, kCFBooleanTrue);
1123
1124 gpt_ref = MKMediaCreateWithPath(kCFAllocatorDefault, device, options, &err);
1125 CFRelease(options);
1126
1127 if (gpt_ref) {
1128 MKStatus mediaErr = 0;
1129 partition = MKCFBuildPartition(PMGPTTYPE, apple_apfs, CFSTR(EDTVolumeFSType), CFSTR(RAMDISK_FS_SPEC), 0, RAMDISK_BLCK_OFFSET, &mediaErr, NULL);
1130
1131 if (!partition) {
1132 fprintf(stderr, "Failed to create partition with err %d\n", mediaErr);
1133 err = mediaErr;
1134 goto done;
1135 }
1136
1137 partitionArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1138
1139 if (!partitionArray) {
1140 fprintf(stderr, "Failed to create partitionArray\n");
1141 err = errno;
1142 CFRelease(partition);
1143 goto done;
1144 }
1145
1146 CFArrayAppendValue(partitionArray, partition);
1147 CFRelease(partition);
1148
1149 CFDictionaryAddValue(layout, CFSTR(MK_PARTITIONS_KEY), partitionArray);
1150 CFRelease(partitionArray);
1151
1152
1153 media = MKCFCreateMedia(&schemes, &mediaErr);
1154
1155 if (!media) {
1156 fprintf(stderr, "Failed to create Schemes with error %d\n", mediaErr);
1157 err = mediaErr;
1158 goto done;
1159 }
1160
1161 map = MKCFCreateMap(PMGPTTYPE, schemes, layout, NULL, NULL, NULL, NULL, NULL, gpt_ref, &mediaErr);
1162 if (!map) {
1163 fprintf(stderr, "Failed to create map with error %d\n", mediaErr);
1164 err = mediaErr;
1165 goto done;
1166 }
1167
1168 err = MKCFWriteMedia(media, layout, NULL, NULL, gpt_ref);
1169 if (err) {
1170 fprintf(stderr, "Failed to WriteMedia with error %d\n", err);
1171 goto done;
1172 }
1173
1174 } else {
1175 fprintf(stderr, "Failed to create gpt_ref with error %d\n", err);
1176 goto done;
1177 }
1178
1179 if (verbose) {
1180 fprintf(stderr, "Releasing MediaKit objects\n");
1181 }
1182 err = 0;
1183
1184 done:
1185 if (media) {
1186 MKCFDisposeMedia(media);
1187 }
1188
1189 if (layout) {
1190 CFRelease(layout);
1191 }
1192
1193 if (gpt_ref) {
1194 CFRelease(gpt_ref);
1195 }
1196
1197 return err;
1198 }
1199
1200 // Triggers newfs_apfs for the target device
1201 int
1202 construct_apfs_volume(char *mounted_device_name)
1203 {
1204 int return_val = -1;
1205 int status = -1;
1206 char *command[5] = { "/sbin/newfs_apfs", "-v", "Var", mounted_device_name, NULL };
1207
1208 status = run_command(command, NULL, &return_val, NULL);
1209 if ( status >= 0 ) {
1210 return return_val;
1211 } else {
1212 fprintf(stderr, "Failed to execute command %s\n", command[0]);
1213 errno_or_sysexit(errno, -1);
1214 }
1215
1216 // shouldn't reach here. This is to satisfy the compiler
1217 return -1;
1218 }
1219
1220 // unmounts device at location
1221 int
1222 unmount_location(char *mount_point)
1223 {
1224 int return_val = -1;
1225 int status = -1;
1226 char *command[4] = { "/sbin/umount", "-f", mount_point, NULL };
1227
1228 status = run_command(command, NULL, &return_val, NULL);
1229 if ( status >= 0 ) {
1230 return return_val;
1231 } else {
1232 fprintf(stderr, "Failed to execute command %s\n", command[0]);
1233 return errno_or_sysexit(errno, -1);
1234 }
1235 }
1236
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
1241 // the seed files.
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.
1249 char*
1250 split_ramdisk_params(char *opts)
1251 {
1252 char* opt = NULL;
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);
1259
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.
1268 break;
1269 }
1270 }
1271 free(optbuf);
1272 return target_str;
1273 }
1274
1275 // returns string for the parameter after the '=' in the search_string
1276 // part of the special ramdisk parameters
1277 char*
1278 parse_parameter_for_token(char * opts, char * search_string)
1279 {
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);
1284
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);
1291 }
1292 }
1293 return return_str;
1294 }
1295
1296
1297 static int _copyfile_status(int what, int stage, copyfile_state_t state, const char * src, const char * dst, void * ctx) {
1298
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);
1304 }
1305 }
1306
1307 return COPYFILE_CONTINUE;
1308 }
1309
1310
1311 // returns 0 upon success and a valid sysexit or errno code upon failure
1312 int
1313 create_mount_ramdisk(struct fstab *fs, int init_flags, char *options)
1314 {
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;
1324
1325 if ( verify_file_existence(mnt_point) != 0 ) {
1326 if (verbose) {
1327 fprintf(stderr, "Default mount %s is not available. Using backup %s.\n", mnt_point, RAMDISK_BCK_MOUNT);
1328 }
1329 mnt_point = RAMDISK_BCK_MOUNT;
1330 if ( verify_file_existence(mnt_point) != 0 ) {
1331 fprintf(stderr, "Mountpoints not available. Exiting.\n");
1332 return ENOENT;
1333 }
1334 }
1335
1336 if ( preflight_create_mount_ramdisk(fs->fs_mntops, &ram_size, seed_location) != 0 ) {
1337 fprintf(stderr, "Failed ramdisk preflight. Exiting.\n");
1338 return EINVAL;
1339 }
1340
1341 if ( verbose ) {
1342 fprintf(stdout, "Attaching device of size %zu\n", ram_size);
1343 }
1344
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));
1348 }
1349
1350 if ( verbose ) {
1351 fprintf(stdout, "Creating partition table for device %s \n", ramdisk_partition);
1352 }
1353
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));
1357 }
1358
1359 snprintf(ramdisk_container, sizeof(ramdisk_container), "%ss1", ramdisk_partition);
1360
1361 if ( verbose ) {
1362 fprintf(stdout, "Creating apfs volume on partition %s\n", ramdisk_container);
1363 }
1364
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));
1368 }
1369
1370 snprintf(ramdisk_volume, sizeof(ramdisk_volume), "%ss1", ramdisk_container);
1371
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));
1375 }
1376
1377 // Mount volume to RAMDISK_TMP_MOUNT
1378 if ( verbose ) {
1379 fprintf(stdout, "Mounting to tmp location %s\n", mnt_point);
1380 }
1381
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));
1386 }
1387
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));
1394 }
1395 copyfile_state_free(state);
1396
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));
1401 }
1402
1403 if( verbose ) {
1404 fprintf(stdout, "Mounting apfs volume %s to %s\n", ramdisk_volume, target_dir);
1405 }
1406
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));
1411 }
1412
1413 // Verify contents in stdout
1414 if ( verbose ) {
1415 print_mount(NULL);
1416 }
1417
1418 return mount_return;
1419 }
1420 #endif // (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1421
1422 // returns 0 upon success and a valid sysexit or errno code upon failure
1423 int
1424 mountfs(const char *vfstype, const char *fs_spec, const char *fs_file, int flags,
1425 const char *options, const char *mntopts)
1426 {
1427 /* List of directories containing mount_xxx subcommands. */
1428 static const char *edirs[] = {
1429 _PATH_SBIN,
1430 _PATH_USRSBIN,
1431 NULL
1432 };
1433 static const char *bdirs[] = {
1434 _PATH_FSBNDL,
1435 _PATH_USRFSBNDL,
1436 NULL
1437 };
1438 const char *argv[100], **edir, **bdir;
1439 struct statfs sf;
1440 pid_t pid;
1441 int argc, i, status;
1442 char *optbuf, execname[MAXPATHLEN + 1], mntpath[MAXPATHLEN];
1443
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));
1452 }
1453 } else {
1454 warn("realpath %s", mntpath);
1455 return (errno_or_sysexit(errno , -1));
1456 }
1457 }
1458
1459 fs_file = mntpath;
1460
1461 if (mntopts == NULL)
1462 mntopts = "";
1463 if (options == NULL) {
1464 if (*mntopts == '\0') {
1465 options = "";
1466 } else {
1467 options = mntopts;
1468 mntopts = "";
1469 }
1470 }
1471 optbuf = catopt(strdup(mntopts), options);
1472
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");
1485
1486
1487 argc = 0;
1488 argv[argc++] = vfstype;
1489 mangle(optbuf, &argc, argv);
1490 argv[argc++] = fs_spec;
1491 argv[argc++] = fs_file;
1492 argv[argc] = NULL;
1493
1494 if (debug) {
1495 (void)printf("exec: mount_%s", vfstype);
1496 for (i = 1; i < argc; i++)
1497 (void)printf(" %s", argv[i]);
1498 (void)printf("\n");
1499 return (0);
1500 }
1501
1502 switch (pid = fork()) {
1503 case -1: /* Error. */
1504 warn("fork");
1505 free(optbuf);
1506 return (errno_or_sysexit(errno, EX_OSERR));
1507 case 0: /* Child. */
1508 /* Go find an executable. */
1509 edir = edirs;
1510 do {
1511 (void)snprintf(execname, sizeof(execname),
1512 "%s/mount_%s", *edir, vfstype);
1513
1514 argv[0] = execname;
1515 execv(execname, (char * const *)argv);
1516 if (errno != ENOENT)
1517 warn("exec %s for %s", execname, fs_file);
1518 } while (*++edir != NULL);
1519
1520 bdir = bdirs;
1521 do {
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);
1526
1527 argv[0] = execname;
1528 execv(execname, (char * const *)argv);
1529 if (errno != ENOENT)
1530 warn("exec %s for %s", execname, fs_file);
1531 } while (*++bdir != NULL);
1532
1533 if (errno == ENOENT) {
1534 warn("exec %s for %s", execname, fs_file);
1535 return (errno_or_sysexit(errno, EX_OSFILE));
1536 }
1537 exit(errno_or_sysexit(errno , -1));
1538 /* NOTREACHED */
1539 default: /* Parent. */
1540 free(optbuf);
1541
1542 if (waitpid(pid, &status, 0) < 0) {
1543 warn("waitpid");
1544 return (errno_or_sysexit(errno , -1));
1545 }
1546
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)));
1551 }
1552 } else if (WIFSIGNALED(status)) {
1553 warnx("%s: %s", fs_file, sys_siglist[WTERMSIG(status)]);
1554 return (errno_or_sysexit(EINTR, EX_UNAVAILABLE));
1555 }
1556
1557 if (verbose) {
1558 if (statfs(fs_file, &sf) < 0) {
1559 warn("statfs %s", fs_file);
1560 return (errno_or_sysexit(errno , -1));
1561 }
1562 prmount(&sf);
1563 }
1564 break;
1565 }
1566
1567 return (EX_OK);
1568 }
1569
1570 static bool
1571 is_sealed(const char *mntpt)
1572 {
1573 struct vol_attr {
1574 uint32_t len;
1575 vol_capabilities_attr_t vol_cap;
1576 } vol_attrs = {};
1577
1578 struct attrlist vol_attr_list = {
1579 .bitmapcount = ATTR_BIT_MAP_COUNT,
1580 .volattr = ATTR_VOL_CAPABILITIES
1581 };
1582
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);
1586 }
1587 return false;
1588 }
1589
1590 void
1591 prmount(sfp)
1592 struct statfs *sfp;
1593 {
1594 int flags;
1595 mountopt_t *o;
1596 struct passwd *pw;
1597
1598 (void)printf("%s on %s (%s", sfp->f_mntfromname, sfp->f_mntonname,
1599 sfp->f_fstypename);
1600
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);
1607 flags &= ~o->o_opt;
1608 }
1609 if (sfp->f_owner) {
1610 (void)printf(", mounted by ");
1611 if ((pw = getpwuid(sfp->f_owner)) != NULL)
1612 (void)printf("%s", pw->pw_name);
1613 else
1614 (void)printf("%d", sfp->f_owner);
1615 }
1616 (void)printf(")\n");
1617 }
1618
1619 struct statfs *
1620 getmntpt(name)
1621 const char *name;
1622 {
1623 struct statfs *mntbuf;
1624 int i, mntsize;
1625
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]);
1631 }
1632 return (NULL);
1633 }
1634
1635 char *
1636 catopt(s0, s1)
1637 char *s0;
1638 const char *s1;
1639 {
1640 size_t i;
1641 char *cp;
1642
1643 if (s0 && *s0) {
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);
1649 } else
1650 cp = strdup(s1);
1651
1652 if (s0)
1653 free(s0);
1654 return (cp);
1655 }
1656
1657 void
1658 mangle(options, argcp, argv)
1659 char *options;
1660 int *argcp;
1661 const char **argv;
1662 {
1663 char *p, *s;
1664 int argc;
1665
1666 argc = *argcp;
1667 for (s = options; (p = strsep(&s, ",")) != NULL;)
1668 if (*p != '\0') {
1669 if (*p == '-') {
1670 argv[argc++] = p;
1671 p = strchr(p, '=');
1672 if (p) {
1673 *p = '\0';
1674 argv[argc++] = p+1;
1675 }
1676 } else {
1677 argv[argc++] = "-o";
1678 argv[argc++] = p;
1679 }
1680 }
1681
1682 *argcp = argc;
1683 }
1684
1685 void
1686 usage()
1687 {
1688
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));
1696 }