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