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