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