1 /* 2 * Copyright (c) 1980, 1986 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that the above copyright notice and this paragraph are 7 * duplicated in all such forms and that any documentation, 8 * advertising materials, and other materials related to such 9 * distribution and use acknowledge that the software was developed 10 * by the University of California, Berkeley. The name of the 11 * University may not be used to endorse or promote products derived 12 * from this software without specific prior written permission. 13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 */ 17 18 #ifndef lint 19 char copyright[] = 20 "@(#) Copyright (c) 1980, 1986 The Regents of the University of California.\n\ 21 All rights reserved.\n"; 22 #endif /* not lint */ 23 24 #ifndef lint 25 static char sccsid[] = "@(#)main.c 5.19 (Berkeley) 02/08/90"; 26 #endif /* not lint */ 27 28 #include <sys/param.h> 29 #include <ufs/dinode.h> 30 #include <ufs/fs.h> 31 #include <sys/stat.h> 32 #include <sys/wait.h> 33 #include <fstab.h> 34 #include <strings.h> 35 #include <ctype.h> 36 #include "fsck.h" 37 38 char *rawname(), *unrawname(), *blockcheck(), *malloc(); 39 void catch(), catchquit(), voidquit(); 40 int returntosingle; 41 42 struct part { 43 char *name; /* device name */ 44 char *fsname; /* mounted filesystem name */ 45 struct part *next; /* forward link of partitions on disk */ 46 } *badlist, **badnext = &badlist; 47 48 struct disk { 49 char *name; /* disk base name */ 50 struct disk *next; /* forward link for list of disks */ 51 struct part *part; /* head of list of partitions on disk */ 52 int pid; /* If != 0, pid of proc working on */ 53 } *disks; 54 55 int nrun, ndisks, maxrun; 56 57 main(argc, argv) 58 int argc; 59 char *argv[]; 60 { 61 struct fstab *fsp; 62 int pid, passno, sumstatus; 63 char *name; 64 register struct disk *dk, *nextdisk; 65 register struct part *pt; 66 67 sync(); 68 while (--argc > 0 && **++argv == '-') { 69 switch (*++*argv) { 70 71 case 'p': 72 preen++; 73 break; 74 75 case 'b': 76 if (argv[0][1] != '\0') { 77 bflag = atoi(argv[0]+1); 78 } else { 79 bflag = atoi(*++argv); 80 argc--; 81 } 82 printf("Alternate super block location: %d\n", bflag); 83 break; 84 85 case 'c': 86 cvtflag++; 87 break; 88 89 case 'd': 90 debug++; 91 break; 92 93 case 'l': 94 if (!isdigit(argv[1][0])) 95 errexit("-l flag requires a number\n"); 96 maxrun = atoi(*++argv); 97 argc--; 98 break; 99 100 case 'm': 101 if (!isdigit(argv[1][0])) 102 errexit("-m flag requires a mode\n"); 103 sscanf(*++argv, "%o", &lfmode); 104 if (lfmode &~ 07777) 105 errexit("bad mode to -m: %o\n", lfmode); 106 argc--; 107 printf("** lost+found creation mode %o\n", lfmode); 108 break; 109 110 case 'n': 111 case 'N': 112 nflag++; 113 yflag = 0; 114 break; 115 116 case 'y': 117 case 'Y': 118 yflag++; 119 nflag = 0; 120 break; 121 122 default: 123 errexit("%c option?\n", **argv); 124 } 125 } 126 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 127 (void)signal(SIGINT, catch); 128 if (preen) 129 (void)signal(SIGQUIT, catchquit); 130 if (argc) { 131 while (argc-- > 0) { 132 hotroot = 0; 133 checkfilesys(*argv++); 134 } 135 exit(0); 136 } 137 sumstatus = 0; 138 for (passno = 1; passno <= 2; passno++) { 139 if (setfsent() == 0) 140 errexit("Can't open checklist file: %s\n", _PATH_FSTAB); 141 while ((fsp = getfsent()) != 0) { 142 if (strcmp(fsp->fs_type, FSTAB_RW) && 143 strcmp(fsp->fs_type, FSTAB_RO) && 144 strcmp(fsp->fs_type, FSTAB_RQ)) 145 continue; 146 if (strcmp(fsp->fs_vfstype, "ufs")) 147 continue; 148 if (preen == 0 || 149 passno == 1 && fsp->fs_passno == 1) { 150 name = blockcheck(fsp->fs_spec); 151 if (name != NULL) 152 checkfilesys(name); 153 else if (preen) 154 exit(8); 155 } else if (passno == 2 && fsp->fs_passno > 1) { 156 name = blockcheck(fsp->fs_spec); 157 if (name == NULL) { 158 pwarn("BAD DISK NAME %s\n", 159 fsp->fs_spec); 160 sumstatus |= 8; 161 continue; 162 } 163 addpart(name, fsp->fs_file); 164 } 165 } 166 } 167 if (preen) { 168 union wait status; 169 170 if (maxrun == 0) 171 maxrun = ndisks; 172 if (maxrun > ndisks) 173 maxrun = ndisks; 174 nextdisk = disks; 175 for (passno = 0; passno < maxrun; ++passno) { 176 startdisk(nextdisk); 177 nextdisk = nextdisk->next; 178 } 179 while ((pid = wait(&status)) != -1) { 180 for (dk = disks; dk; dk = dk->next) 181 if (dk->pid == pid) 182 break; 183 if (dk == 0) { 184 printf("Unknown pid %d\n", pid); 185 continue; 186 } 187 if (status.w_termsig) { 188 printf("%s (%s): EXITED WITH SIGNAL %d\n", 189 dk->part->name, dk->part->fsname, 190 status.w_termsig); 191 status.w_retcode = 8; 192 } 193 if (status.w_retcode != 0) { 194 sumstatus |= status.w_retcode; 195 *badnext = dk->part; 196 badnext = &dk->part->next; 197 dk->part = dk->part->next; 198 *badnext = NULL; 199 } else 200 dk->part = dk->part->next; 201 dk->pid = 0; 202 nrun--; 203 if (dk->part == NULL) 204 ndisks--; 205 206 if (nextdisk == NULL) { 207 if (dk->part) 208 startdisk(dk); 209 } else if (nrun < maxrun && nrun < ndisks) { 210 for ( ;; ) { 211 if ((nextdisk = nextdisk->next) == NULL) 212 nextdisk = disks; 213 if (nextdisk->part != NULL && 214 nextdisk->pid == 0) 215 break; 216 } 217 startdisk(nextdisk); 218 } 219 } 220 } 221 if (sumstatus) { 222 if (badlist == 0) 223 exit(8); 224 printf("THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t", 225 badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:"); 226 for (pt = badlist; pt; pt = pt->next) 227 printf("%s (%s)%s", pt->name, pt->fsname, 228 pt->next ? ", " : "\n"); 229 exit(8); 230 } 231 (void)endfsent(); 232 if (returntosingle) 233 exit(2); 234 exit(0); 235 } 236 237 struct disk * 238 finddisk(name) 239 char *name; 240 { 241 register struct disk *dk, **dkp; 242 register char *p; 243 int len; 244 245 for (p = name + strlen(name) - 1; p >= name; --p) 246 if (isdigit(*p)) { 247 len = p - name + 1; 248 break; 249 } 250 if (p < name) 251 len = strlen(name); 252 253 for (dk = disks, dkp = &disks; dk; dkp = &dk->next, dk = dk->next) { 254 if (strncmp(dk->name, name, len) == 0 && 255 dk->name[len] == 0) 256 return (dk); 257 } 258 if ((*dkp = (struct disk *)malloc(sizeof(struct disk))) == NULL) 259 errexit("out of memory"); 260 dk = *dkp; 261 if ((dk->name = malloc((unsigned int)len + 1)) == NULL) 262 errexit("out of memory"); 263 strncpy(dk->name, name, len); 264 dk->name[len] = '\0'; 265 dk->part = NULL; 266 dk->next = NULL; 267 dk->pid = 0; 268 ndisks++; 269 return (dk); 270 } 271 272 addpart(name, fsname) 273 char *name, *fsname; 274 { 275 struct disk *dk = finddisk(name); 276 register struct part *pt, **ppt = &dk->part; 277 278 for (pt = dk->part; pt; ppt = &pt->next, pt = pt->next) 279 if (strcmp(pt->name, name) == 0) { 280 printf("%s in fstab more than once!\n", name); 281 return; 282 } 283 if ((*ppt = (struct part *)malloc(sizeof(struct part))) == NULL) 284 errexit("out of memory"); 285 pt = *ppt; 286 if ((pt->name = malloc((unsigned int)strlen(name) + 1)) == NULL) 287 errexit("out of memory"); 288 strcpy(pt->name, name); 289 if ((pt->fsname = malloc((unsigned int)strlen(fsname) + 1)) == NULL) 290 errexit("out of memory"); 291 strcpy(pt->fsname, fsname); 292 pt->next = NULL; 293 } 294 295 startdisk(dk) 296 register struct disk *dk; 297 { 298 299 nrun++; 300 dk->pid = fork(); 301 if (dk->pid < 0) { 302 perror("fork"); 303 exit(8); 304 } 305 if (dk->pid == 0) { 306 (void)signal(SIGQUIT, voidquit); 307 checkfilesys(dk->part->name); 308 exit(0); 309 } 310 } 311 312 checkfilesys(filesys) 313 char *filesys; 314 { 315 daddr_t n_ffree, n_bfree; 316 struct dups *dp; 317 struct zlncnt *zlnp; 318 319 devname = filesys; 320 if (debug && preen) 321 pwarn("starting\n"); 322 if (setup(filesys) == 0) { 323 if (preen) 324 pfatal("CAN'T CHECK FILE SYSTEM."); 325 return; 326 } 327 /* 328 * 1: scan inodes tallying blocks used 329 */ 330 if (preen == 0) { 331 printf("** Last Mounted on %s\n", sblock.fs_fsmnt); 332 if (hotroot) 333 printf("** Root file system\n"); 334 printf("** Phase 1 - Check Blocks and Sizes\n"); 335 } 336 pass1(); 337 338 /* 339 * 1b: locate first references to duplicates, if any 340 */ 341 if (duplist) { 342 if (preen) 343 pfatal("INTERNAL ERROR: dups with -p"); 344 printf("** Phase 1b - Rescan For More DUPS\n"); 345 pass1b(); 346 } 347 348 /* 349 * 2: traverse directories from root to mark all connected directories 350 */ 351 if (preen == 0) 352 printf("** Phase 2 - Check Pathnames\n"); 353 pass2(); 354 355 /* 356 * 3: scan inodes looking for disconnected directories 357 */ 358 if (preen == 0) 359 printf("** Phase 3 - Check Connectivity\n"); 360 pass3(); 361 362 /* 363 * 4: scan inodes looking for disconnected files; check reference counts 364 */ 365 if (preen == 0) 366 printf("** Phase 4 - Check Reference Counts\n"); 367 pass4(); 368 369 /* 370 * 5: check and repair resource counts in cylinder groups 371 */ 372 if (preen == 0) 373 printf("** Phase 5 - Check Cyl groups\n"); 374 pass5(); 375 376 /* 377 * print out summary statistics 378 */ 379 n_ffree = sblock.fs_cstotal.cs_nffree; 380 n_bfree = sblock.fs_cstotal.cs_nbfree; 381 pwarn("%d files, %d used, %d free ", 382 n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree); 383 printf("(%d frags, %d blocks, %.1f%% fragmentation)\n", 384 n_ffree, n_bfree, (float)(n_ffree * 100) / sblock.fs_dsize); 385 if (debug && 386 (n_files -= maxino - ROOTINO - sblock.fs_cstotal.cs_nifree)) 387 printf("%d files missing\n", n_files); 388 if (debug) { 389 n_blks += sblock.fs_ncg * 390 (cgdmin(&sblock, 0) - cgsblock(&sblock, 0)); 391 n_blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0); 392 n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize); 393 if (n_blks -= maxfsblock - (n_ffree + sblock.fs_frag * n_bfree)) 394 printf("%d blocks missing\n", n_blks); 395 if (duplist != NULL) { 396 printf("The following duplicate blocks remain:"); 397 for (dp = duplist; dp; dp = dp->next) 398 printf(" %d,", dp->dup); 399 printf("\n"); 400 } 401 if (zlnhead != NULL) { 402 printf("The following zero link count inodes remain:"); 403 for (zlnp = zlnhead; zlnp; zlnp = zlnp->next) 404 printf(" %d,", zlnp->zlncnt); 405 printf("\n"); 406 } 407 } 408 zlnhead = (struct zlncnt *)0; 409 duplist = (struct dups *)0; 410 inocleanup(); 411 if (fsmodified) { 412 (void)time(&sblock.fs_time); 413 sbdirty(); 414 } 415 ckfini(); 416 free(blockmap); 417 free(statemap); 418 free((char *)lncntp); 419 if (!fsmodified) 420 return; 421 if (!preen) { 422 printf("\n***** FILE SYSTEM WAS MODIFIED *****\n"); 423 if (hotroot) 424 printf("\n***** REBOOT UNIX *****\n"); 425 } 426 if (hotroot) { 427 sync(); 428 exit(4); 429 } 430 } 431 432 char * 433 blockcheck(name) 434 char *name; 435 { 436 struct stat stslash, stblock, stchar; 437 char *raw; 438 int retried = 0; 439 440 hotroot = 0; 441 if (stat("/", &stslash) < 0) { 442 perror("/"); 443 printf("Can't stat root\n"); 444 return (0); 445 } 446 retry: 447 if (stat(name, &stblock) < 0) { 448 perror(name); 449 printf("Can't stat %s\n", name); 450 return (0); 451 } 452 if ((stblock.st_mode & S_IFMT) == S_IFBLK) { 453 if (stslash.st_dev == stblock.st_rdev) { 454 hotroot++; 455 return (name); 456 } 457 raw = rawname(name); 458 if (stat(raw, &stchar) < 0) { 459 perror(raw); 460 printf("Can't stat %s\n", raw); 461 return (name); 462 } 463 if ((stchar.st_mode & S_IFMT) == S_IFCHR) { 464 return (raw); 465 } else { 466 printf("%s is not a character device\n", raw); 467 return (name); 468 } 469 } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) { 470 name = unrawname(name); 471 retried++; 472 goto retry; 473 } 474 printf("Can't make sense out of name %s\n", name); 475 return (0); 476 } 477 478 char * 479 unrawname(name) 480 char *name; 481 { 482 char *dp; 483 struct stat stb; 484 485 if ((dp = rindex(name, '/')) == 0) 486 return (name); 487 if (stat(name, &stb) < 0) 488 return (name); 489 if ((stb.st_mode & S_IFMT) != S_IFCHR) 490 return (name); 491 if (*(dp + 1) != 'r') 492 return (name); 493 (void)strcpy(dp + 1, dp + 2); 494 return (name); 495 } 496 497 char * 498 rawname(name) 499 char *name; 500 { 501 static char rawbuf[32]; 502 char *dp; 503 504 if ((dp = rindex(name, '/')) == 0) 505 return (0); 506 *dp = 0; 507 (void)strcpy(rawbuf, name); 508 *dp = '/'; 509 (void)strcat(rawbuf, "/r"); 510 (void)strcat(rawbuf, dp + 1); 511 return (rawbuf); 512 } 513