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 85 passOne(int fd) 86 { 87 if (!Quick) 88 findBadClusters(fd); 89 scanAndFixMetadata(fd); 90 } 91 92 static void 93 writeBackChanges(int fd) 94 { 95 writeFATMods(fd); 96 if (!IsFAT32) 97 writeRootDirMods(fd); 98 writeClusterMods(fd); 99 } 100 101 static void 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 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 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 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 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 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 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 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 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 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