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
passOne(int fd)83 passOne(int fd)
84 {
85 if (!Quick)
86 findBadClusters(fd);
87 scanAndFixMetadata(fd);
88 }
89
90 static void
writeBackChanges(int fd)91 writeBackChanges(int fd)
92 {
93 writeFATMods(fd);
94 if (!IsFAT32)
95 writeRootDirMods(fd);
96 writeClusterMods(fd);
97 }
98
99 static void
tryOpen(int * fd,char * openMe,int oflag,int exitOnFailure)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
doOpen(int * inFD,int * outFD,char * name,char * outName)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
openFS(char * special,int * inFD,int * outFD)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
usage()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
parseSubOptions(char * optsstr)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
sanityCheckOpts(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
confirmMountable(char * special,int fd)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
mountSanityCheckFails(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
preenBail(char * outString)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
main(int argc,char * argv[])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