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