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