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