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