]> git.cameronkatri.com Git - apple_cmds.git/blob - diskdev_cmds/fsck.tproj/fsck.c
system_cmds: 880.120.1
[apple_cmds.git] / diskdev_cmds / fsck.tproj / fsck.c
1 /*
2 * Copyright (c) 2010-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, 1986, 1993
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 <fstab.h>
57 #include <err.h>
58 #include <errno.h>
59 #include <sys/param.h>
60 #include <sys/mount.h>
61 #include <sys/stat.h>
62 #include <stdarg.h>
63 #include <stdio.h>
64 #include <ctype.h>
65 #include <dirent.h>
66 #include <fcntl.h>
67 #include <paths.h>
68 #include <unistd.h>
69 #include <stdlib.h>
70 #include <sys/types.h>
71 #include <sys/event.h>
72 #include <sys/time.h>
73 #include <signal.h>
74
75 #include <TargetConditionals.h>
76
77 #include "fsck.h"
78 #include "../edt_fstab/edt_fstab.h"
79
80
81 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
82 #include <os/bsd.h>
83 #define AUTO_BOOT "auto-boot"
84 #endif
85
86 /* Local Static Functions */
87 static int argtoi(int flag, char *req, char *str, int base);
88 static void usage();
89 static int startdiskcheck(disk_t* disk);
90
91
92 /* Global Variables */
93 int preen = 0; /* We're checking all fs'es in 'preen' mode */
94 int returntosingle = 0; /* Return a non-zero status to prevent multi-user start up */
95 int hotroot= 0; /* We're checking / (slash or root) */
96 int fscks_running = 0; /* Number of currently running fs checks */
97 int ndisks = 0; /* Total number of disks observed */
98 int debug = 0; /* Output Debugging info */
99 int force_fsck = 0; /* Force an fsck even if the underlying FS is clean */
100 int maximum_running = 0; /* Maximum number of sub-processes we'll allow to be spawned */
101 int quick_check = 0; /* Do a quick check. Quick check returns clean, dirty, or fail */
102 int live_check = 0; /* Do a live check. This allows fsck to run on a partition which is mounted RW */
103 int requested_passno = 0; /* only check the filesystems with the specified passno */
104 /*
105 * The two following globals are mutually exclusive; you cannot assume "yes" and "no."
106 * The last one observed will be the one that is honored. e.g. fsck -fdnyny will result
107 * in assume_yes == 1, and assume_no == 0;
108 */
109 int assume_no = 0; /* If set, assume a "no" response to repair requests */
110 int assume_yes = 0; /* If set, assume a "yes" response to repair requests. */
111
112 disk_t *disklist = NULL; /* Disk struct with embedded list to enum. all disks */
113 part_t *badlist = NULL; /* List of partitions which may have had errors */
114
115 static int argtoi(int flag, char *req, char *str, int base) {
116 char *cp;
117 int ret;
118
119 ret = (int)strtol(str, &cp, base);
120 if (cp == str || *cp)
121 errx(EEXIT, "-%c flag requires a %s", flag, req);
122 return (ret);
123 }
124
125 static void usage(void) {
126 fprintf(stderr, "fsck usage: fsck [-fdnypqL] [-l number]\n");
127 }
128
129 #if DEBUG
130 void debug_args (void);
131 void dump_part (part_t* part);
132 void dump_disk (disk_t* disk);
133 void dump_fsp (struct fstab *fsp);
134
135 void debug_args (void) {
136 if (debug) {
137 printf("debug %d\n", debug);
138 }
139
140 if (force_fsck) {
141 printf("force_fsck %d\n", force_fsck);
142 }
143
144 if (assume_no) {
145 printf("assume_no: %d\n", assume_no);
146 }
147
148 if (assume_yes) {
149 printf("assume_yes: %d\n", assume_yes);
150 }
151
152 if (preen) {
153 printf("preen: %d\n", preen);
154 }
155
156 if (quick_check) {
157 printf("quick check %d\n", quick_check);
158 }
159
160 printf("maximum_running %d\n", maximum_running);
161 }
162
163 void dump_fsp (struct fstab *fsp) {
164 fprintf (stderr, "**********dumping fstab entry %p**********\n", fsp);
165 fprintf (stderr, "fstab->fs_spec: %s\n", fsp->fs_spec);
166 fprintf (stderr, "fstab->fs_file: %s\n", fsp->fs_file);
167 fprintf (stderr, "fstab->fs_vfstype: %s\n", fsp->fs_vfstype);
168 fprintf (stderr, "fstab->fs_mntops: %s\n", fsp->fs_mntops);
169 fprintf (stderr, "fstab->fs_type: %s\n", fsp->fs_type);
170 fprintf (stderr, "fstab->fs_freq: %d\n", fsp->fs_freq);
171 fprintf (stderr, "fstab->fs_passno: %d\n", fsp->fs_passno);
172 fprintf (stderr, "********** finished dumping fstab entry %p**********\n\n\n", fsp);
173
174 }
175
176 void dump_disk (disk_t* disk) {
177 part_t *part;
178 fprintf (stderr, "**********dumping disk entry %p**********\n", disk);
179 fprintf (stderr, "disk->name: %s\n", disk->name);
180 fprintf (stderr, "disk->next: %p\n", disk->next);
181 fprintf (stderr, "disk->part: %p\n", disk->part);
182 fprintf (stderr, "disk->pid: %d\n\n", disk->pid);
183
184 part = disk->part;
185 if (part) {
186 fprintf(stderr, "dumping partition entries now... \n");
187 }
188 while (part) {
189 dump_part (part);
190 part = part->next;
191 }
192 fprintf (stderr, "**********done dumping disk entry %p**********\n\n\n", disk);
193
194 }
195
196 void dump_part (part_t* part) {
197 fprintf (stderr, "**********dumping partition entry %p**********\n", part);
198 fprintf (stderr, "part->next: %p\n", part->next);
199 fprintf (stderr, "part->name: %s\n", part->name);
200 fprintf (stderr, "part->fsname: %s\n", part->fsname);
201 fprintf (stderr, "part->vfstype: %s\n\n", part->vfstype);
202 fprintf (stderr, "**********done dumping partition entry %p**********\n\n\n", part);
203 }
204
205 #endif
206
207
208
209 int main (int argc, char** argv) {
210 /* for getopt */
211 extern char *optarg;
212 extern int optind;
213
214 int ch;
215 int ret;
216
217
218 sync();
219 while ((ch = getopt(argc, argv, "dfpR:qnNyYl:L")) != EOF) {
220 switch (ch) {
221 case 'd':
222 debug++;
223 break;
224
225 case 'l':
226 maximum_running = argtoi('l', "number", optarg, 10);
227 break;
228
229 case 'f':
230 force_fsck++;
231 break;
232
233 case 'R':
234 requested_passno = argtoi('R', "number", optarg, 10);
235 /* only allowed to specify 1 or 2 as argument here */
236 if ((requested_passno < ROOT_PASSNO) || (requested_passno > NONROOT_PASSNO)) {
237 usage();
238 exit(EINVAL);
239 }
240 break;
241
242 case 'N':
243 case 'n':
244 assume_no = 1;
245 assume_yes = 0;
246 break;
247
248 case 'p':
249 preen++;
250 break;
251
252 case 'q':
253 quick_check = 1;
254 break;
255
256 case 'Y':
257 case 'y':
258 assume_yes = 1;
259 assume_no = 0;
260 break;
261
262 case 'L':
263 live_check = 1;
264 break;
265
266 default:
267 errx(EEXIT, "%c option?", ch);
268 break;
269 }
270 }
271 argc -= optind;
272 argv += optind;
273
274 /* Install our signal handlers */
275 if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
276 (void)signal(SIGINT, catchsig);
277 }
278
279 if (preen) {
280 (void)signal(SIGQUIT, catchquit);
281 }
282
283 if (argc) {
284 /* We do not support any extra arguments at this time */
285 ret = EINVAL;
286 usage();
287 exit(ret);
288 }
289
290 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
291 char arg_val[16];
292 if (os_parse_boot_arg_string(AUTO_BOOT, arg_val, sizeof(arg_val))) {
293 if (strcmp(arg_val, "false")) {
294 fprintf(stderr, "warning: auto-boot is set to %s\n", arg_val);
295 }
296 }
297 #endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */
298
299 /*
300 * checkfstab does the bulk of work for fsck. It will scan through the
301 * fstab and iterate through the devices, as needed
302 */
303 ret = checkfstab();
304 /* Return a non-zero return status so that we'll stay in single-user */
305 if (returntosingle) {
306 exit(2);
307 }
308 /* Return the error value obtained from checking all filesystems in checkfstab */
309 exit(ret);
310
311 }
312
313 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
314 #include <APFS/APFSConstants.h> // EDT_OS_ENV_MAIN
315
316 static int check_boot_container(void)
317 {
318 int error = 0;
319 uint32_t os_env = 0;
320 const char *boot_container = get_boot_container(&os_env);
321 char *rcontainer = NULL;
322 char *container = NULL;
323
324 if ((os_env != EDT_OS_ENV_MAIN) &&
325 (os_env != EDT_OS_ENV_DIAGS)) {
326 fprintf(stdout, "fsck: not booting main or diagnostic OS. Skipping fsck on OS container\n");
327 return (0);
328 }
329
330 if (!boot_container) {
331 fprintf(stderr, "fsck: failed to get boot container\n");
332 return (EEXIT);
333 }
334
335 /* get a non-const copy */
336 container = strdup(boot_container);
337 if (!container) {
338 fprintf(stderr, "fsck: failed to copy boot container\n");
339 return (EEXIT);
340 }
341
342 /* Take the special device name, and do some cursory checks */
343 if ((rcontainer = blockcheck(container)) != 0) {
344 /* Construct a temporary disk_t for checkfilesys */
345 disk_t disk;
346 part_t part;
347
348 disk.name = NULL;
349 disk.next = NULL;
350 disk.part = &part;
351 disk.pid = 0;
352
353 part.next = NULL;
354 part.name = rcontainer;
355 part.vfstype = "apfs";
356
357 /* Run the filesystem check against the filesystem in question */
358 error = checkfilesys(&disk, 0);
359 }
360
361 free(container);
362
363 return (error);
364 }
365 #endif
366
367 /*
368 * This is now the guts of fsck.
369 *
370 * This function will iterate over all of the elements in the fstab and run
371 * fsck-like binaries on all of the filesystems in question if able. The root filesystem
372 * is checked first, and then non-root filesystems are checked in order.
373 */
374 int checkfstab(void) {
375 int running_status = 0;
376 int ret;
377
378 /*
379 * fsck boot-task (fsck -q):
380 * iOS - fsck_apfs -q will quick-check the container and volumes.
381 * So no need to obtain and check the fstab entries from EDT,
382 * just check the container.
383 * OSX - See comment in build_disklist(). In short - during early boot
384 * getfsent() will only return a synthetic entry for the root volume ("/")
385 * and additional fstab entries. An invalid entry will fail boot.
386 * To avoid this we require passing the "-R 1" flag to only check
387 * the root volume, which per fsck_apfs behaviour will quick-check the container and
388 * the root volume. We dont need to check the other volumes for boot
389 * (except perhaps for the VM and Data volumes but those are mounted earlier anyway).
390 */
391 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
392 if (quick_check && (requested_passno == 0)) {
393 return check_boot_container();
394 }
395 #endif
396
397 ret = build_disklist ();
398 /*
399 * If we encountered any errors or if 'preen' was off,
400 * then we must have scanned everything. Either way, return.
401 */
402 if ((ret) || (preen == 0)) {
403 return ret;
404 }
405
406 if (preen) {
407 /* Otherwise, see if we need to do a cursory fsck against the FS. */
408 ret = do_diskchecks();
409 running_status |= ret;
410 }
411
412
413 if (running_status) {
414 part_t *part = NULL;
415
416 if (badlist == NULL) {
417 /* If there were no disk problems, then return the status */
418 return (running_status);
419 }
420 fprintf(stderr, "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
421 badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:");
422 for (part = badlist; part; part = part->next) {
423 fprintf(stderr, "%s (%s)%s", part->name, part->fsname, part->next ? ", " : "\n");
424 }
425 return (running_status);
426 }
427
428 endfsent();
429 return (0);
430
431 }
432
433 /*
434 * This function builds up the list of disks that fsck will need to
435 * process and check.
436 *
437 * If we're not in 'preen' mode, then we'll go ahead and do the full
438 * check on all of them now.
439 *
440 * If we ARE in 'preen' mode, then we'll just check the root fs, and log
441 * all of the other ones that we encounter by scanning through the fstab
442 * for checking a bit later on. See notes below for checking '/' at boot.
443 */
444 int build_disklist(void) {
445
446 struct fstab *fsp = NULL;
447 int passno = 0;
448 char *name;
449 int retval;
450 int running_status = 0;
451
452 int starting_passno = ROOT_PASSNO; //1
453 int ending_passno = NONROOT_PASSNO; //2
454
455 if (requested_passno) {
456 if (requested_passno == NONROOT_PASSNO) {
457 starting_passno = NONROOT_PASSNO;
458 }
459 else if (requested_passno == ROOT_PASSNO) {
460 ending_passno = ROOT_PASSNO;
461 }
462 }
463
464 /*
465 * We may need to iterate over the elements in the fstab in non-sequential order.
466 * Thus, we take up to two passes to check all fstab fsck-eligible FSes. The first
467 * pass should focus on the root filesystem, which can be inferred from the fsp->fs_passno
468 * field. The library code used to fill in the fsp structure will specify an
469 * fsp->fs_passno == 1 for the root. All other filesystems should get fsp->fs_passno == 2.
470 * (See fstab manpage for more info.)
471 *
472 * However, note that with the addition of the -R argument to this utility, we might "skip"
473 * one of these two passes. By passing in -R 1 or -R 2, the executor of this utility is
474 * specifying that they only want 'fsck' to run on either the root filesystem (passno == 1)
475 * or the non-root filesystems (passno == 2).
476 */
477 #if DEBUG
478 fprintf(stderr, "fsck: iterating fstab - starting passno %d, ending passno %d\n",
479 starting_passno, ending_passno);
480 #endif
481
482 for (passno = starting_passno; passno <= ending_passno; passno++) {
483 /* Open or reset the fstab entry */
484 if (setfsent() == 0) {
485 fprintf(stderr, "Can't get filesystem checklist: %s\n", strerror(errno));
486 return EEXIT;
487 }
488 /* Iterate through the fs entries returned from fstab */
489 while ((fsp = getfsent()) != 0) {
490 /*
491 * Determine if the filesystem is worth checking. Ignore it if it
492 * is not checkable.
493 */
494 if (fs_checkable(fsp) == 0) {
495 continue;
496 }
497 /*
498 * 'preen' mode is a holdover from the BSD days of long ago. It is semi-
499 * equivalent to a fsck -q, except that it skips filesystems who say that they
500 * are cleanly unmounted and fsck -q will actually call into fsck_hfs to do a
501 * journaling check.
502 *
503 * If preen is off, then we will wind up checking everything in order, so
504 * go ahead and just check this item now. However, if requested_passno is set, then
505 * the caller is asking us to explicitly only check partitions with a certain passno
506 * identifier.
507 *
508 * Otherwise, only work on the root filesystem in the first pass. We can
509 * tell that the fsp represents the root filesystem if fsp->fs_passno == 1.
510 *
511 * NOTE: On Mac OSX, LibInfo, part of Libsystem is in charge of vending us a valid
512 * fstab entry when we're running 'fsck -q' in early boot to ensure the validity of the
513 * boot disk. Since it's before the volume is mounted read-write, getfsent() will probe
514 * the Mach port for directory services. Since it's not up yet, it will determine the
515 * underlying /dev/disk entry for '/' and mechanically construct a fstab entry for / here.
516 * It correctly fills in the passno field below, which will allow us to fork/exec in order
517 * to call fsck_XXX as necessary.
518 *
519 * Once we're booted to multi-user, this block of code shouldn't ever really check anything
520 * unless it's a valid fstab entry because the synthesized fstab entries don't supply a passno
521 * field. Also, they would have to be valid /dev/disk fstab entries as opposed to
522 * UUID or LABEL ones.
523 *
524 * on iOS the above is not true; we rely on the EDT for all fstab entries.
525 */
526 if ((preen == 0) || (passno == 1 && fsp->fs_passno == 1)) {
527
528 /*
529 * If the caller specified a -R argument for us to examine only a certain
530 * range of passno fields AND that value does not match our current passno,
531 * then let the loop march on.
532 */
533 if (requested_passno && (fsp->fs_passno != requested_passno)) {
534 continue; //skip to the next fsent entry.
535 }
536
537 /* Take the special device name, and do some cursory checks. */
538 if ((name = blockcheck(fsp->fs_spec)) != 0) {
539 #if TARGET_OS_IPHONE
540 if (!strcmp(name, RAMDISK_FS_SPEC)) {
541 fprintf(stdout, "Encountered ramdisk definition for location %s - will be created during mount.\n", fsp->fs_file);
542 continue;
543 }
544 #endif // TARGET_OS_IPHONE
545
546 /* Construct a temporary disk_t for checkfilesys */
547 disk_t disk;
548 part_t part;
549
550 disk.name = NULL;
551 disk.next = NULL;
552 disk.part = &part;
553 disk.pid = 0;
554
555 part.next = NULL;
556 part.name = name;
557 part.vfstype = fsp->fs_vfstype;
558
559 /* Run the filesystem check against the filesystem in question */
560 if ((retval = checkfilesys(&disk, 0)) != 0) {
561 return (retval);
562 }
563 }
564 else {
565 fprintf(stderr, "BAD DISK NAME %s\n", fsp->fs_spec);
566 /*
567 * If we get here, then blockcheck failed (returned NULL).
568 * Presumably, we couldn't stat the disk device. In this case,
569 * just bail out because we couldn't even find all of the
570 * entries in the fstab.
571 */
572 return EEXIT;
573 }
574 }
575 /*
576 * If we get here, then preen must be ON and we're checking a
577 * non-root filesystem. So we must be on the 2nd pass, and
578 * the passno of the element returned from fstab will be > 1.
579 */
580 else if (passno == 2 && fsp->fs_passno > 1) {
581 /*
582 * If the block device checks tell us the name is bad, mark it in the status
583 * field and continue
584 */
585 if ((name = blockcheck(fsp->fs_spec)) == NULL) {
586 fprintf(stderr, "BAD DISK NAME %s\n", fsp->fs_spec);
587 running_status |= 8;
588 continue;
589 }
590 /*
591 * Since we haven't actually done anything yet, add this partition
592 * to the list of devices to check later on.
593 */
594 addpart(name, fsp->fs_file, fsp->fs_vfstype);
595 }
596 }
597 /* If we're not in preen mode, then we scanned everything already. Just bail out */
598 if (preen == 0) {
599 break;
600 }
601 }
602
603 return running_status;
604
605 }
606
607 /*
608 * This function only runs if we're operating in 'preen' mode. If so,
609 * then iterate over our list of non-root filesystems and fork/exec 'fsck_XXX'
610 * on them to actually do the checking. Spawn up to 'maximum_running' processes.
611 * If 'maximum_running' was not set, then default to the number of disk devices
612 * that we encounter.
613 */
614 int do_diskchecks(void) {
615
616 int fsckno = 0;
617 int pid = 0;
618 int exitstatus = 0;
619 int retval = 0;
620 int running_status = 0;
621 disk_t *disk = NULL;
622 disk_t *nextdisk = NULL;
623
624 /*
625 * If we were not specified a maximum number of FS's to check at once,
626 * or the max exceeded the number of disks we observed, then clip it to
627 * the maximum number of disks.
628 */
629 if ((maximum_running == 0) || (maximum_running > ndisks)) {
630 maximum_running = ndisks;
631 }
632 nextdisk = disklist;
633
634 /* Start as many fscks as we will allow */
635 for (fsckno = 0; fsckno < maximum_running; ++fsckno) {
636 /*
637 * Run the disk check against the disk devices we have seen.
638 * 'fscks_running' is increased for each disk we actually visit.
639 * If we hit an error then sleep for 10 seconds and just try again.
640 */
641 while ((retval = startdiskcheck(nextdisk)) && fscks_running > 0) {
642 sleep(10);
643 }
644 if (retval) {
645 return (retval);
646 }
647 nextdisk = nextdisk->next;
648 }
649
650 /*
651 * Once we get limited by 'maximum_running' as to the maximum
652 * number of processes we can spawn at a time, wait until one of our
653 * child processes exits before spawning another one.
654 */
655 while ((pid = wait(&exitstatus)) != -1) {
656 for (disk = disklist; disk; disk = disk->next) {
657 if (disk->pid == pid) {
658 break;
659 }
660 }
661 if (disk == 0) {
662 /* Couldn't find a new disk to check */
663 printf("Unknown pid %d\n", pid);
664 continue;
665 }
666 /* Check the WIFEXITED macros */
667 if (WIFEXITED(exitstatus)) {
668 retval = WEXITSTATUS(exitstatus);
669 }
670 else {
671 retval = 0;
672 }
673 if (WIFSIGNALED(exitstatus)) {
674 printf("%s (%s): EXITED WITH SIGNAL %d\n",
675 disk->part->name, disk->part->fsname,
676 WTERMSIG(exitstatus));
677 retval = 8;
678 }
679 /* If it hit an error, OR in the value into our running total */
680 if (retval != 0) {
681 part_t *temp_part = badlist;
682
683 /* Add the bad partition to the bad partition list */
684 running_status |= retval;
685 badlist = disk->part;
686 disk->part = disk->part->next;
687 if (temp_part) {
688 badlist->next = temp_part;
689 }
690 } else {
691 /* Just move to the next partition */
692 part_t *temp_part = disk->part;
693 disk->part = disk->part->next;
694 destroy_part (temp_part);
695 }
696 /*
697 * Reset the pid to 0 since this partition was checked.
698 * Decrease the number of running processes. Decrease the
699 * number of disks if we finish one off.
700 */
701 disk->pid = 0;
702 fscks_running--;
703
704 if (disk->part == NULL) {
705 ndisks--;
706 }
707
708 if (nextdisk == NULL) {
709 if (disk->part) {
710 /* Start the next partition up */
711 while ((retval = startdiskcheck(disk)) && fscks_running > 0) {
712 sleep(10);
713 }
714 if (retval) {
715 return (retval);
716 }
717 }
718 }
719 else if (fscks_running < maximum_running && fscks_running < ndisks) {
720 /* If there's more room to go, then find the next valid disk */
721 for ( ;; ) {
722 if ((nextdisk = nextdisk->next) == NULL) {
723 nextdisk = disklist;
724 }
725 if (nextdisk->part != NULL && nextdisk->pid == 0) {
726 break;
727 }
728 }
729
730 while ((retval = startdiskcheck(nextdisk)) && fscks_running > 0) {
731 sleep(10);
732 }
733 if (retval) {
734 return (retval);
735 }
736 }
737 }
738 return running_status;
739
740 }
741
742 /*
743 * fork/exec in order to spawn a process that will eventually
744 * wait4() on the fsck_XXX process.
745 *
746 * Note: The number of forks/execs here is slightly complicated.
747 * We call fork twice, and exec once. The reason we need three total
748 * processes is that the first will continue on as the main line of execution.
749 * This first fork() will create the second process which calls checkfilesys().
750 * In checkfilesys() we will call fork again, followed by an exec. Observe that
751 * the second process created here will *immediately* call wait4 on the third
752 * process, fsck_XXX. This is so that we can track error dialogs and exit statuses
753 * and tie them to the specific instance of fsck_XXX that created them. Otherwise,
754 * if we just called fork a bunch of times and waited on the first one to finish,
755 * it would be difficult to tell which process exited first, and whether or not the
756 * exit status is meaningful.
757 *
758 * Also note that after we get our result from checkfilesys(), we immediately exit,
759 * so that this process doesn't linger and accidentally continue on.
760 */
761 static
762 int startdiskcheck(disk_t* disk) {
763
764 /*
765 * Split this process into the one that will go
766 * call fsck_XX and the one that won't
767 */
768 disk->pid = fork();
769 if (disk->pid < 0) {
770 perror("fork");
771 return (8);
772 }
773 if (disk->pid == 0) {
774 /*
775 * Call checkfilesys. Note the exit() call. Also note that
776 * we pass 1 to checkfilesys since we are a child process
777 */
778 exit(checkfilesys(disk, 1));
779 }
780 else {
781 fscks_running++;
782 }
783 return (0);
784 }
785
786
787
788
789 /*
790 * Call fork/exec in order to spawn instance of fsck_XXX for the filesystem
791 * of the specified vfstype. This will actually spawn the process that does the
792 * checking of the filesystem in question.
793 */
794 int checkfilesys(disk_t *disk, int child) {
795 #define ARGC_MAX 4 /* cmd-name, options, device, NULL-termination */
796 part_t *part = disk->part;
797 const char *argv[ARGC_MAX];
798 int argc;
799 int error = 0;
800 struct stat buf;
801 pid_t pid;
802 int status = 0;
803 char options[] = "-pdfnyql"; /* constant strings are not on the stack */
804 char progname[NAME_MAX];
805 char execname[MAXPATHLEN + 1];
806 char* filesys = part->name;
807 char* vfstype = part->vfstype;
808
809 if (preen && child) {
810 (void)signal(SIGQUIT, ignore_single_quit);
811 }
812
813 #if TARGET_OS_IPHONE
814 if (!strcmp(filesys, RAMDISK_FS_SPEC)) {
815 fprintf(stdout, "No need to check filesys for ramdisk, does not exist yet.\n");
816 return 0;
817 }
818 #endif // TARGET_OS_IPHONE
819
820 /*
821 * If there was a vfstype specified, then we can go ahead and fork/exec
822 * the child fsck process if the fsck_XXX binary exists.
823 */
824 if (vfstype) {
825 int exitstatus;
826
827 /*
828 * Not all options are currently supported by all 5 fsck_* binaries.
829 * Specifically:
830 * udf does not support debug, quick or live
831 * msdos does not support quick or live
832 * exfat does not support live
833 * apfs does not support preen
834 * When the System invokes fsck it is during boot (one of launchd's boot-tasks).
835 * This task is run with the quick and live options.
836 * On iOS we can assume all partitions are APFS or HFS.
837 * On OSX we run this only against the System volume which will always be HFS or APFS
838 */
839 bzero(options, sizeof(options));
840 snprintf(options, sizeof(options), "-%s%s%s%s%s%s%s",
841 (preen) ? "p" : "",
842 (debug) ? "d" : "",
843 (force_fsck) ? "f" : "",
844 (assume_no) ? "n" : "",
845 (assume_yes) ? "y" : "",
846 (quick_check) ? "q" : "",
847 (live_check) ? "l" : ""
848 );
849
850 argc = 0;
851 snprintf(progname, sizeof(progname), "fsck_%s", vfstype);
852 argv[argc++] = progname;
853 if (strlen(options) > 1) {
854 argv[argc++] = options;
855 }
856 argv[argc++] = filesys;
857 argv[argc] = NULL;
858
859 /* Create the string to the fsck binary */
860 (void)snprintf(execname, sizeof(execname), "%s/fsck_%s", _PATH_SBIN, vfstype);
861
862 /* Check that the binary exists */
863 error = stat (execname, &buf);
864 if (error != 0) {
865 fprintf(stderr, "Filesystem cannot be checked \n");
866 return EEXIT;
867 }
868
869 pid = fork();
870 switch (pid) {
871 case -1:
872 /* The fork failed. */
873 fprintf(stderr, "fork failed for %s \n", filesys);
874 if (preen) {
875 fprintf(stderr, "\n%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
876 filesys);
877 exit(EEXIT);
878 }
879
880 status = EEXIT;
881 break;
882
883 case 0:
884 /* The child */
885 if (preen) {
886 (void)signal(SIGQUIT, ignore_single_quit);
887 }
888 #if DEBUG
889 printf("exec: %s", execname);
890 for (int i = 1; i < argc; i++) {
891 printf(" %s", argv[i]);
892 }
893 printf("\n");
894 exit(0);
895 #endif
896 execv(execname, (char * const *)argv);
897 fprintf(stderr, "error attempting to exec %s\n", execname);
898 _exit(8);
899 break;
900
901 default:
902 /* The parent; child is process "pid" */
903 waitpid(pid, &exitstatus, 0);
904 if (WIFEXITED(exitstatus)) {
905 status = WEXITSTATUS(exitstatus);
906 }
907 else {
908 status = 0;
909 }
910 if (WIFSIGNALED(exitstatus)) {
911 printf("%s (%s) EXITED WITH SIGNAL %d\n", filesys, vfstype, WTERMSIG(exitstatus));
912 status = 8;
913 }
914 break;
915 }
916
917 return status;
918 }
919 else {
920 fprintf(stderr, "Filesystem cannot be checked \n");
921 return EEXIT;
922 }
923 }
924
925 /*
926 * When preening, allow a single quit to signal
927 * a special exit after filesystem checks complete
928 * so that reboot sequence may be interrupted.
929 */
930 void catchquit(int sig) {
931 extern int returntosingle;
932
933 printf("returning to single-user after filesystem check\n");
934 returntosingle = 1;
935 (void)signal(SIGQUIT, SIG_DFL);
936 }
937
938 /* Quit if we catch a signal here. Emit 12 */
939 void catchsig(int sig) {
940 exit (12);
941 }
942
943
944 /*
945 * Determine whether a filesystem should be checked.
946 *
947 * Zero indicates that no check should be performed.
948 */
949 int fs_checkable(struct fstab *fsp) {
950
951 /*
952 * APFS, HFS, MSDOS, exfat, and UDF are allowed for now.
953 */
954 if (strcmp(fsp->fs_vfstype, "apfs") &&
955 strcmp(fsp->fs_vfstype, "hfs") &&
956 strcmp(fsp->fs_vfstype, "msdos") &&
957 strcmp(fsp->fs_vfstype, "exfat") &&
958 strcmp(fsp->fs_vfstype, "udf")) {
959 return 0;
960 }
961
962 /* if not RW and not RO (SW or XX?), ignore it */
963 if ((strcmp(fsp->fs_type, FSTAB_RW) && strcmp(fsp->fs_type, FSTAB_RO)) ||
964 fsp->fs_passno == 0) {
965 return 0;
966 }
967
968 #define DISKARB_LABEL "LABEL="
969 #define DISKARB_UUID "UUID="
970 /* If LABEL or UUID specified, ignore it */
971 if ((strncmp(fsp->fs_spec, DISKARB_LABEL, strlen(DISKARB_LABEL)) == 0)
972 || (strncmp(fsp->fs_spec, DISKARB_UUID, strlen(DISKARB_UUID)) == 0)) {
973 return 0;
974 }
975
976 /* Otherwise, it looks fine. Go ahead and check! */
977 return 1;
978 }
979
980 /*
981 * Do some cursory checks on the pathname provided to ensure that it's really a block
982 * device. If it is, then generate the raw device name and vend it out.
983 */
984 char *blockcheck (char *origname) {
985 struct stat stslash;
986 struct stat stblock;
987 struct stat stchar;
988
989 char *newname;
990 char *raw;
991 int retried = 0;
992 int error = 0;
993
994 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
995 /* Variables for setting up the kqueue listener*/
996 #define TIMEOUT_SEC 30l
997 struct kevent kev;
998 struct kevent results;
999 struct timespec ts;
1000 int slashdev_fd;
1001 int kq = -1;
1002 int ct;
1003 time_t end;
1004 time_t now;
1005 #endif // (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1006
1007 hotroot = 0;
1008 /* Try to get device info for '/' */
1009 if (stat("/", &stslash) < 0) {
1010 perror("/");
1011 /* If we can't get any info on root, then bail out */
1012 printf("Can't stat root\n");
1013 return (origname);
1014 }
1015 newname = origname;
1016 #if TARGET_OS_IPHONE
1017 if (!strcmp(newname, RAMDISK_FS_SPEC)) {
1018 // Keyword ramdisk
1019 fprintf(stdout, "Encountered ramdisk definition. Do not stat\n");
1020 return (newname);
1021 }
1022 #endif // TARGET_OS_IPHONE
1023
1024 retry:
1025 /* Poke the block device argument */
1026 error = stat(newname, &stblock);
1027 if (error < 0) {
1028 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1029 /*
1030 * If the device node is not present, set up
1031 * a kqueue and wait for up to 30 seconds for it to be
1032 * published.
1033 */
1034 kq = kqueue();
1035 if (kq < 0) {
1036 printf("kqueue: could not create kqueue: %d\n", errno);
1037 printf("Can't stat %s\n", newname);
1038 return NULL;
1039 }
1040 slashdev_fd = open(_PATH_DEV, O_RDONLY);
1041
1042 EV_SET(&kev, slashdev_fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, NOTE_WRITE, 0, NULL);
1043 ct = kevent(kq, &kev, 1, NULL, 0, NULL);
1044 if (ct != 0) {
1045 printf("kevent() failed to register: %d\n", errno);
1046 printf("Can't stat %s\n", newname);
1047 /* If we can't register the kqueue, bail out */
1048 close (kq);
1049 kq = -1;
1050 return NULL;
1051 }
1052 now = time(NULL);
1053 end = now + TIMEOUT_SEC;
1054
1055 ts.tv_nsec = 0;
1056 while ((now = time(NULL)) < end) {
1057 ts.tv_sec = end - now;
1058 ct = kevent(kq, NULL, 0, &results, 1, &ts);
1059 if (results.flags & EV_ERROR) {
1060 /* If we register any errors, bail out */
1061 printf("kevent: registered errors.\n");
1062 error = -1;
1063 close (kq);
1064 kq = -1;
1065 break;
1066 }
1067 error = stat (newname, &stblock);
1068 if (error == 0) {
1069 /* found the item. continue on */
1070 if (kq >= 0) {
1071 close (kq);
1072 }
1073 break;
1074 }
1075 }
1076 if (error != 0) {
1077 /* Time out. bail out */
1078 if (kq >= 0) {
1079 close(kq);
1080 }
1081 printf("fsck timed out. Can't stat %s\n", newname);
1082 return NULL;
1083 }
1084
1085 #else //(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1086 perror(newname);
1087 printf("Can't stat %s\n", newname);
1088 return (NULL);
1089 #endif // (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1090 }
1091
1092 if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
1093 /*
1094 * If the block device we're checking is the same as '/' then
1095 * update hotroot global for debugging.
1096 */
1097 if (stslash.st_dev == stblock.st_rdev) {
1098 hotroot++;
1099 }
1100 raw = rawname(newname);
1101 if (stat(raw, &stchar) < 0) {
1102 perror(raw);
1103 printf("Can't stat %s\n", raw);
1104 return (origname);
1105 }
1106 if ((stchar.st_mode & S_IFMT) == S_IFCHR) {
1107 return (raw);
1108 } else {
1109 printf("%s is not a character device\n", raw);
1110 return (origname);
1111 }
1112 } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) {
1113 newname = unrawname(newname);
1114 retried++;
1115 goto retry;
1116 }
1117 /*
1118 * Not a block or character device, return NULL and
1119 * let the user decide what to do.
1120 */
1121 return (NULL);
1122 }
1123
1124
1125 /*
1126 * Generate a raw disk device pathname from a normal one.
1127 *
1128 * For input /dev/disk1s2, generate /dev/rdisk1s2
1129 */
1130 char *rawname(char *name) {
1131 static char rawbuf[32];
1132 char *dp;
1133
1134 /*
1135 * Search for the last '/' in the pathname.
1136 * If it's not there, then bail out
1137 */
1138 if ((dp = strrchr(name, '/')) == 0) {
1139 return (0);
1140 }
1141 /*
1142 * Insert a NULL in the place of the final '/' so that we can
1143 * copy everything BEFORE that last '/' into a separate buffer.
1144 */
1145 *dp = 0;
1146 (void)strlcpy(rawbuf, name, sizeof(rawbuf));
1147 *dp = '/';
1148 /* Now add an /r to our buffer, then copy everything after the final / */
1149 (void)strlcat(rawbuf, "/r", sizeof(rawbuf));
1150 (void)strlcat(rawbuf, &dp[1], sizeof(rawbuf));
1151 return (rawbuf);
1152 }
1153
1154 /*
1155 * Generate a regular disk device name from a raw one.
1156 *
1157 * For input /dev/rdisk1s2, generate /dev/disk1s2
1158 */
1159 char *unrawname(char *name) {
1160 char *dp;
1161 struct stat stb;
1162 size_t length;
1163
1164 /* Find the last '/' in the pathname */
1165 if ((dp = strrchr(name, '/')) == 0) {
1166 return (name);
1167 }
1168
1169 /* Stat the disk device argument */
1170 if (stat(name, &stb) < 0) {
1171 return (name);
1172 }
1173
1174 /* If it's not a character device, error out */
1175 if ((stb.st_mode & S_IFMT) != S_IFCHR) {
1176 return (name);
1177 }
1178
1179 /* If it's not a real raw name, then error out */
1180 if (dp[1] != 'r') {
1181 return (name);
1182 }
1183 length = strlen(&dp[2]);
1184 length++; /* to account for trailing NULL */
1185
1186 memmove(&dp[1], &dp[2], length);
1187 return (name);
1188 }
1189
1190 /*
1191 * Given a pathname to a disk device, generate the relevant disk_t for that
1192 * disk device. It is assumed that this function will be called for each item in the
1193 * fstab that needs to get checked.
1194 */
1195 disk_t *finddisk (char *pathname) {
1196 disk_t *disk;
1197 disk_t **dkp;
1198 char *tmp;
1199 size_t len;
1200
1201 /*
1202 * Find the disk name. It is assumed that the disk name ends with the
1203 * first run of digit(s) in the last component of the path.
1204 */
1205 tmp = strrchr(pathname, '/'); /* Find the last component of the path */
1206 if (tmp == NULL) {
1207 tmp = pathname;
1208 }
1209 else {
1210 tmp++;
1211 }
1212 for (; *tmp && !isdigit(*tmp); tmp++) { /* Skip non-digits */
1213 continue;
1214 }
1215
1216 for (; *tmp && isdigit(*tmp); tmp++){ /* Skip to end of consecutive digits */
1217 continue;
1218 }
1219
1220 len = tmp - pathname;
1221 if (len == 0) {
1222 len = strlen(pathname);
1223 }
1224
1225 /* Iterate through all known disks to see if this item was already seen before */
1226 for (disk = disklist, dkp = &disklist; disk; dkp = &disk->next, disk = disk->next) {
1227 if ((strncmp(disk->name, pathname, len) == 0) &&
1228 (disk->name[len] == 0)) {
1229 return (disk);
1230 }
1231 }
1232 /* If not, then allocate a new structure and add it to the end of the list */
1233 if ((*dkp = (disk_t*)malloc(sizeof(disk_t))) == NULL) {
1234 fprintf(stderr, "out of memory");
1235 exit (8);
1236 }
1237 /* Make 'disk' point to the newly allocated structure */
1238 disk = *dkp;
1239 if ((disk->name = malloc(len + 1)) == NULL) {
1240 fprintf(stderr, "out of memory");
1241 exit (8);
1242 }
1243 /* copy the name into place */
1244 (void)strncpy(disk->name, pathname, len);
1245 disk->name[len] = '\0';
1246 /* Initialize 'part' and 'next' to NULL for now */
1247 disk->part = NULL;
1248 disk->next = NULL;
1249 disk->pid = 0;
1250 /* Increase total number of disks observed */
1251 ndisks++;
1252
1253 /* Bubble out either the newly created disk_t or the one we found */
1254 return (disk);
1255 }
1256
1257
1258 /*
1259 * Add this partition to the list of devices to check.
1260 */
1261 void addpart(char *name, char *fsname, char *vfstype) {
1262 disk_t *disk;
1263 part_t *part;
1264 part_t **ppt;
1265
1266 /* Find the disk_t that corresponds to our element */
1267 disk = finddisk(name);
1268 ppt = &(disk->part);
1269
1270 /*
1271 * Now iterate through all of the partitions of that disk.
1272 * If we see our partition name already in there, then it means the entry
1273 * was in the fstab more than once, which is bad.
1274 */
1275 for (part = disk->part; part; ppt = &part->next, part = part->next) {
1276 if (strcmp(part->name, name) == 0) {
1277 printf("%s in fstab more than once!\n", name);
1278 return;
1279 }
1280 }
1281
1282 /* Hopefully we get here. Allocate a new partition structure for the disk */
1283 if ((*ppt = (part_t*)malloc(sizeof(part_t))) == NULL) {
1284 fprintf(stderr, "out of memory");
1285 exit (8);
1286 }
1287 part = *ppt;
1288 if ((part->name = strdup(name)) == NULL) {
1289 fprintf(stderr, "out of memory");
1290 exit (8);
1291 }
1292
1293 /* Add the name & vfs info to the partition struct */
1294 if ((part->fsname = strdup(fsname)) == NULL) {
1295 fprintf(stderr, "out of memory");
1296 exit (8);
1297 }
1298 part->next = NULL;
1299 part->vfstype = strdup(vfstype);
1300 if (part->vfstype == NULL) {
1301 fprintf(stderr, "out of memory");
1302 exit (8);
1303 }
1304 }
1305
1306 /*
1307 * Free the partition and its fields.
1308 */
1309 void destroy_part (part_t *part) {
1310 if (part->name) {
1311 free (part->name);
1312 }
1313
1314 if (part->fsname) {
1315 free (part->fsname);
1316 }
1317
1318 if (part->vfstype) {
1319 free (part->vfstype);
1320 }
1321
1322 free (part);
1323 }
1324
1325
1326 /*
1327 * Ignore a single quit signal; wait and flush just in case.
1328 * Used by child processes in preen mode.
1329 */
1330 void
1331 ignore_single_quit(int sig) {
1332
1333 sleep(1);
1334 (void)signal(SIGQUIT, SIG_IGN);
1335 (void)signal(SIGQUIT, SIG_DFL);
1336 }
1337
1338
1339