death to register
[openafs.git] / src / vfsck / main.c
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 #include <afsconfig.h>
19 #include <afs/param.h>
20
21
22 #define VICE                    /* allow us to put our changes in at will */
23 #include <stdio.h>
24
25 #include <sys/param.h>
26 #include <sys/time.h>
27
28 #ifdef AFS_SUN_ENV
29 #define KERNEL
30 #endif /* AFS_SUN_ENV */
31 #include <sys/mount.h>
32 #ifdef AFS_SUN_ENV
33 #undef KERNEL
34 #endif
35
36 #include <sys/file.h>
37
38 #ifdef  AFS_OSF_ENV
39 #include <sys/vnode.h>
40 #include <sys/mount.h>
41 #include <ufs/inode.h>
42 #include <ufs/fs.h>
43 #else /* AFS_OSF_ENV */
44 #ifdef AFS_VFSINCL_ENV
45 #define VFS
46 #include <sys/vnode.h>
47 #ifdef    AFS_SUN5_ENV
48 #include <unistd.h>
49 #include <sys/fs/ufs_inode.h>
50 #include <sys/fs/ufs_fs.h>
51 #define _KERNEL
52 #include <sys/fs/ufs_fsdir.h>
53 #undef _KERNEL
54 #include <sys/fs/ufs_mount.h>
55 #else
56 #include <ufs/inode.h>
57 #include <ufs/fs.h>
58 #define KERNEL
59 #include <ufs/fsdir.h>
60 #undef KERNEL
61 #endif
62 #else /* AFS_VFSINCL_ENV */
63
64 #include <sys/inode.h>
65 #ifdef  AFS_HPUX_ENV
66 #include <ctype.h>
67 #define LONGFILENAMES   1
68 #include <sys/sysmacros.h>
69 #include <sys/ino.h>
70 #define DIRSIZ_MACRO
71 #ifdef HAVE_USR_OLD_USR_INCLUDE_NDIR_H
72 #include </usr/old/usr/include/ndir.h>
73 #else
74 #include <ndir.h>
75 #endif
76 #else
77 #define KERNEL
78 #include <sys/dir.h>
79 #undef KERNEL
80 #endif
81 #include <sys/fs.h>
82 #endif /* AFS_VFSINCL_ENV */
83 #endif /* AFS_OSF_ENV */
84
85 #include <sys/stat.h>
86 #include <sys/wait.h>
87 #ifdef  AFS_SUN5_ENV
88 #include <string.h>
89 #else
90 #include <strings.h>
91 #endif
92 #include <ctype.h>
93 #ifdef  XAFS_SUN_ENV
94 #include <mntent.h>
95 #else
96 #ifdef  AFS_SUN5_ENV
97 #include <sys/mnttab.h>
98 #include <sys/mntent.h>
99 #include <sys/vfstab.h>
100 #include <sys/ustat.h>
101 #else
102 #include <fstab.h>
103 #endif
104 #endif
105 #include "fsck.h"
106 #include <errno.h>
107 #include <sys/signal.h>
108
109 char *rawname(), *unrawname(), *blockcheck(), *malloc();
110 void catch(), catchquit(), voidquit();
111 static int tryForce;
112 int returntosingle;
113
114 extern int errno;
115
116 struct part {
117     char *name;                 /* device name */
118     char *fsname;               /* mounted filesystem name */
119     struct part *next;          /* forward link of partitions on disk */
120 } *badlist, **badnext = &badlist;
121
122 struct disk {
123     char *name;                 /* disk base name */
124     struct disk *next;          /* forward link for list of disks */
125     struct part *part;          /* head of list of partitions on disk */
126     int pid;                    /* If != 0, pid of proc working on */
127 } *disks;
128
129 int nrun, ndisks, maxrun, wflag = 0;
130 #ifdef  AFS_HPUX_ENV
131 int fixed;
132 #endif
133
134 #if     defined(AFS_HPUX100_ENV)
135 #include <ustat.h>
136 #include <mntent.h>
137 #endif
138
139 #ifdef VICE
140 #define msgprintf   vfscklogprintf
141 #else /* VICE */
142 #define msgprintf   printf
143 #endif /* VICE */
144
145 #ifdef  AFS_SUN5_ENV
146 int mnt_passno = 0;
147 #endif
148
149 #include "AFS_component_version_number.c"
150
151 #ifdef  AFS_HPUX_ENV
152 int ge_danger = 0;              /* on when fsck is not able to fix the dirty file 
153                                  * system within single run. Problems like dup table
154                                  * overflow, maxdup is exceeding MAXDUP.. etc. could
155                                  * potentailly prevent fsck from doing a complete 
156                                  * repair. This is found in a GE hotsite. */
157 #endif
158
159 main(argc, argv)
160      int argc;
161      char *argv[];
162 {
163     struct fstab *fsp;
164     int pid, passno, sumstatus;
165     char *name;
166     struct disk *dk, *nextdisk;
167     struct part *pt;
168     extern char *AFSVersion;    /* generated version */
169 #ifdef  AFS_SUN5_ENV
170     int other_than_ufs = 0;
171     char *subopt;
172     struct vfstab vt;
173     FILE *vfile;
174     int ret;
175     struct vfstab vget;
176     FILE *fd;
177 #endif
178
179     sync();
180     tryForce = 0;
181 #if     defined(AFS_HPUX_ENV)
182     pclean = 0;
183 #endif
184 #if     defined(AFS_HPUX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_OSF_ENV)
185     fflag = 0;
186 #endif
187 #ifdef  AFS_SUN5_ENV
188     fsflag = oflag = mflag = exitstat = 0;
189 #endif
190 #if     defined(AFS_HPUX100_ENV)
191     mflag = 0;
192 #endif
193     printf("----Open AFS (R) %s fsck----\n", AFSVersion);       /* generated version */
194     if (access("/TRYFORCE", 0) == 0)
195         tryForce = 1;
196     while (--argc > 0 && **++argv == '-') {
197         switch (*++*argv) {
198
199 #if     defined(AFS_HPUX_ENV)
200 #if     defined(AFS_HPUX100_ENV)
201         case 'f':               /* default yes to answer force to check */
202             fflag++;
203             break;
204 #else /* AFS_HPUX100_ENV */
205 #ifdef  AFS_HPUX_ENV
206         case 'F':               /* default yes to answer force to check */
207             fflag++;
208             break;
209 #endif /* AFS_HPUX_ENV */
210 #endif /* AFS_HPUX100_ENV */
211         case 'P':
212             pclean++;
213             preen++;
214             break;
215 #endif
216         case 'p':
217             preen++;
218             break;
219 #if     defined(AFS_HPUX100_ENV)
220         case 'V':
221             {
222                 int opt_count;
223                 char *opt_text;
224
225                 (void)fprintf(stdout, "fsck -F hfs ");
226                 for (opt_count = 1; opt_count < argc; opt_count++) {
227                     opt_text = argv[opt_count];
228                     if (opt_text)
229                         (void)fprintf(stdout, " %s ", opt_text);
230                 }
231                 (void)fprintf(stdout, "\n");
232                 exit(0);
233             }
234             break;
235         case 'm':
236             mflag++;
237             break;
238 #endif
239 #ifdef  AFS_SUN5_ENV
240         case 'V':
241             {
242                 int opt_count;
243                 char *opt_text;
244
245                 (void)fprintf(stdout, "fsck -F ufs ");
246                 for (opt_count = 1; opt_count < argc; opt_count++) {
247                     opt_text = argv[opt_count];
248                     if (opt_text)
249                         (void)fprintf(stdout, " %s ", opt_text);
250                 }
251                 (void)fprintf(stdout, "\n");
252             }
253             break;
254
255         case 'o':
256             subopt = *++argv;
257             argc--;
258             while (*subopt != '\0') {
259                 if (*subopt == 'p') {
260                     preen++;
261                     break;
262                 } else if (*subopt == 'b') {
263                     if (argv[0][1] != '\0') {
264                         bflag = atoi(argv[0] + 1);
265                     } else {
266                         bflag = atoi(*++argv);
267                         argc--;
268                     }
269                     msgprintf("Alternate super block location: %d\n", bflag);
270                     break;
271                 } else if (*subopt == 'd') {
272                     debug++;
273                     break;
274                 } else if (*subopt == 'r') {
275                     break;
276                 } else if (*subopt == 'w') {
277                     wflag++;
278                     break;
279                 } else if (*subopt == 'c') {
280                     cvtflag++;
281                     break;
282                 } else if (*subopt == 'f') {
283                     fflag++;
284                     break;
285                 } else {
286                     errexit("-o %c option?\n", *subopt);
287                 }
288                 subopt++;
289                 ++argv;
290                 argc--;
291             }
292             oflag++;
293             break;
294         case 'm':
295             mflag++;
296             break;
297 #else
298         case 'b':
299             if (argv[0][1] != '\0') {
300                 bflag = atoi(argv[0] + 1);
301             } else {
302                 bflag = atoi(*++argv);
303                 argc--;
304             }
305             msgprintf("Alternate super block location: %d\n", bflag);
306             break;
307
308         case 'c':
309             cvtflag++;
310             break;
311
312             /* who knows?  defined, but doesn't do much */
313         case 'r':
314             break;
315
316         case 'w':               /* check writable only */
317             wflag++;
318             break;
319         case 'd':
320             debug++;
321             break;
322         case 'l':
323             if (!isdigit(argv[1][0]))
324                 errexit("-l flag requires a number\n");
325             maxrun = atoi(*++argv);
326             argc--;
327             break;
328 #if     !defined(AFS_HPUX100_ENV)
329         case 'm':
330             if (!isdigit(argv[1][0]))
331                 errexit("-m flag requires a mode\n");
332             sscanf(*++argv, "%o", &lfmode);
333             if (lfmode & ~07777)
334                 errexit("bad mode to -m: %o\n", lfmode);
335             argc--;
336             printf("** lost+found creation mode %o\n", lfmode);
337             break;
338 #endif /* AFS_HPUX100_ENV */
339 #endif /* AFS_SUN5_ENV */
340 #ifdef  AFS_OSF_ENV
341         case 'o':
342             fflag++;
343             break;
344 #endif /* AFS_OSF_ENV */
345         case 'n':
346         case 'N':
347             nflag++;
348             yflag = 0;
349             break;
350
351             /*
352              * NOTE: -q flag is used only by HPux fsck versions but we add it for all systems since
353              * it's general/useful flag to use.
354              */
355         case 'q':
356             qflag++;
357             break;
358
359         case 'y':
360         case 'Y':
361             yflag++;
362             nflag = 0;
363             break;
364
365         default:
366             errexit("%c option?\n", **argv);
367         }
368     }
369     /*
370      * The following checks were only available on hpux but are useful to all systems.
371      */
372     if (nflag && preen)
373         errexit("Incompatible options: -n and -p\n");
374     if (nflag && qflag)
375         errexit("Incompatible options: -n and -q\n");
376
377 #ifdef  AFS_SUN5_ENV
378     rflag++;                    /* check raw devices */
379 #endif
380     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
381         (void)signal(SIGINT, catch);
382     if (preen)
383         (void)signal(SIGQUIT, catchquit);
384     if (argc) {
385         while (argc-- > 0) {
386             hotroot = 0;
387 #ifdef  AFS_SUN5_ENV
388             if (wflag && !writable(*argv)) {
389                 (void)fprintf(stderr, "not writeable '%s'\n", *argv);
390                 argv++;
391             } else
392 #endif
393                 checkfilesys(*argv++, NULL);
394         }
395 #ifdef  AFS_HPUX_ENV
396         if (ge_danger)
397             exit(-1);
398 #endif
399 #ifdef  AFS_SUN5_ENV
400         exit(exitstat);
401 #else
402         exit(0);
403 #endif
404     }
405 #ifndef AFS_SUN5_ENV
406     sumstatus = 0;
407 #ifdef  AFS_SUN5_ENV
408     if (fstype == NULL || strcmp(fstype, MNTTYPE_UFS) == 0) {
409         int status;
410
411         if ((fd = fopen(VFSTAB, "r")) == NULL) {
412             errexit("vfsck: cannot open vfstab\n");
413         }
414         while ((ret = getvfsent(fd, &vget)) == 0) {
415             if (strcmp(vget.vfs_fstype, MNTTYPE_UFS)
416                 && numbers(vget.vfs_fsckpass)) {
417                 other_than_ufs++;
418                 continue;
419             }
420             if (numbers(vget.vfs_fsckpass))
421                 passno = atoi(vget.vfs_fsckpass);
422             else
423                 continue;
424             if (passno < 1)
425                 continue;
426             if (preen == 0 || passno == 1) {
427                 checkfilesys(vget.vfs_fsckdev, get.vfs_mountp);
428             } else if (passno > 1) {
429                 addpart(vget.vfs_fsckdev, vget.vfs_special);
430             }
431         }
432 #else
433     for (passno = 1; passno <= 2; passno++) {
434         if (setfsent() == 0)
435             errexit("Can't open checklist file: %s\n", FSTAB);
436         while ((fsp = getfsent()) != 0) {
437             if (strcmp(fsp->fs_type, FSTAB_RW)
438                 && strcmp(fsp->fs_type, FSTAB_RO)
439                 && strcmp(fsp->fs_type, FSTAB_RQ))
440                 continue;
441 #ifdef  AFS_OSF_ENV
442             if (strcmp(fsp->fs_vfstype, "ufs") || fsp->fs_passno == 0) {
443                 continue;
444             }
445 #endif /* AFS_OSF_ENV */
446             if (preen == 0 || passno == 1 && fsp->fs_passno == 1) {
447                 if (passno == 1) {
448                     name = blockcheck(fsp->fs_spec);
449                     if (name != NULL) {
450                         checkfilesys(name, fsp->fs_file);
451                     } else if (preen) {
452                         printf("pid %d exiting 8/1\n", getpid());
453                         exit(8);
454                     }
455                 }
456             } else if (passno == 2 && fsp->fs_passno > 1) {
457                 name = blockcheck(fsp->fs_spec);
458                 if (name == NULL) {
459                     pwarn("BAD DISK NAME %s\n", fsp->fs_spec);
460                     sumstatus |= 8;
461                     printf("pid %d saw bad disk name 8/3\n", getpid());
462                     continue;
463                 }
464                 addpart(name, fsp->fs_file);
465             }
466         }
467 #endif /* AFS_SUN5_ENV */
468     }
469     if (preen) {
470         int status, rc;
471
472         if (maxrun == 0)
473             maxrun = ndisks;
474         if (maxrun > ndisks)
475             maxrun = ndisks;
476         nextdisk = disks;
477         for (passno = 0; passno < maxrun; ++passno) {
478             startdisk(nextdisk);
479             nextdisk = nextdisk->next;
480         }
481         while ((pid = wait(&status)) != -1) {
482             for (dk = disks; dk; dk = dk->next)
483                 if (dk->pid == pid)
484                     break;
485             if (dk == 0) {
486                 printf("Unknown pid %d\n", pid);
487                 continue;
488             }
489             rc = WEXITSTATUS(status);
490             if (WIFSIGNALED(status)) {
491                 printf("%s (%s): EXITED WITH SIGNAL %d\n", dk->part->name,
492                        dk->part->fsname, WTERMSIG(status));
493                 rc = 8;
494             }
495             if (rc != 0) {
496                 sumstatus |= rc;
497                 *badnext = dk->part;
498                 badnext = &dk->part->next;
499                 dk->part = dk->part->next;
500                 *badnext = NULL;
501             } else
502                 dk->part = dk->part->next;
503             dk->pid = 0;
504             nrun--;
505             if (dk->part == NULL)
506                 ndisks--;
507
508             if (nextdisk == NULL) {
509                 if (dk->part)
510                     startdisk(dk);
511             } else if (nrun < maxrun && nrun < ndisks) {
512                 for (;;) {
513                     if ((nextdisk = nextdisk->next) == NULL)
514                         nextdisk = disks;
515                     if (nextdisk->part != NULL && nextdisk->pid == 0)
516                         break;
517                 }
518                 startdisk(nextdisk);
519             }
520         }
521     }
522     if (sumstatus) {
523         if (badlist == 0) {
524             printf("pid %d exiting 8/2\n", getpid());
525             exit(8);
526         }
527         printf("THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
528                badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:");
529         for (pt = badlist; pt; pt = pt->next)
530             printf("%s (%s)%s", pt->name, pt->fsname, pt->next ? ", " : "\n");
531         exit(8);
532     }
533 #ifdef  AFS_SUN5_ENV
534     fclose(fd);
535 #else
536     (void)endfsent();
537 #endif
538     if (returntosingle)
539         exit(2);
540 #endif /* !AFS_SUN5_ENV */
541     exit(0);
542 }
543
544 struct disk *
545 finddisk(name)
546      char *name;
547 {
548     struct disk *dk, **dkp;
549     char *p;
550     int len;
551
552     for (p = name + strlen(name) - 1; p >= name; --p)
553         if (isdigit(*p)) {
554             len = p - name + 1;
555             break;
556         }
557     if (p < name)
558         len = strlen(name);
559
560     for (dk = disks, dkp = &disks; dk; dkp = &dk->next, dk = dk->next) {
561         if (strncmp(dk->name, name, len) == 0 && dk->name[len] == 0)
562             return (dk);
563     }
564     if ((*dkp = (struct disk *)malloc(sizeof(struct disk))) == NULL)
565         errexit("out of memory");
566     dk = *dkp;
567     if ((dk->name = malloc((unsigned int)len + 1)) == NULL)
568         errexit("out of memory");
569     strncpy(dk->name, name, len);
570     dk->name[len] = '\0';
571     dk->part = NULL;
572     dk->next = NULL;
573     dk->pid = 0;
574     ndisks++;
575     return (dk);
576 }
577
578 addpart(name, fsname)
579      char *name, *fsname;
580 {
581     struct disk *dk = finddisk(name);
582     struct part *pt, **ppt = &dk->part;
583
584     for (pt = dk->part; pt; ppt = &pt->next, pt = pt->next)
585         if (strcmp(pt->name, name) == 0) {
586             printf("%s in fstab more than once!\n", name);
587             return;
588         }
589     if ((*ppt = (struct part *)malloc(sizeof(struct part))) == NULL)
590         errexit("out of memory");
591     pt = *ppt;
592     if ((pt->name = malloc((unsigned int)strlen(name) + 1)) == NULL)
593         errexit("out of memory");
594     strcpy(pt->name, name);
595     if ((pt->fsname = malloc((unsigned int)strlen(fsname) + 1)) == NULL)
596         errexit("out of memory");
597     strcpy(pt->fsname, fsname);
598     pt->next = NULL;
599 }
600
601 startdisk(dk)
602      struct disk *dk;
603 {
604
605     nrun++;
606     dk->pid = fork();
607     if (dk->pid < 0) {
608         perror("fork");
609         exit(8);
610     }
611     if (dk->pid == 0) {
612         (void)signal(SIGQUIT, voidquit);
613         checkfilesys(dk->part->name, dk->part->fsname);
614         exit(0);
615     }
616 }
617
618 checkfilesys(filesys, parname)
619      char *filesys;
620 {
621     daddr_t n_ffree, n_bfree;
622     struct dups *dp;
623     struct stat tstat;          /* for ultrix 3 unmount */
624     struct zlncnt *zlnp;
625     char devbuffer[128];
626     int ret_val;
627
628 #ifdef  AFS_OSF_ENV
629     int temp;
630 #endif /* AFS_OSF_ENV */
631
632 #ifdef  AFS_SUN_ENV
633     iscorrupt = 1;
634 #endif
635 #ifdef  AFS_SUN5_ENV
636     mountedfs = 0;
637     isconvert = 0;
638 #endif
639 #ifdef  AFS_HPUX_ENV
640     ge_danger = 0;              /* set to 1 by any table overflow or more 
641                                  * dup/bad blocks than expected */
642
643     fixed = 1;                  /* set to 0 by any 'no' reply */
644 #endif
645     strcpy(devbuffer, filesys); /* copy the file system name to the device buffer */
646     devname = devbuffer;        /* remember generic ptr for later */
647     EnsureDevice(devname);      /* canonicalize name */
648     if (debug && preen)
649         pinfo("starting\n");
650
651         ret_val = setup(devname);
652
653         if (ret_val == 0) {
654 #ifdef  AFS_SUN_ENV
655             if (iscorrupt == 0)
656                 return;
657 #endif
658             if (preen)
659                 pfatal("CAN'T CHECK FILE SYSTEM.");
660 #ifdef  AFS_SUN5_ENV
661             if ((exitstat == 0) && (mflag))
662                 exitstat = 32;
663             exit(exitstat);
664 #endif
665             return (0);
666 #ifdef  AFS_HPUX_ENV
667         } else if (ret_val == -1) {     /* pclean && FS_CLEAN */
668             return (1);
669 #endif
670 #if     defined(AFS_OSF_ENV)
671         } else if (ret_val == FS_CLEAN) {       /* pclean && FS_CLEAN */
672             return (1);
673 #endif
674         }
675 #if     defined(AFS_HPUX100_ENV)
676         if (mflag)
677             check_sanity(filesys);
678 #endif
679
680 #ifdef  AFS_SUN5_ENV
681         if (mflag)
682             check_sanity(filesys);
683         if (debug)
684             printclean();
685 #endif
686 #ifdef  AFS_SUN_ENV
687         iscorrupt = 0;
688 #endif
689         /*
690          * 1: scan inodes tallying blocks used
691          */
692         if (preen == 0) {
693 #if     defined(AFS_SUN5_ENV)
694             if (mountedfs)
695                 msgprintf("** Currently Mounted on %s\n", sblock.fs_fsmnt);
696             else
697 #endif
698                 msgprintf("** Last Mounted on %s\n", sblock.fs_fsmnt);
699             if (hotroot)
700                 msgprintf("** Root file system\n");
701 #ifdef  AFS_SUN5_ENV
702             if (mflag) {
703                 printf("** Phase 1 - Sanity Check only\n");
704                 return;
705             } else
706 #endif
707                 msgprintf("** Phase 1 - Check Blocks and Sizes\n");
708         }
709         pass1();
710
711         /*
712          * 1b: locate first references to duplicates, if any
713          */
714         if (duplist) {
715             if (preen)
716                 pfatal("INTERNAL ERROR: dups with -p");
717             msgprintf("** Phase 1b - Rescan For More DUPS\n");
718             pass1b();
719         }
720
721         /*
722          * 2: traverse directories from root to mark all connected directories
723          */
724         if (preen == 0)
725             msgprintf("** Phase 2 - Check Pathnames\n");
726         pass2();
727
728         /*
729          * 3: scan inodes looking for disconnected directories
730          */
731         if (preen == 0)
732             msgprintf("** Phase 3 - Check Connectivity\n");
733         pass3();
734
735         /*
736          * 4: scan inodes looking for disconnected files; check reference counts
737          */
738         if (preen == 0)
739             msgprintf("** Phase 4 - Check Reference Counts\n");
740         pass4();
741
742         /*
743          * 5: check and repair resource counts in cylinder groups
744          */
745         if (preen == 0)
746             msgprintf("** Phase 5 - Check Cyl groups\n");
747         pass5();
748
749 #if     defined(AFS_SUN_ENV)
750     updateclean();
751     if (debug)
752         printclean();
753 #endif
754     /*
755      * print out summary statistics
756      */
757     n_ffree = sblock.fs_cstotal.cs_nffree;
758     n_bfree = sblock.fs_cstotal.cs_nbfree;
759 #ifdef VICE
760 #if defined(ACLS) && defined(AFS_HPUX_ENV)
761     pinfo("%d files, %d icont, %d used, %d free", n_files, n_cont, n_blks,
762           n_ffree + sblock.fs_frag * n_bfree);
763 #else
764     pinfo("%d files, %d used, %d free", n_files, n_blks,
765           n_ffree + sblock.fs_frag * n_bfree);
766 #endif
767     if (nViceFiles)
768         msgprintf(", %d AFS files", nViceFiles);
769     msgprintf(" (%d frags, %d blocks, %.1f%% fragmentation)\n", n_ffree,
770               n_bfree, (float)(n_ffree * 100) / sblock.fs_dsize);
771 #else /* VICE */
772 #if defined(ACLS) && defined(AFS_HPUX_ENV)
773     pinfo("%d files, %d icont, %d used, %d free ", n_files, n_cont, n_blks,
774           n_ffree + sblock.fs_frag * n_bfree);
775 #else
776     pinfo("%d files, %d used, %d free ", n_files, n_blks,
777           n_ffree + sblock.fs_frag * n_bfree);
778 #endif
779     n printf("(%d frags, %d blocks, %.1f%% fragmentation)\n", n_ffree,
780              n_bfree, (float)(n_ffree * 100) / sblock.fs_dsize);
781 #endif /* VICE */
782     if (debug && (n_files -= maxino - ROOTINO - sblock.fs_cstotal.cs_nifree))
783         msgprintf("%d files missing\n", n_files);
784     if (debug) {
785         n_blks += sblock.fs_ncg * (cgdmin(&sblock, 0) - cgsblock(&sblock, 0));
786         n_blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0);
787         n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize);
788         if (n_blks -= maxfsblock - (n_ffree + sblock.fs_frag * n_bfree))
789             printf("%d blocks missing\n", n_blks);
790         if (duplist != NULL) {
791             msgprintf("The following duplicate blocks remain:");
792             for (dp = duplist; dp; dp = dp->next)
793                 msgprintf(" %d,", dp->dup);
794             msgprintf("\n");
795         }
796         if (zlnhead != NULL) {
797             msgprintf("The following zero link count inodes remain:");
798             for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
799                 msgprintf(" %d,", zlnp->zlncnt);
800             msgprintf("\n");
801         }
802     }
803 #ifdef  AFS_HPUX_ENV
804     /* if user's specification denotes that the file system block
805      * is going to be modified (nflag == 0) then fsck store the
806      * correct magic number in the super block if it is not already
807      * there
808      */
809     if (!nflag && !(fswritefd < 0)) {
810         if (ge_danger) {
811             printf("***** FILE SYSTEM IS NOT CLEAN, FSCK AGAIN *****\n");
812             fsmodified++;
813         } else {
814             if (!hotroot) {
815                 if (fixed && (sblock.fs_clean != FS_CLEAN)) {
816                     if (!preen && !qflag)
817                         printf("***** MARKING FILE SYSTEM CLEAN *****\n");
818                     sblock.fs_clean = FS_CLEAN;
819                     fsmodified++;
820                 }
821             } else {
822                 /* fix FS_CLEAN if changes made and no 'no' replies */
823                 if (fsmodified && fixed)
824                     sblock.fs_clean = FS_CLEAN;
825                 /*
826                  *  Fix fs_clean if there were no 'no' replies.
827                  *  This is done for both the s300 and s800.  The s800 root will be 
828                  *  guaranteed clean as of 7.0.
829                  */
830                 if (fixed && (sblock.fs_clean != FS_OK)) {
831                     if (!preen && !qflag)
832                         printf("***** MARKING FILE SYSTEM CLEAN *****\n");
833                     sblock.fs_clean = FS_CLEAN;
834                     fsmodified++;
835                 }
836             }
837         }
838     }
839 #endif
840     zlnhead = NULL;
841     duplist = NULL;
842
843 #if     defined(AFS_SUN_ENV) && !defined(AFS_SUN3_ENV)  /* WAS AFS_SUN5_ENV */
844 #ifdef  notdef
845     inocleanup();
846 #endif
847     if (fsmodified)
848         fixstate = 1;
849     else
850         fixstate = 0;
851     if (hotroot && sblock.fs_clean == FSACTIVE)
852         rebflg = 1;
853 #ifdef  AFS_SUN5_ENV
854     else if (!((sblock.fs_state + (afs_int32) sblock.fs_time == FSOKAY) &&
855 #else
856     else if (!((fs_get_state(&sblock) + (afs_int32) sblock.fs_time == FSOKAY)
857                &&
858 #endif
859                (sblock.fs_clean == FSCLEAN || sblock.fs_clean == FSSTABLE))) {
860         if (yflag || !iscorrupt) {
861             printf("%s FILE SYSTEM STATE SET TO OKAY\n", devname);
862             fixstate = 1;
863         } else {
864             printf("%s FILE SYSTEM STATE NOT SET TO OKAY\n", devname);
865             fixstate = 0;
866         }
867     }
868     if (fixstate) {
869         (void)time(&sblock.fs_time);
870         if (!iscorrupt) {
871             if (hotroot && rebflg)
872                 sblock.fs_clean = FSACTIVE;
873             else
874                 sblock.fs_clean = FSSTABLE;
875 #ifdef  AFS_SUN5_ENV
876             sblock.fs_state = FSOKAY - (afs_int32) sblock.fs_time;
877 #else
878             fs_set_state(&sblock, FSOKAY - (afs_int32) sblock.fs_time);
879 #endif
880         }
881         sbdirty();
882     }
883 #else
884 #ifdef  AFS_OSF_ENV
885     if (!nflag && !bflag && !hotroot) {
886         temp = fsmodified;
887         sblock.fs_clean = FS_CLEAN;
888         (void)time(&sblock.fs_time);
889         sbdirty();
890         flush(fswritefd, &sblk);
891         fsmodified = temp;
892     }
893 #else /* AFS_OSF_ENV */
894     if (fsmodified) {
895         (void)time(&sblock.fs_time);
896         sbdirty();
897     }
898 #endif
899 #endif
900     ckfini();
901     free(blockmap);
902     free(statemap);
903     free((char *)lncntp);
904     lncntp = NULL;
905     blockmap = statemap = NULL;
906 #ifdef  AFS_SUN5_ENV
907     if (iscorrupt)
908         exitstat = 36;
909 #endif
910     if (!fsmodified)
911         return;
912     if (!preen) {
913         msgprintf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
914
915         if (hotroot)
916             msgprintf("\n***** REBOOT UNIX *****\n");
917     }
918 #ifdef  AFS_SUN5_ENV
919     if (mountedfs || hotroot) {
920         exitstat = 40;
921     }
922 #endif
923     if (hotroot) {
924         sync();
925 #ifdef  AFS_HPUX_ENV
926         if (ge_danger)
927             exit(-1);
928         else
929 #endif
930 #ifdef  AFS_SUN5_ENV
931             exit(exitstat);
932 #else
933             exit(4);
934 #endif
935     }
936 #ifdef VICE
937     (void)close(fsreadfd);
938     (void)close(fswritefd);
939     if (nViceFiles || tryForce) {
940         /* Modified file system with vice files: force full salvage */
941         /* Salvager recognizes the file FORCESALVAGE in the root of each partition */
942         struct ufs_args ufsargs;
943
944         char pname[100], fname[100], *special;
945         int fd, code, failed = 0;
946
947         msgprintf
948             ("%s: AFS file system partition was modified; forcing full salvage\n",
949              devname);
950         devname = unrawname(devname);
951         special = (char *)strrchr(devname, '/');
952         if (!special++)
953             special = devname;
954         strcpy(pname, "/etc/vfsck.");   /* Using /etc, rather than /tmp, since
955                                          * /tmp is a link to /usr/tmp on some systems, and isn't mounted now */
956         strcat(pname, special);
957 #ifdef AFS_SUN_ENV
958         /* if system mounted / as read-only, we'll try to fix now */
959         if (access("/", W_OK) < 0 && errno == EROFS) {
960             code = system("mount -o remount /");
961             if (code) {
962                 printf("Couldn't remount / R/W; continuing anyway (%d).\n",
963                        errno);
964                 failed = 1;
965             }
966         }
967 #endif
968 #ifdef  AFS_OSF_ENV
969         /* if system mounted / as read-only, we'll try to fix now */
970         if (access("/", W_OK) < 0 && errno == EROFS) {
971             printf("Can't RW acceess /; %d\n", errno);
972             code = system("/sbin/mount -u /");
973             if (code) {
974                 printf("Couldn't remount / R/W; continuing anyway (%d).\n",
975                        errno);
976                 failed = 1;
977             }
978         }
979 #endif
980         rmdir(pname);
981         unlink(pname);
982         if (mkdir(pname, 0777) < 0) {
983             if (errno != EEXIST) {
984                 perror("fsck mkdir");
985                 failed = 1;
986             }
987         }
988         if (failed && parname) {
989             strcpy(pname, parname);
990         }
991 #if !defined(AFS_HPUX_ENV)
992 #ifdef  AFS_SUN5_ENV
993         ufsargs.flags = UFSMNT_NOINTR;
994 #else
995         ufsargs.fspec = devname;
996 #endif
997 #ifdef  AFS_SUN5_ENV
998         if (mount
999             (devname, pname, MS_DATA, "ufs", (char *)&ufsargs,
1000              sizeof(ufsargs)) < 0) {
1001 #else
1002         if (mount(MOUNT_UFS, pname, 0, &ufsargs) < 0) {
1003 #endif
1004 #else
1005         if (mount(devname, pname, 0) < 0) {
1006 #endif
1007             printf
1008                 ("Couldn't mount %s on %s to force FULL SALVAGE; continuing anyway (%d)!\n",
1009                  devname, pname, errno);
1010         } else {
1011             strcpy(fname, pname);
1012             strcat(fname, "/FORCESALVAGE");
1013             fd = open(fname, O_CREAT, 0);
1014             if (fd == -1) {
1015                 errexit("Couldn't create %s to force full salvage!\n", fname);
1016             } else {
1017                 fstat(fd, &tstat);
1018                 close(fd);
1019             }
1020 #if !defined(AFS_HPUX_ENV) && !defined(AFS_SUN5_ENV) && !defined(AFS_OSF_ENV)
1021             unmount(pname);
1022 #else
1023 #if     defined(AFS_OSF_ENV)
1024             umount(pname, MNT_NOFORCE);
1025 #else /* AFS_OSF_ENV */
1026             umount(devname);
1027 #endif
1028 #endif
1029         }
1030         rmdir(pname);
1031     }
1032     if (logfile) {
1033         fsync(fileno(logfile)); /* Since update isn't running */
1034         fclose(logfile);
1035         logfile = 0;
1036     }
1037 #endif /* VICE */
1038
1039 }
1040
1041 char *
1042 blockcheck(name)
1043      char *name;
1044 {
1045     struct stat stslash, stblock, stchar;
1046     char *raw;
1047     int retried = 0;
1048
1049     hotroot = 0;
1050     if (stat("/", &stslash) < 0) {
1051         perror("/");
1052         printf("Can't stat root\n");
1053         return (0);
1054     }
1055   retry:
1056     if (stat(name, &stblock) < 0) {
1057         perror(name);
1058         printf("Can't stat %s\n", name);
1059         return (0);
1060     }
1061     if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
1062         if (stslash.st_dev == stblock.st_rdev) {
1063             hotroot++;
1064 #if     !defined(AFS_OSF_ENV)   /*  OSF/1 always uses the raw device, even for / */
1065             return (name);
1066 #endif /* AFS_OSF_ENV */
1067         }
1068         raw = rawname(name);
1069         if (raw) {
1070             return (raw);
1071         } else {
1072             printf("Cannot find character device for %s\n", name);
1073             return (name);
1074         }
1075     } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) {
1076         name = unrawname(name);
1077         retried++;
1078         goto retry;
1079     }
1080     printf("Can't make sense out of name %s\n", name);
1081     return (0);
1082 }
1083
1084
1085 #if     defined(AFS_HPUX_ENV) || defined(AFS_SUN5_ENV)
1086
1087 #ifdef  AFS_SUN5_ENV
1088 /*
1089  * exit 0 - file system is unmounted and okay
1090  * exit 32 - file system is unmounted and needs checking
1091  * exit 33 - file system is mounted for root file system
1092  * exit 34 - cannot stat device
1093  */
1094 check_sanity(filename)
1095      char *filename;
1096 {
1097     struct stat stbd, stbr;
1098     struct ustat usb;
1099     char *devname;
1100     struct vfstab vfsbuf;
1101     FILE *vfstab;
1102     int is_root = 0;
1103     int is_usr = 0;
1104     int is_block = 0;
1105
1106     if (stat(filename, &stbd) < 0) {
1107         fprintf(stderr, "ufs fsck: sanity check failed : cannot stat %s\n",
1108                 filename);
1109         exit(34);
1110     }
1111
1112     if ((stbd.st_mode & S_IFMT) == S_IFBLK)
1113         is_block = 1;
1114     else if ((stbd.st_mode & S_IFMT) == S_IFCHR)
1115         is_block = 0;
1116     else {
1117         fprintf(stderr,
1118                 "ufs fsck: sanity check failed: %s not block or character device\n",
1119                 filename);
1120         exit(34);
1121     }
1122     /*
1123      * Determine if this is the root file system via vfstab. Give up
1124      * silently on failures. The whole point of this is not to care
1125      * if the root file system is already mounted.
1126      *
1127      * XXX - similar for /usr. This should be fixed to simply return
1128      * a new code indicating, mounted and needs to be checked.
1129      */
1130     if ((vfstab = fopen(VFSTAB, "r")) != 0) {
1131         if (getvfsfile(vfstab, &vfsbuf, "/") == 0) {
1132             if (is_block)
1133                 devname = vfsbuf.vfs_special;
1134             else
1135                 devname = vfsbuf.vfs_fsckdev;
1136             if (stat(devname, &stbr) == 0)
1137                 if (stbr.st_rdev == stbd.st_rdev)
1138                     is_root = 1;
1139         }
1140         if (getvfsfile(vfstab, &vfsbuf, "/usr") == 0) {
1141             if (is_block)
1142                 devname = vfsbuf.vfs_special;
1143             else
1144                 devname = vfsbuf.vfs_fsckdev;
1145             if (stat(devname, &stbr) == 0)
1146                 if (stbr.st_rdev == stbd.st_rdev)
1147                     is_usr = 1;
1148         }
1149     }
1150
1151     /* XXX - only works if filename is a block device or if
1152      * character and block device has the same dev_t value */
1153     if (is_root == 0 && is_usr == 0 && ustat(stbd.st_rdev, &usb) == 0) {
1154         fprintf(stderr, "ufs fsck: sanity check: %s already mounted\n",
1155                 filename);
1156         exit(33);
1157     }
1158     /*
1159      * We mount the ufs root file system read-only first.  After fsck
1160      * runs, we remount the root as read-write.  Therefore, we no longer
1161      * check for different values for fs_state between the root file 
1162      * system and the rest of file systems.
1163      */
1164     if (!((sblock.fs_state + (time_t) sblock.fs_time == FSOKAY)
1165           && (sblock.fs_clean == FSCLEAN || sblock.fs_clean == FSSTABLE))) {
1166         fprintf(stderr, "ufs fsck: sanity check: %s needs checking\n",
1167                 filename);
1168         exit(32);
1169     }
1170     fprintf(stderr, "ufs fsck: sanity check: %s okay\n", filename);
1171     exit(0);
1172 }
1173 #endif
1174
1175 #if     defined(AFS_HPUX100_ENV)
1176 check_sanity(filename)
1177      char *filename;
1178 {
1179     struct stat stbd, stbr;
1180     struct ustat usb;
1181     char *devname;
1182     FILE *vfstab;
1183     struct mntent *mnt;
1184     int is_root = 0;
1185     int is_usr = 0;
1186     int is_block = 0;
1187
1188     if (stat(filename, &stbd) < 0) {
1189         fprintf(stderr, "hfs fsck: sanity check failed : cannot stat %s\n",
1190                 filename);
1191         exit(34);
1192     }
1193
1194     if ((stbd.st_mode & S_IFMT) == S_IFBLK)
1195         is_block = 1;
1196     else if ((stbd.st_mode & S_IFMT) == S_IFCHR)
1197         is_block = 0;
1198     else {
1199         fprintf(stderr,
1200                 "hfs fsck: sanity check failed: %s not block or character device\n",
1201                 filename);
1202         exit(34);
1203     }
1204     /*
1205      * Determine if this is the root file system via vfstab. Give up
1206      * silently on failures. The whole point of this is not to care
1207      * if the root file system is already mounted.
1208      *
1209      * XXX - similar for /usr. This should be fixed to simply return
1210      * a new code indicating, mounted and needs to be checked.
1211      */
1212     if ((vfstab = setmntent(FSTAB, "r")) != 0) {
1213         while (mnt = getmntent(vfstab)) {
1214             if (!strcmp(mnt->mnt_dir, "/"))
1215                 if (stat(mnt->mnt_fsname, &stbr) == 0)
1216                     if (stbr.st_rdev == stbd.st_rdev)
1217                         is_root = 1;
1218
1219             if (!strcmp(mnt->mnt_dir, "/usr"))
1220                 if (stat(mnt->mnt_fsname, &stbr) == 0)
1221                     if (stbr.st_rdev == stbd.st_rdev)
1222                         is_usr = 1;
1223         }
1224         endmntent(vfstab);
1225     }
1226
1227     /* XXX - only works if filename is a block device or if
1228      * character and block device has the same dev_t value */
1229     if (is_root == 0 && is_usr == 0 && ustat(stbd.st_rdev, &usb) == 0) {
1230         fprintf(stderr, "hfs fsck: sanity check: %s already mounted\n",
1231                 filename);
1232         exit(33);
1233     }
1234     /*
1235      * We mount the ufs root file system read-only first.  After fsck
1236      * runs, we remount the root as read-write.  Therefore, we no longer
1237      * check for different values for fs_state between the root file 
1238      * system and the rest of file systems.
1239      */
1240     if (!((sblock.fs_clean == FS_CLEAN || sblock.fs_clean == FS_OK))) {
1241         fprintf(stderr, "hfs fsck: sanity check: %s needs checking\n",
1242                 filename);
1243         exit(32);
1244     }
1245     fprintf(stderr, "hfs fsck: sanity check: %s okay\n", filename);
1246     exit(0);
1247 }
1248 #endif
1249 /* see if all numbers */
1250 numbers(yp)
1251      char *yp;
1252 {
1253     if (yp == NULL)
1254         return (0);
1255     while ('0' <= *yp && *yp <= '9')
1256         yp++;
1257     if (*yp)
1258         return (0);
1259     return (1);
1260 }
1261 #endif
1262
1263 /* Convert a raw device name into a block device name. 
1264  * If the block device is not found, return the raw device name.
1265  * For HP and SUN, the returned value is not changed. For other 
1266  * platforms it is changed (I see no rhyme or reason -jpm).
1267  */
1268 char *
1269 unrawname(rawdev)
1270      char *rawdev;
1271 {
1272     static char bldev[256];
1273     struct stat statbuf;
1274     int code, i;
1275
1276     code = stat(rawdev, &statbuf);
1277     if ((code < 0) || !S_ISCHR(statbuf.st_mode))
1278         return (rawdev);        /* Not a char device */
1279
1280     for (i = strlen(rawdev) - 2; i >= 0; i--) {
1281         if ((rawdev[i] == '/') && (rawdev[i + 1] == 'r')) {
1282             strcpy(bldev, rawdev);
1283             bldev[i + 1] = 0;
1284             strcat(bldev, &rawdev[i + 2]);
1285
1286             code = stat(bldev, &statbuf);       /* test for block device */
1287             if (!code && S_ISBLK(statbuf.st_mode)) {
1288 #if defined(AFS_HPUX_ENV) || defined(AFS_SUN5_ENV)
1289                 return (bldev);
1290 #else
1291                 strcpy(rawdev, bldev);  /* Replace */
1292                 return (rawdev);
1293 #endif
1294             }
1295         }
1296     }
1297     return (rawdev);
1298 }
1299
1300 /* Convert a block device name into a raw device name. 
1301  * If the block device is not found, return null
1302  */
1303 char *
1304 rawname(bldev)
1305      char *bldev;
1306 {
1307     static char rawdev[256];
1308     struct stat statbuf;
1309     int code, i;
1310
1311     for (i = strlen(bldev) - 1; i >= 0; i--) {
1312         if (bldev[i] == '/') {
1313             strcpy(rawdev, bldev);
1314             rawdev[i + 1] = 'r';
1315             rawdev[i + 2] = 0;
1316             strcat(rawdev, &bldev[i + 1]);
1317
1318             code = stat(rawdev, &statbuf);
1319             if (!code && S_ISCHR(statbuf.st_mode))
1320                 return (rawdev);
1321         }
1322     }
1323     return NULL;
1324 }