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