xref: /illumos-gate/usr/src/cmd/fs.d/pcfs/fsck/fsck.c (revision 1e56f352c1c208679012bca47d552e127f5b1072)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1999,2001 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 /*
28  * fsck_pcfs -- main routines.
29  */
30 
31 #include <stdio.h>
32 #include <errno.h>
33 #include <stdlib.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <strings.h>
38 #include <libintl.h>
39 #include <locale.h>
40 #include <sys/fcntl.h>
41 #include <sys/dktp/fdisk.h>
42 #include "pcfs_common.h"
43 #include "fsck_pcfs.h"
44 #include "pcfs_bpb.h"
45 
46 int32_t BytesPerCluster;
47 int32_t TotalClusters;
48 int32_t LastCluster;
49 off64_t	FirstClusterOffset;
50 off64_t	PartitionOffset;
51 bpb_t	TheBIOSParameterBlock;
52 
53 /*
54  * {Output,Input}Image are the file names where we should write the
55  * checked fs image and from which we should read the initial fs.
56  * The image capability is designed for debugging purposes.
57  */
58 static char	*OutputImage = NULL;
59 static char	*InputImage = NULL;
60 static int	WritableOnly = 0; /* -o w, check writable fs' only */
61 static int	Mflag = 0;	  /* -m, sanity check if fs is mountable */
62 static int	Preen = 0;	  /* -o p, preen; non-interactive */
63 /*
64  * By default be quick; skip verify reads.
65  * If the user wants more exhaustive checking,
66  * they should run with the -o v option.
67  */
68 static int	Quick = 1;
69 
70 int	ReadOnly = 0;
71 int	IsFAT32 = 0;
72 int	Verbose = 0;
73 
74 int	AlwaysYes = 0;	/* -y or -Y, assume a yes answer to all questions */
75 int	AlwaysNo = 0;	/* -n or -N, assume a no answer to all questions */
76 
77 extern	ClusterContents	TheRootDir;
78 
79 /*
80  * Function definitions
81  */
82 static void
83 passOne(int fd)
84 {
85 	if (!Quick)
86 		findBadClusters(fd);
87 	scanAndFixMetadata(fd);
88 }
89 
90 static void
91 writeBackChanges(int fd)
92 {
93 	writeFATMods(fd);
94 	if (!IsFAT32)
95 		writeRootDirMods(fd);
96 	writeClusterMods(fd);
97 }
98 
99 static void
100 tryOpen(int *fd, char *openMe, int oflag, int exitOnFailure)
101 {
102 	int saveError;
103 
104 	if ((*fd = open(openMe, oflag)) < 0) {
105 		if (exitOnFailure == RETURN_ON_OPEN_FAILURE)
106 			return;
107 		saveError = errno;
108 		mountSanityCheckFails();
109 		(void) fprintf(stderr, "%s: ", openMe);
110 		(void) fprintf(stderr, strerror(saveError));
111 		(void) fprintf(stderr, "\n");
112 		exit(1);
113 	}
114 }
115 
116 static void
117 doOpen(int *inFD, int *outFD, char *name, char *outName)
118 {
119 	if (ReadOnly) {
120 		tryOpen(inFD, name, O_RDONLY, EXIT_ON_OPEN_FAILURE);
121 		*outFD = -1;
122 	} else {
123 		tryOpen(inFD, name, O_RDWR, RETURN_ON_OPEN_FAILURE);
124 		if (*inFD < 0) {
125 			if (errno != EACCES || WritableOnly) {
126 				int saveError = errno;
127 				mountSanityCheckFails();
128 				(void) fprintf(stderr,
129 				    gettext("%s: "), name);
130 				(void) fprintf(stderr, strerror(saveError));
131 				(void) fprintf(stderr, "\n");
132 				exit(2);
133 			} else {
134 				tryOpen(inFD, name, O_RDONLY,
135 				    EXIT_ON_OPEN_FAILURE);
136 				AlwaysYes = 0;
137 				AlwaysNo = 1;
138 				ReadOnly = 1;
139 				*outFD = -1;
140 			}
141 		} else {
142 			*outFD = *inFD;
143 		}
144 	}
145 
146 	if (outName != NULL) {
147 		tryOpen(outFD, outName, (O_RDWR | O_CREAT),
148 		    EXIT_ON_OPEN_FAILURE);
149 	}
150 
151 	(void) printf("** %s %s\n", name,
152 	    ReadOnly ? gettext("(NO WRITE)") : "");
153 }
154 
155 static void
156 openFS(char *special, int *inFD, int *outFD)
157 {
158 	struct stat dinfo;
159 	char *actualDisk = NULL;
160 	char *suffix = NULL;
161 
162 	if (Verbose)
163 		(void) fprintf(stderr, gettext("Opening file system.\n"));
164 
165 	if (InputImage == NULL) {
166 		actualDisk = stat_actual_disk(special, &dinfo, &suffix);
167 		/*
168 		 *  Destination exists, now find more about it.
169 		 */
170 		if (!(S_ISCHR(dinfo.st_mode))) {
171 			mountSanityCheckFails();
172 			(void) fprintf(stderr,
173 			    gettext("\n%s: device name must be a "
174 			    "character special device.\n"), actualDisk);
175 			exit(2);
176 		}
177 	} else {
178 		actualDisk = InputImage;
179 	}
180 	doOpen(inFD, outFD, actualDisk, OutputImage);
181 	if (suffix) {
182 		if ((PartitionOffset =
183 		    findPartitionOffset(*inFD, suffix)) < 0) {
184 			mountSanityCheckFails();
185 			(void) fprintf(stderr,
186 			    gettext("Unable to find logical drive %s\n"),
187 			    suffix);
188 			exit(2);
189 		} else if (Verbose) {
190 			(void) fprintf(stderr,
191 			    gettext("Partition starts at offset %lld\n"),
192 			    PartitionOffset);
193 		}
194 	} else {
195 		PartitionOffset = 0;
196 	}
197 }
198 
199 void
200 usage()
201 {
202 	(void) fprintf(stderr,
203 		gettext("pcfs Usage: fsck -F pcfs [-o v|p|w] special-file\n"));
204 	exit(1);
205 }
206 
207 static
208 char *LegalOpts[] = {
209 #define	VFLAG 0
210 	"v",
211 #define	PFLAG 1
212 	"p",
213 #define	WFLAG 2
214 	"w",
215 #define	DFLAG 3
216 	"d",
217 #define	IFLAG 4
218 	"i",
219 #define	OFLAG 5
220 	"o",
221 	NULL
222 };
223 
224 static void
225 parseSubOptions(char *optsstr)
226 {
227 	char *value;
228 	int c;
229 
230 	while (*optsstr != '\0') {
231 		switch (c = getsubopt(&optsstr, LegalOpts, &value)) {
232 		case VFLAG:
233 			Quick = 0;
234 			break;
235 		case PFLAG:
236 			Preen++;
237 			break;
238 		case WFLAG:
239 			WritableOnly++;
240 			break;
241 		case DFLAG:
242 			Verbose++;
243 			break;
244 		case IFLAG:
245 			if (value == NULL) {
246 				missing_arg(LegalOpts[c]);
247 			} else {
248 				InputImage = value;
249 			}
250 			break;
251 		case OFLAG:
252 			if (value == NULL) {
253 				missing_arg(LegalOpts[c]);
254 			} else {
255 				OutputImage = value;
256 			}
257 			break;
258 		default:
259 			bad_arg(value);
260 			break;
261 		}
262 	}
263 }
264 
265 static void
266 sanityCheckOpts(void)
267 {
268 	if (WritableOnly && ReadOnly) {
269 		(void) fprintf(stderr,
270 		    gettext("-w option may not be used with the -n "
271 		    "or -m options\n"));
272 		exit(4);
273 	}
274 }
275 
276 static void
277 confirmMountable(char *special, int fd)
278 {
279 	char *printName;
280 	int okayToMount = 1;
281 
282 	printName = InputImage ? InputImage : special;
283 
284 	if (!IsFAT32) {
285 		/* make sure we can at least read the root directory */
286 		getRootDirectory(fd);
287 		if (TheRootDir.bytes == NULL)
288 			okayToMount = 0;
289 	} else {
290 		/* check the bit designed into FAT32 for this purpose */
291 		okayToMount = checkFAT32CleanBit(fd);
292 	}
293 	if (okayToMount) {
294 		(void) fprintf(stderr,
295 		    gettext("pcfs fsck: sanity check: %s okay\n"), printName);
296 		exit(0);
297 	} else {
298 		(void) fprintf(stderr,
299 		    gettext("pcfs fsck: sanity check: %s needs checking\n"),
300 		    printName);
301 		exit(32);
302 	}
303 }
304 
305 void
306 mountSanityCheckFails(void)
307 {
308 	if (Mflag) {
309 		(void) fprintf(stderr,
310 		    gettext("pcfs fsck: sanity check failed: "));
311 	}
312 }
313 
314 /*
315  * preenBail
316  *	Routine that other routines can call if they would go into a
317  *	state where they need user input.  They can send an optional
318  *	message string to be printed before the exit.  Caller should
319  *	send a NULL string if they don't have an exit message.
320  */
321 void
322 preenBail(char *outString)
323 {
324 	/*
325 	 *  If we are running in the 'preen' mode, we got here because
326 	 *  we reached a situation that would require user intervention.
327 	 *  We have no choice but to bail at this point.
328 	 */
329 	if (Preen) {
330 		if (outString)
331 			(void) printf("%s", outString);
332 		(void) printf(gettext("FILE SYSTEM FIX REQUIRES USER "
333 		    "INTERVENTION; RUN fsck MANUALLY.\n"));
334 		exit(36);
335 	}
336 }
337 
338 int
339 main(int argc, char *argv[])
340 {
341 	char *string;
342 	int  ifd, ofd;
343 	int  c;
344 
345 	(void) setlocale(LC_ALL, "");
346 
347 #if !defined(TEXT_DOMAIN)
348 #define	TEXT_DOMAIN "SYS_TEST"
349 #endif
350 	(void) textdomain(TEXT_DOMAIN);
351 
352 	if (argc < 2)
353 		usage();
354 
355 	while ((c = getopt(argc, argv, "F:VYNynmo:")) != EOF) {
356 		switch (c) {
357 		case 'F':
358 			string = optarg;
359 			if (strcmp(string, "pcfs") != 0)
360 				usage();
361 			break;
362 		case 'V': {
363 				char	*opt_text;
364 				int	opt_count;
365 
366 				(void) printf(gettext("fsck -F pcfs "));
367 				for (opt_count = 1; opt_count < argc;
368 				    opt_count++) {
369 					opt_text = argv[opt_count];
370 					if (opt_text)
371 						(void) printf(" %s ",
372 						    opt_text);
373 				}
374 				(void) printf("\n");
375 				exit(0);
376 			}
377 			break;
378 		case 'N':
379 		case 'n':
380 			AlwaysYes = 0;
381 			AlwaysNo = 1;
382 			ReadOnly = 1;
383 			break;
384 		case 'Y':
385 		case 'y':
386 			AlwaysYes = 1;
387 			AlwaysNo = 0;
388 			break;
389 		case 'm':
390 			Mflag++;
391 			ReadOnly = 1;
392 			break;
393 		case 'o':
394 			string = optarg;
395 			parseSubOptions(string);
396 			break;
397 		}
398 	}
399 
400 	sanityCheckOpts();
401 	if (InputImage == NULL && (optind < 0 || optind >= argc))
402 		usage();
403 
404 	openFS(argv[optind], &ifd, &ofd);
405 	readBPB(ifd);
406 
407 	/*
408 	 * -m mountable fs check.  This call will not return.
409 	 */
410 	if (Mflag)
411 		confirmMountable(argv[optind], ifd);
412 
413 	/*
414 	 *  Pass 1: Find any bad clusters and adjust the FAT and directory
415 	 *	entries accordingly
416 	 */
417 	passOne(ifd);
418 
419 	/*
420 	 *  XXX - future passes?
421 	 *	Ideas:
422 	 *	    Data relocation for bad clusters with partial read success?
423 	 *	    Syncing backup FAT copies with main copy?
424 	 *	    Syncing backup root sector for FAT32?
425 	 */
426 
427 	/*
428 	 *  No problems if we made it this far.
429 	 */
430 	printSummary(stdout);
431 	writeBackChanges(ofd);
432 	return (0);
433 }
434