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