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