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