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