Abstract /vicepX header traversal
[openafs.git] / src / vol / vol-salvage.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  *      System:         VICE-TWO
12  *      Module:         vol-salvage.c
13  *      Institution:    The Information Technology Center, Carnegie-Mellon University
14  */
15
16 /*  1.2 features:
17         Correct handling of bad "." and ".." entries.
18         Message if volume has "destroyMe" flag set--but doesn't delete yet.
19         Link count bug fixed--bug was that vnodeEssence link count was unsigned
20         14 bits.  Needs to be signed.
21
22     1.3 features:
23         Change to DirHandle stuff to make sure that cache entries are reused at the
24         right time (this parallels the file server change, but is not identical).
25
26         Added calls to directory salvager routines; doesn't salvage dir unless debug=1.
27
28     1.4 features:
29         Fixed bug which was causing inode link counts to go bad (thus leaking
30         disk blocks).
31 Vnodes with 0 inode pointers in RW volumes are now deleted.
32         An inode with a matching inode number to the vnode is preferred to an
33         inode with a higer data version.
34         Bug is probably fixed that was causing data version to remain wrong,
35         despite assurances from the salvager to the contrary.
36
37     1.5 features:
38         Added limited salvaging:  unless ForceSalvage is on, then the volume will
39         not be salvaged if the dontSalvage flag is set in the Volume Header.
40         The ForceSalvage flag is turned on if an individual volume is salvaged or
41         if the file FORCESALVAGE exists in the partition header of the file system
42         being salvaged.  This isn't used for anything but could be set by vfsck.
43         A -f flag was also added to force salvage.
44
45     1.6 features:
46         It now deletes obsolete volume inodes without complaining
47
48     1.7 features:
49         Repairs rw volume headers (again).
50
51     1.8 features:
52         Correlates volume headers & inodes correctly, thus preventing occasional deletion
53         of read-only volumes...
54         No longer forces a directory salvage for volume 144 (which may be a good volume
55         at some other site!)
56         Some of the messages are cleaned up or made more explicit.  One or two added.
57         Logging cleaned up.
58         A bug was fixed which forced salvage of read-only volumes without a corresponding
59         read/write volume.
60
61     1.9 features:
62         When a volume header is recreated, the new name will be "bogus.volume#"
63
64     2.0 features:
65         Directory salvaging turned on!!!
66
67     2.1 features:
68         Prints warning messages for setuid programs.
69
70     2.2 features:
71         Logs missing inode numbers.
72
73     2.3 features:
74             Increments directory version number by 200 (rather than by 1) when it is salvaged, in order to prevent problems due to the fact that a version number can be promised to a workstation before it is written to disk.  If the server crashes, it may have an older version.  Salvaging it could bring the version number up to the same version the workstation believed it already had a call back on. 
75
76     2.4 features:
77             Locks the file /vice/vol/salvage.lock before starting.  Aborts if it can't acquire the lock.
78             Time stamps on log entries.
79             Fcntl on stdout to cause all entries to be appended.
80             Problems writing to temporary files are now all detected.
81             Inode summary files are now dynamically named (so that multiple salvagers wouldn't conflict).
82             Some cleanup of error messages.
83 */
84
85
86 #include <afsconfig.h>
87 #include <afs/param.h>
88
89
90 #ifndef AFS_NT40_ENV
91 #include <sys/param.h>
92 #include <sys/file.h>
93 #ifndef ITIMER_REAL
94 #include <sys/time.h>
95 #endif /* ITIMER_REAL */
96 #endif
97 #include <stdlib.h>
98 #include <stdio.h>
99 #include <string.h>
100 #include <dirent.h>
101 #include <sys/stat.h>
102 #include <time.h>
103 #include <errno.h>
104 #ifdef AFS_NT40_ENV
105 #include <io.h>
106 #include <WINNT/afsevent.h>
107 #endif
108 #ifndef WCOREDUMP
109 #define WCOREDUMP(x)    ((x) & 0200)
110 #endif
111 #include <rx/xdr.h>
112 #include <afs/afsint.h>
113 #include <afs/assert.h>
114 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
115 #if defined(AFS_VFSINCL_ENV)
116 #include <sys/vnode.h>
117 #ifdef  AFS_SUN5_ENV
118 #include <sys/fs/ufs_inode.h>
119 #else
120 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
121 #include <ufs/ufs/dinode.h>
122 #include <ufs/ffs/fs.h>
123 #else
124 #include <ufs/inode.h>
125 #endif
126 #endif
127 #else /* AFS_VFSINCL_ENV */
128 #ifdef  AFS_OSF_ENV
129 #include <ufs/inode.h>
130 #else /* AFS_OSF_ENV */
131 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
132 #include <sys/inode.h>
133 #endif
134 #endif
135 #endif /* AFS_VFSINCL_ENV */
136 #endif /* AFS_SGI_ENV */
137 #ifdef  AFS_AIX_ENV
138 #include <sys/vfs.h>
139 #include <sys/lockf.h>
140 #else
141 #ifdef  AFS_HPUX_ENV
142 #include <unistd.h>
143 #include <checklist.h>
144 #else
145 #if defined(AFS_SGI_ENV)
146 #include <unistd.h>
147 #include <fcntl.h>
148 #include <mntent.h>
149 #else
150 #if     defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
151 #ifdef    AFS_SUN5_ENV
152 #include <unistd.h>
153 #include <sys/mnttab.h>
154 #include <sys/mntent.h>
155 #else
156 #include <mntent.h>
157 #endif
158 #else
159 #endif /* AFS_SGI_ENV */
160 #endif /* AFS_HPUX_ENV */
161 #endif
162 #endif
163 #include <fcntl.h>
164 #ifndef AFS_NT40_ENV
165 #include <afs/osi_inode.h>
166 #endif
167 #include <afs/cmd.h>
168 #include <afs/dir.h>
169 #include <afs/afsutil.h>
170 #include <afs/fileutil.h>
171 #include <afs/procmgmt.h>       /* signal(), kill(), wait(), etc. */
172 #ifndef AFS_NT40_ENV
173 #include <syslog.h>
174 #endif
175
176 #include "nfs.h"
177 #include "lwp.h"
178 #include "lock.h"
179 #include <afs/afssyscalls.h>
180 #include "ihandle.h"
181 #include "vnode.h"
182 #include "volume.h"
183 #include "partition.h"
184 #include "daemon_com.h"
185 #include "fssync.h"
186 #include "salvsync.h"
187 #include "viceinode.h"
188 #include "salvage.h"
189 #include "volinodes.h"          /* header magic number, etc. stuff */
190 #include "vol-salvage.h"
191 #include "vol_internal.h"
192
193 #ifdef FSSYNC_BUILD_CLIENT
194 #include "vg_cache.h"
195 #endif
196
197 #ifdef AFS_NT40_ENV
198 #include <pthread.h>
199 #endif
200
201 /*@+fcnmacros +macrofcndecl@*/
202 #ifdef O_LARGEFILE
203 #ifdef S_SPLINT_S
204 extern off64_t afs_lseek(int FD, off64_t O, int F);
205 #endif /*S_SPLINT_S */
206 #define afs_lseek(FD, O, F)     lseek64(FD, (off64_t) (O), F)
207 #define afs_stat        stat64
208 #define afs_fstat       fstat64
209 #define afs_open        open64
210 #define afs_fopen       fopen64
211 #else /* !O_LARGEFILE */
212 #ifdef S_SPLINT_S
213 extern off_t afs_lseek(int FD, off_t O, int F);
214 #endif /*S_SPLINT_S */
215 #define afs_lseek(FD, O, F)     lseek(FD, (off_t) (O), F)
216 #define afs_stat        stat
217 #define afs_fstat       fstat
218 #define afs_open        open
219 #define afs_fopen       fopen
220 #endif /* !O_LARGEFILE */
221 /*@=fcnmacros =macrofcndecl@*/
222
223 #ifdef  AFS_OSF_ENV
224 extern void *calloc();
225 #endif
226 static char *TimeStamp(time_t clock, int precision);
227
228
229 int debug;                      /* -d flag */
230 extern int Testing;             /* -n flag */
231 int ListInodeOption;            /* -i flag */
232 int ShowRootFiles;              /* -r flag */
233 int RebuildDirs;                /* -sal flag */
234 int Parallel = 4;               /* -para X flag */
235 int PartsPerDisk = 8;           /* Salvage up to 8 partitions on same disk sequentially */
236 int forceR = 0;                 /* -b flag */
237 int ShowLog = 0;                /* -showlog flag */
238 int ShowSuid = 0;               /* -showsuid flag */
239 int ShowMounts = 0;             /* -showmounts flag */
240 int orphans = ORPH_IGNORE;      /* -orphans option */
241 int Showmode = 0;
242
243
244 #ifndef AFS_NT40_ENV
245 int useSyslog = 0;              /* -syslog flag */
246 int useSyslogFacility = LOG_DAEMON;     /* -syslogfacility option */
247 #endif
248
249 #ifdef AFS_NT40_ENV
250 int canfork = 0;
251 #else
252 int canfork = 1;
253 #endif
254
255 #define MAXPARALLEL     32
256
257 int OKToZap;                    /* -o flag */
258 int ForceSalvage;               /* If salvage should occur despite the DONT_SALVAGE flag
259                                  * in the volume header */
260
261 FILE *logFile = 0;      /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
262
263 #define ROOTINODE       2       /* Root inode of a 4.2 Unix file system
264                                  * partition */
265 Device fileSysDevice;           /* The device number of the current
266                                  * partition being salvaged */
267 #ifdef AFS_NT40_ENV
268 char fileSysPath[8];
269 #else
270 char *fileSysPath;              /* The path of the mounted partition currently
271                                  * being salvaged, i.e. the directory
272                                  * containing the volume headers */
273 #endif
274 char *fileSysPathName;          /* NT needs this to make name pretty in log. */
275 IHandle_t *VGLinkH;             /* Link handle for current volume group. */
276 int VGLinkH_cnt;                /* # of references to lnk handle. */
277 struct DiskPartition64 *fileSysPartition;       /* Partition  being salvaged */
278 #ifndef AFS_NT40_ENV
279 char *fileSysDeviceName;        /* The block device where the file system
280                                  * being salvaged was mounted */
281 char *filesysfulldev;
282 #endif
283 int VolumeChanged;              /* Set by any routine which would change the volume in
284                                  * a way which would require callback is to be broken if the
285                                  * volume was put back on line by an active file server */
286
287 VolumeDiskData VolInfo;         /* A copy of the last good or salvaged volume header dealt with */
288
289 int nVolumesInInodeFile;        /* Number of read-write volumes summarized */
290 int inodeFd;                    /* File descriptor for inode file */
291
292
293 struct VnodeInfo vnodeInfo[nVNODECLASSES];
294
295
296 struct VolumeSummary *volumeSummaryp = NULL;    /* Holds all the volumes in a part */
297 int nVolumes;                   /* Number of volumes (read-write and read-only)
298                                  * in volume summary */
299
300 char *tmpdir = NULL;
301
302
303
304 /* Forward declarations */
305 /*@printflike@*/ void Log(const char *format, ...);
306 /*@printflike@*/ void Abort(const char *format, ...);
307 static int IsVnodeOrphaned(VnodeId vnode);
308 static int AskVolumeSummary(VolumeId singleVolumeNumber);
309
310 /* Uniquifier stored in the Inode */
311 static Unique
312 IUnique(Unique u)
313 {
314 #ifdef  AFS_3DISPARES
315     return (u & 0x3fffff);
316 #else
317 #if defined(AFS_SGI_EXMAG)
318     return (u & SGI_UNIQMASK);
319 #else
320     return (u);
321 #endif /* AFS_SGI_EXMAG */
322 #endif
323 }
324
325 static int
326 BadError(register int aerror)
327 {
328     if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
329         return 1;
330     return 0;                   /* otherwise may be transient, e.g. EMFILE */
331 }
332
333 #define MAX_ARGS 128
334 #ifdef AFS_NT40_ENV
335 char *save_args[MAX_ARGS];
336 int n_save_args = 0;
337 extern pthread_t main_thread;
338 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD, "" };
339 #endif
340
341 /* Get the salvage lock if not already held. Hold until process exits. */
342 void
343 ObtainSalvageLock(void)
344 {
345     FD_t salvageLock;
346
347 #ifdef AFS_NT40_ENV
348     salvageLock =
349         (FD_t)CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
350                         OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
351     if (salvageLock == INVALID_FD) {
352         fprintf(stderr,
353                 "salvager:  There appears to be another salvager running!  Aborted.\n");
354         Exit(1);
355     }
356 #else
357     salvageLock =
358         afs_open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT | O_RDWR, 0666);
359     if (salvageLock < 0) {
360         fprintf(stderr,
361                 "salvager:  can't open salvage lock file %s, aborting\n",
362                 AFSDIR_SERVER_SLVGLOCK_FILEPATH);
363         Exit(1);
364     }
365 #ifdef AFS_DARWIN_ENV
366     if (flock(salvageLock, LOCK_EX) == -1) {
367 #else
368     if (lockf(salvageLock, F_LOCK, 0) == -1) {
369 #endif
370         fprintf(stderr,
371                 "salvager:  There appears to be another salvager running!  Aborted.\n");
372         Exit(1);
373     }
374 #endif
375 }
376
377
378 #ifdef AFS_SGI_XFS_IOPS_ENV
379 /* Check if the given partition is mounted. For XFS, the root inode is not a
380  * constant. So we check the hard way.
381  */
382 int
383 IsPartitionMounted(char *part)
384 {
385     FILE *mntfp;
386     struct mntent *mntent;
387
388     assert(mntfp = setmntent(MOUNTED, "r"));
389     while (mntent = getmntent(mntfp)) {
390         if (!strcmp(part, mntent->mnt_dir))
391             break;
392     }
393     endmntent(mntfp);
394
395     return mntent ? 1 : 1;
396 }
397 #endif
398 /* Check if the given inode is the root of the filesystem. */
399 #ifndef AFS_SGI_XFS_IOPS_ENV
400 int
401 IsRootInode(struct afs_stat *status)
402 {
403     /*
404      * The root inode is not a fixed value in XFS partitions. So we need to
405      * see if the partition is in the list of mounted partitions. This only 
406      * affects the SalvageFileSys path, so we check there.
407      */
408     return (status->st_ino == ROOTINODE);
409 }
410 #endif
411
412 #ifdef AFS_AIX42_ENV
413 #ifndef AFS_NAMEI_ENV
414 /* We don't want to salvage big files filesystems, since we can't put volumes on
415  * them.
416  */
417 int
418 CheckIfBigFilesFS(char *mountPoint, char *devName)
419 {
420     struct superblock fs;
421     char name[128];
422
423     if (strncmp(devName, "/dev/", 5)) {
424         (void)sprintf(name, "/dev/%s", devName);
425     } else {
426         (void)strcpy(name, devName);
427     }
428
429     if (ReadSuper(&fs, name) < 0) {
430         Log("Unable to read superblock. Not salvaging partition %s.\n",
431             mountPoint);
432         return 1;
433     }
434     if (IsBigFilesFileSystem(&fs)) {
435         Log("Partition %s is a big files filesystem, not salvaging.\n",
436             mountPoint);
437         return 1;
438     }
439     return 0;
440 }
441 #endif
442 #endif
443
444 #ifdef AFS_NT40_ENV
445 #define HDSTR "\\Device\\Harddisk"
446 #define HDLEN  (sizeof(HDSTR)-1)        /* Length of "\Device\Harddisk" */
447 int
448 SameDisk(struct DiskPartition64 *p1, struct DiskPartition64 *p2)
449 {
450 #define RES_LEN 256
451     char res[RES_LEN];
452     int d1, d2;
453     static int dowarn = 1;
454
455     if (!QueryDosDevice(p1->devName, res, RES_LEN - 1))
456         return 1;
457     if (strncmp(res, HDSTR, HDLEN)) {
458         if (dowarn) {
459             dowarn = 0;
460             Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
461                 res, HDSTR, p1->devName);
462         }
463         return 1;
464     }
465     d1 = atoi(&res[HDLEN]);
466
467     if (!QueryDosDevice(p2->devName, res, RES_LEN - 1))
468         return 1;
469     if (strncmp(res, HDSTR, HDLEN)) {
470         if (dowarn) {
471             dowarn = 0;
472             Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
473                 res, HDSTR, p2->devName);
474         }
475         return 1;
476     }
477     d2 = atoi(&res[HDLEN]);
478
479     return d1 == d2;
480 }
481 #else
482 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
483 #endif
484
485 /* This assumes that two partitions with the same device number divided by
486  * PartsPerDisk are on the same disk.
487  */
488 void
489 SalvageFileSysParallel(struct DiskPartition64 *partP)
490 {
491     struct job {
492         struct DiskPartition64 *partP;
493         int pid;                /* Pid for this job */
494         int jobnumb;            /* Log file job number */
495         struct job *nextjob;    /* Next partition on disk to salvage */
496     };
497     static struct job *jobs[MAXPARALLEL] = { 0 };       /* Need to zero this */
498     struct job *thisjob = 0;
499     static int numjobs = 0;
500     static int jobcount = 0;
501     char buf[1024];
502     int wstatus;
503     struct job *oldjob;
504     int startjob;
505     FILE *passLog;
506     char logFileName[256];
507     int i, j, pid;
508
509     if (partP) {
510         /* We have a partition to salvage. Copy it into thisjob */
511         thisjob = (struct job *)malloc(sizeof(struct job));
512         if (!thisjob) {
513             Log("Can't salvage '%s'. Not enough memory\n", partP->name);
514             return;
515         }
516         memset(thisjob, 0, sizeof(struct job));
517         thisjob->partP = partP;
518         thisjob->jobnumb = jobcount;
519         jobcount++;
520     } else if (jobcount == 0) {
521         /* We are asking to wait for all jobs (partp == 0), yet we never
522          * started any.
523          */
524         Log("No file system partitions named %s* found; not salvaged\n",
525             VICE_PARTITION_PREFIX);
526         return;
527     }
528
529     if (debug || Parallel == 1) {
530         if (thisjob) {
531             SalvageFileSys(thisjob->partP, 0);
532             free(thisjob);
533         }
534         return;
535     }
536
537     if (thisjob) {
538         /* Check to see if thisjob is for a disk that we are already 
539          * salvaging. If it is, link it in as the next job to do. The
540          * jobs array has 1 entry per disk being salvages. numjobs is 
541          * the total number of disks currently being salvaged. In 
542          * order to keep thejobs array compact, when a disk is
543          * completed, the hightest element in the jobs array is moved 
544          * down to now open slot.
545          */
546         for (j = 0; j < numjobs; j++) {
547             if (SameDisk(jobs[j]->partP, thisjob->partP)) {
548                 /* On same disk, add it to this list and return */
549                 thisjob->nextjob = jobs[j]->nextjob;
550                 jobs[j]->nextjob = thisjob;
551                 thisjob = 0;
552                 break;
553             }
554         }
555     }
556
557     /* Loop until we start thisjob or until all existing jobs are finished */
558     while (thisjob || (!partP && (numjobs > 0))) {
559         startjob = -1;          /* No new job to start */
560
561         if ((numjobs >= Parallel) || (!partP && (numjobs > 0))) {
562             /* Either the max jobs are running or we have to wait for all
563              * the jobs to finish. In either case, we wait for at least one
564              * job to finish. When it's done, clean up after it.
565              */
566             pid = wait(&wstatus);
567             assert(pid != -1);
568             for (j = 0; j < numjobs; j++) {     /* Find which job it is */
569                 if (pid == jobs[j]->pid)
570                     break;
571             }
572             assert(j < numjobs);
573             if (WCOREDUMP(wstatus)) {   /* Say if the job core dumped */
574                 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
575             }
576
577             numjobs--;          /* job no longer running */
578             oldjob = jobs[j];   /* remember */
579             jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
580             free(oldjob);       /* free the old job */
581
582             /* If there is another partition on the disk to salvage, then 
583              * say we will start it (startjob). If not, then put thisjob there
584              * and say we will start it.
585              */
586             if (jobs[j]) {      /* Another partitions to salvage */
587                 startjob = j;   /* Will start it */
588             } else {            /* There is not another partition to salvage */
589                 if (thisjob) {
590                     jobs[j] = thisjob;  /* Add thisjob */
591                     thisjob = 0;
592                     startjob = j;       /* Will start it */
593                 } else {
594                     jobs[j] = jobs[numjobs];    /* Move last job up to this slot */
595                     startjob = -1;      /* Don't start it - already running */
596                 }
597             }
598         } else {
599             /* We don't have to wait for a job to complete */
600             if (thisjob) {
601                 jobs[numjobs] = thisjob;        /* Add this job */
602                 thisjob = 0;
603                 startjob = numjobs;     /* Will start it */
604             }
605         }
606
607         /* Start up a new salvage job on a partition in job slot "startjob" */
608         if (startjob != -1) {
609             if (!Showmode)
610                 Log("Starting salvage of file system partition %s\n",
611                     jobs[startjob]->partP->name);
612 #ifdef AFS_NT40_ENV
613             /* For NT, we not only fork, but re-exec the salvager. Pass in the
614              * commands and pass the child job number via the data path.
615              */
616             pid =
617                 nt_SalvagePartition(jobs[startjob]->partP->name,
618                                     jobs[startjob]->jobnumb);
619             jobs[startjob]->pid = pid;
620             numjobs++;
621 #else
622             pid = Fork();
623             if (pid) {
624                 jobs[startjob]->pid = pid;
625                 numjobs++;
626             } else {
627                 int fd;
628
629                 ShowLog = 0;
630                 for (fd = 0; fd < 16; fd++)
631                     close(fd);
632                 open("/", 0);
633                 dup2(0, 1);
634                 dup2(0, 2);
635 #ifndef AFS_NT40_ENV
636                 if (useSyslog) {
637                     openlog("salvager", LOG_PID, useSyslogFacility);
638                 } else
639 #endif
640                 {
641                     (void)afs_snprintf(logFileName, sizeof logFileName,
642                                        "%s.%d",
643                                        AFSDIR_SERVER_SLVGLOG_FILEPATH,
644                                        jobs[startjob]->jobnumb);
645                     logFile = afs_fopen(logFileName, "w");
646                 }
647                 if (!logFile)
648                     logFile = stdout;
649
650                 SalvageFileSys1(jobs[startjob]->partP, 0);
651                 Exit(0);
652             }
653 #endif
654         }
655     }                           /* while ( thisjob || (!partP && numjobs > 0) ) */
656
657     /* If waited for all jobs to complete, now collect log files and return */
658 #ifndef AFS_NT40_ENV
659     if (!useSyslog)             /* if syslogging - no need to collect */
660 #endif
661         if (!partP) {
662             for (i = 0; i < jobcount; i++) {
663                 (void)afs_snprintf(logFileName, sizeof logFileName, "%s.%d",
664                                    AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
665                 if ((passLog = afs_fopen(logFileName, "r"))) {
666                     while (fgets(buf, sizeof(buf), passLog)) {
667                         fputs(buf, logFile);
668                     }
669                     fclose(passLog);
670                 }
671                 (void)unlink(logFileName);
672             }
673             fflush(logFile);
674         }
675     return;
676 }
677
678
679 void
680 SalvageFileSys(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
681 {
682     if (!canfork || debug || Fork() == 0) {
683         SalvageFileSys1(partP, singleVolumeNumber);
684         if (canfork && !debug) {
685             ShowLog = 0;
686             Exit(0);
687         }
688     } else
689         Wait("SalvageFileSys");
690 }
691
692 char *
693 get_DevName(char *pbuffer, char *wpath)
694 {
695     char pbuf[128], *ptr;
696     strcpy(pbuf, pbuffer);
697     ptr = (char *)strrchr(pbuf, '/');
698     if (ptr) {
699         *ptr = '\0';
700         strcpy(wpath, pbuf);
701     } else
702         return NULL;
703     ptr = (char *)strrchr(pbuffer, '/');
704     if (ptr) {
705         strcpy(pbuffer, ptr + 1);
706         return pbuffer;
707     } else
708         return NULL;
709 }
710
711 void
712 SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
713 {
714     char *name, *tdir;
715     char inodeListPath[256];
716     FILE *inodeFile;
717     static char tmpDevName[100];
718     static char wpath[100];
719     struct VolumeSummary *vsp, *esp;
720     int i, j;
721     int code;
722
723     fileSysPartition = partP;
724     fileSysDevice = fileSysPartition->device;
725     fileSysPathName = VPartitionPath(fileSysPartition);
726
727 #ifdef AFS_NT40_ENV
728     /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
729     (void)sprintf(fileSysPath, "%s\\", fileSysPathName);
730     name = partP->devName;
731 #else
732     fileSysPath = fileSysPathName;
733     strcpy(tmpDevName, partP->devName);
734     name = get_DevName(tmpDevName, wpath);
735     fileSysDeviceName = name;
736     filesysfulldev = wpath;
737 #endif
738
739     VLockPartition(partP->name);
740     if (singleVolumeNumber || ForceSalvage)
741         ForceSalvage = 1;
742     else
743         ForceSalvage = UseTheForceLuke(fileSysPath);
744
745     if (singleVolumeNumber) {
746         /* salvageserver already setup fssync conn for us */
747         if ((programType != salvageServer) && !VConnectFS()) {
748             Abort("Couldn't connect to file server\n");
749         }
750         AskOffline(singleVolumeNumber, partP->name);
751     } else {
752         if (!Showmode)
753             Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n",
754                 partP->name, name, (Testing ? "(READONLY mode)" : ""));
755         if (ForceSalvage)
756             Log("***Forced salvage of all volumes on this partition***\n");
757     }
758
759
760     /*
761      * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.* 
762      * files 
763      */
764     {
765         DIR *dirp;
766         struct dirent *dp;
767
768         assert((dirp = opendir(fileSysPath)) != NULL);
769         while ((dp = readdir(dirp))) {
770             if (!strncmp(dp->d_name, "salvage.inodes.", 15)
771                 || !strncmp(dp->d_name, "salvage.temp.", 13)) {
772                 char npath[1024];
773                 Log("Removing old salvager temp files %s\n", dp->d_name);
774                 strcpy(npath, fileSysPath);
775                 strcat(npath, "/");
776                 strcat(npath, dp->d_name);
777                 unlink(npath);
778             }
779         }
780         closedir(dirp);
781     }
782     tdir = (tmpdir ? tmpdir : fileSysPath);
783 #ifdef AFS_NT40_ENV
784     (void)_putenv("TMP=");      /* If "TMP" is set, then that overrides tdir. */
785     (void)strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
786 #else
787     snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name,
788              getpid());
789 #endif
790
791     inodeFile = fopen(inodeListPath, "w+b");
792     if (!inodeFile) {
793         Abort("Error %d when creating inode description file %s; not salvaged\n", errno, inodeListPath);
794     }
795 #ifdef AFS_NT40_ENV
796     /* Using nt_unlink here since we're really using the delete on close
797      * semantics of unlink. In most places in the salvager, we really do
798      * mean to unlink the file at that point. Those places have been
799      * modified to actually do that so that the NT crt can be used there.
800      */
801     code = nt_unlink(inodeListPath);
802 #else
803     code = unlink(inodeListPath);
804 #endif
805     if (code < 0) {
806         Log("Error %d when trying to unlink %s\n", errno, inodeListPath);
807     }
808
809     if (GetInodeSummary(inodeFile, singleVolumeNumber) < 0) {
810         fclose(inodeFile);
811         return;
812     }
813     inodeFd = fileno(inodeFile);
814     if (inodeFd == -1)
815         Abort("Temporary file %s is missing...\n", inodeListPath);
816     afs_lseek(inodeFd, 0L, SEEK_SET);
817     if (ListInodeOption) {
818         PrintInodeList();
819         return;
820     }
821     /* enumerate volumes in the partition.
822      * figure out sets of read-only + rw volumes.
823      * salvage each set, read-only volumes first, then read-write.
824      * Fix up inodes on last volume in set (whether it is read-write
825      * or read-only).
826      */
827     GetVolumeSummary(singleVolumeNumber);
828
829     for (i = j = 0, vsp = volumeSummaryp, esp = vsp + nVolumes;
830          i < nVolumesInInodeFile; i = j) {
831         VolumeId rwvid = inodeSummary[i].RWvolumeId;
832         for (j = i;
833              j < nVolumesInInodeFile && inodeSummary[j].RWvolumeId == rwvid;
834              j++) {
835             VolumeId vid = inodeSummary[j].volumeId;
836             struct VolumeSummary *tsp;
837             /* Scan volume list (from partition root directory) looking for the
838              * current rw volume number in the volume list from the inode scan.
839              * If there is one here that is not in the inode volume list,
840              * delete it now. */
841             for (; vsp < esp && (vsp->header.parent < rwvid); vsp++) {
842                 if (vsp->fileName)
843                     DeleteExtraVolumeHeaderFile(vsp);
844             }
845             /* Now match up the volume summary info from the root directory with the
846              * entry in the volume list obtained from scanning inodes */
847             inodeSummary[j].volSummary = NULL;
848             for (tsp = vsp; tsp < esp && (tsp->header.parent == rwvid); tsp++) {
849                 if (tsp->header.id == vid) {
850                     inodeSummary[j].volSummary = tsp;
851                     tsp->fileName = 0;
852                     break;
853                 }
854             }
855         }
856         /* Salvage the group of volumes (several read-only + 1 read/write)
857          * starting with the current read-only volume we're looking at.
858          */
859         SalvageVolumeGroup(&inodeSummary[i], j - i);
860     }
861
862     /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
863     for (; vsp < esp; vsp++) {
864         if (vsp->fileName)
865             DeleteExtraVolumeHeaderFile(vsp);
866     }
867
868     if (!singleVolumeNumber)    /* Remove the FORCESALVAGE file */
869         RemoveTheForce(fileSysPath);
870
871     if (!Testing && singleVolumeNumber) {
872         AskOnline(singleVolumeNumber, fileSysPartition->name);
873
874         /* Step through the volumeSummary list and set all volumes on-line.
875          * The volumes were taken off-line in GetVolumeSummary.
876          */
877         for (j = 0; j < nVolumes; j++) {
878             AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
879         }
880     } else {
881         if (!Showmode)
882             Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
883                 fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
884     }
885
886     fclose(inodeFile);          /* SalvageVolumeGroup was the last which needed it. */
887 }
888
889 void
890 DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
891 {
892     char path[64];
893     sprintf(path, "%s/%s", fileSysPath, vsp->fileName);
894
895     if (!Showmode)
896         Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", path, (Testing ? "would have been " : ""));
897     if (!Testing) {
898         afs_int32 code;
899         code = VDestroyVolumeDiskHeader(fileSysPartition, vsp->header.id, vsp->header.parent);
900         if (code) {
901             Log("Error %ld destroying volume disk header for volume %lu\n",
902                 afs_printable_int32_ld(code),
903                 afs_printable_uint32_lu(vsp->header.id));
904         }
905
906         /* make sure we actually delete the fileName file; ENOENT
907          * is fine, since VDestroyVolumeDiskHeader probably already
908          * unlinked it */
909         if (unlink(path) && errno != ENOENT) {
910             Log("Unable to unlink %s (errno = %d)\n", path, errno);
911         }
912     }
913     vsp->fileName = 0;
914 }
915
916 int
917 CompareInodes(const void *_p1, const void *_p2)
918 {
919     register const struct ViceInodeInfo *p1 = _p1;
920     register const struct ViceInodeInfo *p2 = _p2;
921     if (p1->u.vnode.vnodeNumber == INODESPECIAL
922         || p2->u.vnode.vnodeNumber == INODESPECIAL) {
923         VolumeId p1rwid, p2rwid;
924         p1rwid =
925             (p1->u.vnode.vnodeNumber ==
926              INODESPECIAL ? p1->u.special.parentId : p1->u.vnode.volumeId);
927         p2rwid =
928             (p2->u.vnode.vnodeNumber ==
929              INODESPECIAL ? p2->u.special.parentId : p2->u.vnode.volumeId);
930         if (p1rwid < p2rwid)
931             return -1;
932         if (p1rwid > p2rwid)
933             return 1;
934         if (p1->u.vnode.vnodeNumber == INODESPECIAL
935             && p2->u.vnode.vnodeNumber == INODESPECIAL) {
936             if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
937                 return (p1->u.special.type < p2->u.special.type ? -1 : 1);
938             if (p1->u.vnode.volumeId == p1rwid)
939                 return -1;
940             if (p2->u.vnode.volumeId == p2rwid)
941                 return 1;
942             return (p1->u.vnode.volumeId < p2->u.vnode.volumeId ? -1 : 1);
943         }
944         if (p1->u.vnode.vnodeNumber != INODESPECIAL)
945             return (p2->u.vnode.volumeId == p2rwid ? 1 : -1);
946         return (p1->u.vnode.volumeId == p1rwid ? -1 : 1);
947     }
948     if (p1->u.vnode.volumeId < p2->u.vnode.volumeId)
949         return -1;
950     if (p1->u.vnode.volumeId > p2->u.vnode.volumeId)
951         return 1;
952     if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
953         return -1;
954     if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
955         return 1;
956     /* The following tests are reversed, so that the most desirable
957      * of several similar inodes comes first */
958     if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
959 #ifdef  AFS_3DISPARES
960         if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */  &&
961             p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
962             return 1;
963 #endif
964 #ifdef  AFS_SGI_EXMAG
965         if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */  &&
966             p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
967             return 1;
968 #endif
969         return -1;
970     }
971     if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
972 #ifdef  AFS_3DISPARES
973         if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */  &&
974             p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
975             return -1;
976 #endif
977 #ifdef  AFS_SGI_EXMAG
978         if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */  &&
979             p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
980             return 1;
981 #endif
982         return 1;
983     }
984     if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
985 #ifdef  AFS_3DISPARES
986         if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */  &&
987             p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
988             return 1;
989 #endif
990 #ifdef  AFS_SGI_EXMAG
991         if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */  &&
992             p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
993             return 1;
994 #endif
995         return -1;
996     }
997     if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
998 #ifdef  AFS_3DISPARES
999         if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */  &&
1000             p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1001             return -1;
1002 #endif
1003 #ifdef  AFS_SGI_EXMAG
1004         if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */  &&
1005             p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1006             return 1;
1007 #endif
1008         return 1;
1009     }
1010     return 0;
1011 }
1012
1013 void
1014 CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1015                   register struct InodeSummary *summary)
1016 {
1017     VolumeId volume = ip->u.vnode.volumeId;
1018     VolumeId rwvolume = volume;
1019     register int n, nSpecial;
1020     register Unique maxunique;
1021     n = nSpecial = 0;
1022     maxunique = 0;
1023     while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1024         n++;
1025         if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1026             nSpecial++;
1027             rwvolume = ip->u.special.parentId;
1028             /* This isn't quite right, as there could (in error) be different
1029              * parent inodes in different special vnodes */
1030         } else {
1031             if (maxunique < ip->u.vnode.vnodeUniquifier)
1032                 maxunique = ip->u.vnode.vnodeUniquifier;
1033         }
1034         ip++;
1035     }
1036     summary->volumeId = volume;
1037     summary->RWvolumeId = rwvolume;
1038     summary->nInodes = n;
1039     summary->nSpecialInodes = nSpecial;
1040     summary->maxUniquifier = maxunique;
1041 }
1042
1043 int
1044 OnlyOneVolume(struct ViceInodeInfo *inodeinfo, afs_uint32 singleVolumeNumber, void *rock)
1045 {
1046     if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1047         return (inodeinfo->u.special.parentId == singleVolumeNumber);
1048     return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1049 }
1050
1051 /* GetInodeSummary
1052  *
1053  * Collect list of inodes in file named by path. If a truly fatal error,
1054  * unlink the file and abort. For lessor errors, return -1. The file will
1055  * be unlinked by the caller.
1056  */
1057 int
1058 GetInodeSummary(FILE *inodeFile, VolumeId singleVolumeNumber)
1059 {
1060     struct afs_stat status;
1061     int forceSal, err;
1062     int code;
1063     struct ViceInodeInfo *ip;
1064     struct InodeSummary summary;
1065     char summaryFileName[50];
1066     FILE *summaryFile;
1067 #ifdef AFS_NT40_ENV
1068     char *dev = fileSysPath;
1069     char *wpath = fileSysPath;
1070 #else
1071     char *dev = fileSysDeviceName;
1072     char *wpath = filesysfulldev;
1073 #endif
1074     char *part = fileSysPath;
1075     char *tdir;
1076
1077     /* This file used to come from vfsck; cobble it up ourselves now... */
1078     if ((err =
1079          ListViceInodes(dev, fileSysPath, inodeFile,
1080                         singleVolumeNumber ? OnlyOneVolume : 0,
1081                         singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
1082         if (err == -2) {
1083             Log("*** I/O error %d when writing a tmp inode file; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n", errno, dev);
1084             return -1;
1085         }
1086         Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1087     }
1088     if (forceSal && !ForceSalvage) {
1089         Log("***Forced salvage of all volumes on this partition***\n");
1090         ForceSalvage = 1;
1091     }
1092     fseek(inodeFile, 0L, SEEK_SET);
1093     inodeFd = fileno(inodeFile);
1094     if (inodeFd == -1 || afs_fstat(inodeFd, &status) == -1) {
1095         Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1096     }
1097     tdir = (tmpdir ? tmpdir : part);
1098 #ifdef AFS_NT40_ENV
1099     (void)_putenv("TMP=");      /* If "TMP" is set, then that overrides tdir. */
1100     (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1101 #else
1102     (void)afs_snprintf(summaryFileName, sizeof summaryFileName,
1103                        "%s/salvage.temp.%d", tdir, getpid());
1104 #endif
1105     summaryFile = afs_fopen(summaryFileName, "a+");
1106     if (summaryFile == NULL) {
1107         Abort("Unable to create inode summary file\n");
1108     }
1109
1110 #ifdef AFS_NT40_ENV
1111     /* Using nt_unlink here since we're really using the delete on close
1112      * semantics of unlink. In most places in the salvager, we really do
1113      * mean to unlink the file at that point. Those places have been
1114      * modified to actually do that so that the NT crt can be used there.
1115      */
1116     code = nt_unlink(summaryFileName);
1117 #else
1118     code = unlink(summaryFileName);
1119 #endif
1120     if (code < 0) {
1121         Log("Error %d when trying to unlink %s\n", errno, summaryFileName);
1122     }
1123
1124     if (!canfork || debug || Fork() == 0) {
1125         int nInodes;
1126         unsigned long st_size=(unsigned long) status.st_size;
1127         nInodes = st_size / sizeof(struct ViceInodeInfo);
1128         if (nInodes == 0) {
1129             fclose(summaryFile);
1130             if (!singleVolumeNumber)    /* Remove the FORCESALVAGE file */
1131                 RemoveTheForce(fileSysPath);
1132             else {
1133                 struct VolumeSummary *vsp;
1134                 int i;
1135
1136                 GetVolumeSummary(singleVolumeNumber);
1137
1138                 for (i = 0, vsp = volumeSummaryp; i < nVolumes; i++) {
1139                     if (vsp->fileName)
1140                         DeleteExtraVolumeHeaderFile(vsp);
1141                 }
1142             }
1143             Log("%s vice inodes on %s; not salvaged\n",
1144                 singleVolumeNumber ? "No applicable" : "No", dev);
1145             return -1;
1146         }
1147         ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
1148         if (ip == NULL) {
1149             fclose(summaryFile);
1150             Abort
1151                 ("Unable to allocate enough space to read inode table; %s not salvaged\n",
1152                  dev);
1153         }
1154         if (read(inodeFd, ip, st_size) != st_size) {
1155             fclose(summaryFile);
1156             Abort("Unable to read inode table; %s not salvaged\n", dev);
1157         }
1158         qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1159         if (afs_lseek(inodeFd, 0, SEEK_SET) == -1
1160             || write(inodeFd, ip, st_size) != st_size) {
1161             fclose(summaryFile);
1162             Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1163         }
1164         summary.index = 0;
1165         while (nInodes) {
1166             CountVolumeInodes(ip, nInodes, &summary);
1167             if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
1168                 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1169                 fclose(summaryFile);
1170                 return -1;
1171             }
1172             summary.index += (summary.nInodes);
1173             nInodes -= summary.nInodes;
1174             ip += summary.nInodes;
1175         }
1176         /* Following fflush is not fclose, because if it was debug mode would not work */
1177         if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1178             Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1179             fclose(summaryFile);
1180             return -1;
1181         }
1182         if (canfork && !debug) {
1183             ShowLog = 0;
1184             Exit(0);
1185         }
1186     } else {
1187         if (Wait("Inode summary") == -1) {
1188             fclose(summaryFile);
1189             Exit(1);            /* salvage of this partition aborted */
1190         }
1191     }
1192     assert(afs_fstat(fileno(summaryFile), &status) != -1);
1193     if (status.st_size != 0) {
1194         int ret;
1195         unsigned long st_status=(unsigned long)status.st_size;
1196         inodeSummary = (struct InodeSummary *)malloc(st_status);
1197         assert(inodeSummary != NULL);
1198         /* For GNU we need to do lseek to get the file pointer moved. */
1199         assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1200         ret = read(fileno(summaryFile), inodeSummary, st_status);
1201         assert(ret == st_status);
1202     }
1203     nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
1204     Log("%d nVolumesInInodeFile %d \n",nVolumesInInodeFile,(unsigned long)(status.st_size));
1205     fclose(summaryFile);
1206     return 0;
1207 }
1208
1209 /* Comparison routine for volume sort.
1210    This is setup so that a read-write volume comes immediately before
1211    any read-only clones of that volume */
1212 int
1213 CompareVolumes(const void *_p1, const void *_p2)
1214 {
1215     register const struct VolumeSummary *p1 = _p1;
1216     register const struct VolumeSummary *p2 = _p2;
1217     if (p1->header.parent != p2->header.parent)
1218         return p1->header.parent < p2->header.parent ? -1 : 1;
1219     if (p1->header.id == p1->header.parent)     /* p1 is rw volume */
1220         return -1;
1221     if (p2->header.id == p2->header.parent)     /* p2 is rw volume */
1222         return 1;
1223     return p1->header.id < p2->header.id ? -1 : 1;      /* Both read-only */
1224 }
1225
1226 /**
1227  * Gleans volumeSummary information by asking the fileserver
1228  *
1229  * @param[in] singleVolumeNumber  the volume we're salvaging. 0 if we're
1230  *                                salvaging a whole partition
1231  *
1232  * @return whether we obtained the volume summary information or not
1233  *  @retval 0 success; we obtained the volume summary information
1234  *  @retval nonzero we did not get the volume summary information; either the
1235  *            fileserver responded with an error, or we are not supposed to
1236  *            ask the fileserver for the information (e.g. we are salvaging
1237  *            the entire partition or we are not the salvageserver)
1238  *
1239  * @note for non-DAFS, always returns 1
1240  */
1241 static int
1242 AskVolumeSummary(VolumeId singleVolumeNumber)
1243 {
1244     afs_int32 code = 1;
1245 #ifdef FSSYNC_BUILD_CLIENT
1246     if (programType == salvageServer) {
1247         if (singleVolumeNumber) {
1248             FSSYNC_VGQry_response_t q_res;
1249             SYNC_response res;
1250             struct VolumeSummary *vsp;
1251             int i;
1252             struct VolumeDiskHeader diskHdr;
1253
1254             memset(&res, 0, sizeof(res));
1255
1256             code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1257
1258             /*
1259              * We must wait for the partition to finish scanning before
1260              * can continue, since we will not know if we got the entire
1261              * VG membership unless the partition is fully scanned.
1262              * We could, in theory, just scan the partition ourselves if
1263              * the VG cache is not ready, but we would be doing the exact
1264              * same scan the fileserver is doing; it will almost always
1265              * be faster to wait for the fileserver. The only exceptions
1266              * are if the partition does not take very long to scan, and
1267              * in that case it's fast either way, so who cares?
1268              */
1269             if (code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING) {
1270                 Log("waiting for fileserver to finish scanning partition %s...\n",
1271                     fileSysPartition->name);
1272
1273                 for (i = 1; code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING; i++) {
1274                     /* linearly ramp up from 1 to 10 seconds; nothing fancy,
1275                      * just so small partitions don't need to wait over 10
1276                      * seconds every time, and large partitions are generally
1277                      * polled only once every ten seconds. */
1278                     sleep((i > 10) ? (i = 10) : i);
1279
1280                     code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1281                 }
1282             }
1283
1284             if (code == SYNC_FAILED && res.hdr.reason == FSYNC_UNKNOWN_VOLID) {
1285                 /* This can happen if there's no header for the volume
1286                  * we're salvaging, or no headers exist for the VG (if
1287                  * we're salvaging an RW). Act as if we got a response
1288                  * with no VG members. The headers may be created during
1289                  * salvaging, if there are inodes in this VG. */
1290                 code = 0;
1291                 memset(&q_res, 0, sizeof(q_res));
1292                 q_res.rw = singleVolumeNumber;
1293             }
1294
1295             if (code) {
1296                 Log("fileserver refused VGCQuery request for volume %lu on "
1297                     "partition %s, code %ld reason %ld\n",
1298                     afs_printable_uint32_lu(singleVolumeNumber),
1299                     fileSysPartition->name,
1300                     afs_printable_int32_ld(code),
1301                     afs_printable_int32_ld(res.hdr.reason));
1302                 goto done;
1303             }
1304
1305             if (q_res.rw != singleVolumeNumber) {
1306                 Log("fileserver requested salvage of clone %lu; scheduling salvage of volume group %lu...\n",
1307                     afs_printable_uint32_lu(singleVolumeNumber),
1308                     afs_printable_uint32_lu(q_res.rw));
1309 #ifdef SALVSYNC_BUILD_CLIENT
1310                 if (SALVSYNC_LinkVolume(q_res.rw,
1311                                        singleVolumeNumber,
1312                                        fileSysPartition->name,
1313                                        NULL) != SYNC_OK) {
1314                     Log("schedule request failed\n");
1315                 }
1316 #endif /* SALVSYNC_BUILD_CLIENT */
1317                 Exit(SALSRV_EXIT_VOLGROUP_LINK);
1318             }
1319
1320             volumeSummaryp = malloc(VOL_VG_MAX_VOLS * sizeof(struct VolumeSummary));
1321             assert(volumeSummaryp != NULL);
1322
1323             nVolumes = 0;
1324             vsp = volumeSummaryp;
1325
1326             for (i = 0; i < VOL_VG_MAX_VOLS; i++) {
1327                 char name[VMAXPATHLEN];
1328
1329                 if (!q_res.children[i]) {
1330                     continue;
1331                 }
1332
1333                 if (q_res.children[i] != singleVolumeNumber) {
1334                     AskOffline(q_res.children[i], fileSysPartition->name);
1335                 }
1336                 code = VReadVolumeDiskHeader(q_res.children[i], fileSysPartition, &diskHdr);
1337                 if (code) {
1338                     Log("Cannot read header for %lu; trying to salvage group anyway\n",
1339                         afs_printable_uint32_lu(q_res.children[i]));
1340                     code = 0;
1341                     continue;
1342                 }
1343
1344                 DiskToVolumeHeader(&vsp->header, &diskHdr);
1345                 VolumeExternalName_r(q_res.children[i], name, sizeof(name));
1346                 vsp->fileName = ToString(name);
1347                 nVolumes++;
1348                 vsp++;
1349             }
1350
1351             qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1352                   CompareVolumes);
1353         }
1354       done:
1355         if (code) {
1356             Log("Cannot get volume summary from fileserver; falling back to scanning "
1357                 "entire partition\n");
1358         }
1359     }
1360 #endif /* FSSYNC_BUILD_CLIENT */
1361     return code;
1362 }
1363
1364 /**
1365  * count how many volume headers are found by VWalkVolumeHeaders.
1366  *
1367  * @param[in] dp   the disk partition (unused)
1368  * @param[in] name full path to the .vol header (unused)
1369  * @param[in] hdr  the header data (unused)
1370  * @param[in] last whether this is the last try or not (unused)
1371  * @param[in] rock actually an afs_int32*; the running count of how many
1372  *                 volumes we have found
1373  *
1374  * @retval 0 always
1375  */
1376 static int
1377 CountHeader(struct DiskPartition64 *dp, const char *name,
1378             struct VolumeDiskHeader *hdr, int last, void *rock)
1379 {
1380     afs_int32 *nvols = (afs_int32 *)rock;
1381     (*nvols)++;
1382     return 0;
1383 }
1384
1385 /**
1386  * parameters to pass to the VWalkVolumeHeaders callbacks when recording volume
1387  * data.
1388  */
1389 struct SalvageScanParams {
1390     VolumeId singleVolumeNumber; /**< 0 for a partition-salvage, otherwise the
1391                                   * vol id of the VG we're salvaging */
1392     struct VolumeSummary *vsp;   /**< ptr to the current volume summary object
1393                                   * we're filling in */
1394     afs_int32 nVolumes;          /**< # of vols we've encountered */
1395     afs_int32 totalVolumes;      /**< max # of vols we should encounter (the
1396                                   * # of vols we've alloc'd memory for) */
1397 };
1398
1399 /**
1400  * records volume summary info found from VWalkVolumeHeaders.
1401  *
1402  * Found volumes are also taken offline if they are in the specific volume
1403  * group we are looking for.
1404  *
1405  * @param[in] dp   the disk partition
1406  * @param[in] name full path to the .vol header
1407  * @param[in] hdr  the header data
1408  * @param[in] last 1 if this is the last try to read the header, 0 otherwise
1409  * @param[in] rock actually a struct SalvageScanParams*, containing the
1410  *                 information needed to record the volume summary data
1411  *
1412  * @return operation status
1413  *  @retval 0 success
1414  *  @retval 1 volume header is mis-named and should be deleted
1415  */
1416 static int
1417 RecordHeader(struct DiskPartition64 *dp, const char *name,
1418              struct VolumeDiskHeader *hdr, int last, void *rock)
1419 {
1420     char nameShouldBe[64];
1421     struct SalvageScanParams *params;
1422     struct VolumeSummary summary;
1423     VolumeId singleVolumeNumber;
1424
1425     params = (struct SalvageScanParams *)rock;
1426
1427     singleVolumeNumber = params->singleVolumeNumber;
1428
1429     DiskToVolumeHeader(&summary.header, hdr);
1430
1431     if (singleVolumeNumber && summary.header.id == singleVolumeNumber
1432         && summary.header.parent != singleVolumeNumber) {
1433
1434         if (programType == salvageServer) {
1435 #ifdef SALVSYNC_BUILD_CLIENT
1436             Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n",
1437                 summary.header.id, summary.header.parent);
1438             if (SALVSYNC_LinkVolume(summary.header.parent,
1439                                     summary.header.id,
1440                                     dp->name,
1441                                     NULL) != SYNC_OK) {
1442                 Log("schedule request failed\n");
1443             }
1444 #endif
1445             Exit(SALSRV_EXIT_VOLGROUP_LINK);
1446
1447         } else {
1448             Log("%u is a read-only volume; not salvaged\n",
1449                 singleVolumeNumber);
1450             Exit(1);
1451         }
1452     }
1453
1454     if (!singleVolumeNumber || summary.header.id == singleVolumeNumber
1455         || summary.header.parent == singleVolumeNumber) {
1456
1457         /* check if the header file is incorrectly named */
1458         int badname = 0;
1459         const char *base = strrchr(name, '/');
1460         if (base) {
1461             base++;
1462         } else {
1463             base = name;
1464         }
1465
1466         (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1467                            VFORMAT, afs_printable_uint32_lu(summary.header.id));
1468
1469
1470         if (strcmp(nameShouldBe, base)) {
1471             /* .vol file has wrong name; retry/delete */
1472             badname = 1;
1473         }
1474
1475         if (!badname || last) {
1476             /* only offline the volume if the header is good, or if this is
1477              * the last try looking at it; avoid AskOffline'ing the same vol
1478              * multiple times */
1479
1480             if (singleVolumeNumber 
1481                 && summary.header.id != singleVolumeNumber) {
1482                 /* don't offline singleVolumeNumber; we already did that
1483                  * earlier */
1484
1485                 AskOffline(summary.header.id, fileSysPartition->name);
1486             }
1487         }
1488         if (badname) {
1489             if (last && !Showmode) {
1490                 Log("Volume header file %s is incorrectly named (should be %s "
1491                     "not %s); %sdeleted (it will be recreated later, if "
1492                     "necessary)\n", name, nameShouldBe, base,
1493                     (Testing ? "it would have been " : ""));
1494             }
1495             return 1;
1496         }
1497
1498         summary.fileName = ToString(base);
1499         params->nVolumes++;
1500
1501         if (params->nVolumes > params->totalVolumes) {
1502             /* We found more volumes than we found on the first partition walk;
1503              * apparently something created a volume while we were
1504              * partition-salvaging, or we found more than 20 vols when salvaging a
1505              * particular volume. Abort if we detect this, since other programs
1506              * supposed to not touch the partition while it is partition-salvaging,
1507              * and we shouldn't find more than 20 vols in a VG.
1508              */
1509             Abort("Found %ld vol headers, but should have found at most %ld! "
1510                   "Make sure the volserver/fileserver are not running at the "
1511                   "same time as a partition salvage\n",
1512                   afs_printable_int32_ld(params->nVolumes),
1513                   afs_printable_int32_ld(params->totalVolumes));
1514         }
1515
1516         memcpy(params->vsp, &summary, sizeof(summary));
1517         params->vsp++;
1518     }
1519
1520     return 0;
1521 }
1522
1523 /**
1524  * possibly unlinks bad volume headers found from VWalkVolumeHeaders.
1525  *
1526  * If the header could not be read in at all, the header is always unlinked.
1527  * If instead RecordHeader said the header was bad (that is, the header file
1528  * is mis-named), we only unlink if we are doing a partition salvage, as
1529  * opposed to salvaging a specific volume group.
1530  *
1531  * @param[in] dp   the disk partition
1532  * @param[in] name full path to the .vol header
1533  * @param[in] hdr  header data, or NULL if the header could not be read
1534  * @param[in] rock actually a struct SalvageScanParams*, with some information
1535  *                 about the scan
1536  */
1537 static void
1538 UnlinkHeader(struct DiskPartition64 *dp, const char *name,
1539              struct VolumeDiskHeader *hdr, void *rock)
1540 {
1541     struct SalvageScanParams *params;
1542     int dounlink = 0;
1543
1544     params = (struct SalvageScanParams *)rock;
1545
1546     if (!hdr) {
1547         /* no header; header is too bogus to read in at all */
1548         if (!Showmode) {
1549             Log("%s is not a legitimate volume header file; %sdeleted\n", name, (Testing ? "it would have been " : ""));
1550         }
1551         if (!Testing) {
1552             dounlink = 1;
1553         }
1554
1555     } else if (!params->singleVolumeNumber) {
1556         /* We were able to read in a header, but RecordHeader said something
1557          * was wrong with it. We only unlink those if we are doing a partition
1558          * salvage. */
1559         if (!Testing) {
1560             dounlink = 1;
1561         }
1562     }
1563
1564     if (dounlink && unlink(name)) {
1565         Log("Error %d while trying to unlink %s\n", errno, name);
1566     }
1567 }
1568
1569 void
1570 GetVolumeSummary(VolumeId singleVolumeNumber)
1571 {
1572     afs_int32 nvols = 0;
1573     struct SalvageScanParams params;
1574     int code;
1575
1576     if (AskVolumeSummary(singleVolumeNumber) == 0) {
1577         /* we successfully got the vol information from the fileserver; no
1578          * need to scan the partition */
1579         return;
1580     }
1581
1582     if (!singleVolumeNumber) {
1583         /* Count how many volumes we have in /vicepX */
1584         code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, CountHeader,
1585                                   NULL, &nvols);
1586         if (code < 0) {
1587             Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1588         }
1589         if (!nvols)
1590             nvols = 1;
1591     } else {
1592         nvols = VOL_VG_MAX_VOLS;
1593     }
1594
1595     volumeSummaryp = malloc(nvols * sizeof(struct VolumeSummary));
1596     assert(volumeSummaryp != NULL);
1597
1598     params.singleVolumeNumber = singleVolumeNumber;
1599     params.vsp = volumeSummaryp;
1600     params.nVolumes = 0;
1601     params.totalVolumes = nvols;
1602
1603     /* walk the partition directory of volume headers and record the info
1604      * about them; unlinking invalid headers */
1605     code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, RecordHeader,
1606                               UnlinkHeader, &params);
1607     if (code < 0) {
1608         Abort("Failed to get volume header summary\n");
1609     }
1610     nVolumes = params.nVolumes;
1611
1612     qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1613           CompareVolumes);
1614 }
1615
1616 /* Find the link table. This should be associated with the RW volume or, if
1617  * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1618  */
1619 Inode
1620 FindLinkHandle(register struct InodeSummary *isp, int nVols,
1621                struct ViceInodeInfo *allInodes)
1622 {
1623     int i, j;
1624     struct ViceInodeInfo *ip;
1625
1626     for (i = 0; i < nVols; i++) {
1627         ip = allInodes + isp[i].index;
1628         for (j = 0; j < isp[i].nSpecialInodes; j++) {
1629             if (ip[j].u.special.type == VI_LINKTABLE)
1630                 return ip[j].inodeNumber;
1631         }
1632     }
1633     return (Inode) - 1;
1634 }
1635
1636 int
1637 CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1638 {
1639     struct versionStamp version;
1640     FdHandle_t *fdP;
1641
1642     if (!VALID_INO(ino))
1643         ino =
1644             IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1645                       INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1646     if (!VALID_INO(ino))
1647         Abort
1648             ("Unable to allocate link table inode for volume %u (error = %d)\n",
1649              isp->RWvolumeId, errno);
1650     IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1651     fdP = IH_OPEN(VGLinkH);
1652     if (fdP == NULL)
1653         Abort("Can't open link table for volume %u (error = %d)\n",
1654               isp->RWvolumeId, errno);
1655
1656     if (FDH_TRUNC(fdP, sizeof(version) + sizeof(short)) < 0)
1657         Abort("Can't truncate link table for volume %u (error = %d)\n",
1658               isp->RWvolumeId, errno);
1659
1660     version.magic = LINKTABLEMAGIC;
1661     version.version = LINKTABLEVERSION;
1662
1663     if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
1664         != sizeof(version))
1665         Abort("Can't truncate link table for volume %u (error = %d)\n",
1666               isp->RWvolumeId, errno);
1667
1668     FDH_REALLYCLOSE(fdP);
1669
1670     /* If the volume summary exits (i.e.,  the V*.vol header file exists),
1671      * then set this inode there as well.
1672      */
1673     if (isp->volSummary)
1674         isp->volSummary->header.linkTable = ino;
1675
1676     return 0;
1677 }
1678
1679 #ifdef AFS_NT40_ENV
1680 void *
1681 nt_SVG(void *arg)
1682 {
1683     SVGParms_t *parms = (SVGParms_t *) arg;
1684     DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1685     return NULL;
1686 }
1687
1688 void
1689 SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1690 {
1691     pthread_t tid;
1692     pthread_attr_t tattr;
1693     int code;
1694     SVGParms_t parms;
1695
1696     /* Initialize per volume global variables, even if later code does so */
1697     VolumeChanged = 0;
1698     VGLinkH = NULL;
1699     VGLinkH_cnt = 0;
1700     memset(&VolInfo, 0, sizeof(VolInfo));
1701
1702     parms.svgp_inodeSummaryp = isp;
1703     parms.svgp_count = nVols;
1704     code = pthread_attr_init(&tattr);
1705     if (code) {
1706         Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1707             isp->RWvolumeId);
1708         return;
1709     }
1710     code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1711     if (code) {
1712         Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1713         return;
1714     }
1715     code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1716     if (code) {
1717         Log("Failed to create thread to salvage volume group %u\n",
1718             isp->RWvolumeId);
1719         return;
1720     }
1721     (void)pthread_join(tid, NULL);
1722 }
1723 #endif /* AFS_NT40_ENV */
1724
1725 void
1726 DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1727 {
1728     struct ViceInodeInfo *inodes, *allInodes, *ip;
1729     int i, totalInodes, size, salvageTo;
1730     int haveRWvolume;
1731     int check;
1732     Inode ino;
1733     int dec_VGLinkH = 0;
1734     int VGLinkH_p1 =0;
1735     FdHandle_t *fdP = NULL;
1736
1737     VGLinkH_cnt = 0;
1738     haveRWvolume = (isp->volumeId == isp->RWvolumeId
1739                     && isp->nSpecialInodes > 0);
1740     if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1741         if (!ForceSalvage && QuickCheck(isp, nVols))
1742             return;
1743     }
1744     if (ShowMounts && !haveRWvolume)
1745         return;
1746     if (canfork && !debug && Fork() != 0) {
1747         (void)Wait("Salvage volume group");
1748         return;
1749     }
1750     for (i = 0, totalInodes = 0; i < nVols; i++)
1751         totalInodes += isp[i].nInodes;
1752     size = totalInodes * sizeof(struct ViceInodeInfo);
1753     inodes = (struct ViceInodeInfo *)malloc(size);
1754     allInodes = inodes - isp->index;    /* this would the base of all the inodes
1755                                          * for the partition, if all the inodes
1756                                          * had been read into memory */
1757     assert(afs_lseek
1758            (inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1759             SEEK_SET) != -1);
1760     assert(read(inodeFd, inodes, size) == size);
1761
1762     /* Don't try to salvage a read write volume if there isn't one on this
1763      * partition */
1764     salvageTo = haveRWvolume ? 0 : 1;
1765
1766 #ifdef AFS_NAMEI_ENV
1767     ino = FindLinkHandle(isp, nVols, allInodes);
1768     if (VALID_INO(ino)) {
1769         IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1770         fdP = IH_OPEN(VGLinkH);
1771     }
1772     if (!VALID_INO(ino) || fdP == NULL) {
1773         Log("%s link table for volume %u.\n",
1774             Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1775         if (Testing) {
1776             IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1777         } else {
1778             int i, j;
1779             struct ViceInodeInfo *ip;
1780             CreateLinkTable(isp, ino);
1781             fdP = IH_OPEN(VGLinkH);
1782             /* Sync fake 1 link counts to the link table, now that it exists */
1783             if (fdP) {
1784                 for (i = 0; i < nVols; i++) {
1785                         ip = allInodes + isp[i].index;
1786                          for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
1787 #ifdef AFS_NT40_ENV
1788                                  nt_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1789 #else
1790                                  namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1791 #endif
1792                     }
1793                 }
1794             }
1795         }
1796     }
1797     if (fdP)
1798         FDH_REALLYCLOSE(fdP);
1799 #else
1800     IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1801 #endif
1802
1803     /* Salvage in reverse order--read/write volume last; this way any
1804      * Inodes not referenced by the time we salvage the read/write volume
1805      * can be picked up by the read/write volume */
1806     /* ACTUALLY, that's not done right now--the inodes just vanish */
1807     for (i = nVols - 1; i >= salvageTo; i--) {
1808         int rw = (i == 0);
1809         struct InodeSummary *lisp = &isp[i];
1810 #ifdef AFS_NAMEI_ENV
1811         /* If only the RO is present on this partition, the link table
1812          * shows up as a RW volume special file. Need to make sure the
1813          * salvager doesn't try to salvage the non-existent RW.
1814          */
1815         if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1816             /* If this only special inode is the link table, continue */
1817             if (inodes->u.special.type == VI_LINKTABLE) {
1818                 haveRWvolume = 0;
1819                 continue;
1820             }
1821         }
1822 #endif
1823         if (!Showmode)
1824             Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1825                 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1826         /* Check inodes twice.  The second time do things seriously.  This
1827          * way the whole RO volume can be deleted, below, if anything goes wrong */
1828         for (check = 1; check >= 0; check--) {
1829             int deleteMe;
1830             if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
1831                 == -1) {
1832                 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
1833                 if (rw && deleteMe) {
1834                     haveRWvolume = 0;   /* This will cause its inodes to be deleted--since salvage
1835                                          * volume won't be called */
1836                     break;
1837                 }
1838                 if (!rw)
1839                     break;
1840             }
1841             if (rw && check == 1)
1842                 continue;
1843             if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
1844                 MaybeZapVolume(lisp, "Vnode index", 0, check);
1845                 break;
1846             }
1847         }
1848     }
1849
1850     /* Fix actual inode counts */
1851     if (!Showmode) {
1852         Log("totalInodes %d\n",totalInodes);
1853         for (ip = inodes; totalInodes; ip++, totalInodes--) {
1854             static int TraceBadLinkCounts = 0;
1855 #ifdef AFS_NAMEI_ENV
1856             if (VGLinkH->ih_ino == ip->inodeNumber) {
1857                 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1858                 VGLinkH_p1 = ip->u.param[0];
1859                 continue;       /* Deal with this last. */
1860             }
1861 #endif
1862             if (ip->linkCount != 0 && TraceBadLinkCounts) {
1863                 TraceBadLinkCounts--;   /* Limit reports, per volume */
1864                 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %llu, p=(%u,%u,%u,%u)\n", ip->linkCount, PrintInode(NULL, ip->inodeNumber), (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
1865             }
1866             while (ip->linkCount > 0) {
1867                 /* below used to assert, not break */
1868                 if (!Testing) {
1869                     if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1870                         Log("idec failed. inode %s errno %d\n",
1871                             PrintInode(NULL, ip->inodeNumber), errno);
1872                         break;
1873                     }
1874                 }
1875                 ip->linkCount--;
1876             }
1877             while (ip->linkCount < 0) {
1878                 /* these used to be asserts */
1879                 if (!Testing) {
1880                     if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1881                         Log("iinc failed. inode %s errno %d\n",
1882                             PrintInode(NULL, ip->inodeNumber), errno);
1883                         break;
1884                     }
1885                 }
1886                 ip->linkCount++;
1887             }
1888         }
1889 #ifdef AFS_NAMEI_ENV
1890         while (dec_VGLinkH > 0) {
1891             if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1892                 Log("idec failed on link table, errno = %d\n", errno);
1893             }
1894             dec_VGLinkH--;
1895         }
1896         while (dec_VGLinkH < 0) {
1897             if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1898                 Log("iinc failed on link table, errno = %d\n", errno);
1899             }
1900             dec_VGLinkH++;
1901         }
1902 #endif
1903     }
1904     free(inodes);
1905     /* Directory consistency checks on the rw volume */
1906     if (haveRWvolume)
1907         SalvageVolume(isp, VGLinkH);
1908     IH_RELEASE(VGLinkH);
1909
1910     if (canfork && !debug) {
1911         ShowLog = 0;
1912         Exit(0);
1913     }
1914 }
1915
1916 int
1917 QuickCheck(register struct InodeSummary *isp, int nVols)
1918 {
1919     /* Check headers BEFORE forking */
1920     register int i;
1921     IHandle_t *h;
1922
1923     for (i = 0; i < nVols; i++) {
1924         struct VolumeSummary *vs = isp[i].volSummary;
1925         VolumeDiskData volHeader;
1926         if (!vs) {
1927             /* Don't salvage just because phantom rw volume is there... */
1928             /* (If a read-only volume exists, read/write inodes must also exist) */
1929             if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
1930                 continue;
1931             return 0;
1932         }
1933         IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1934         if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
1935             == sizeof(volHeader)
1936             && volHeader.stamp.magic == VOLUMEINFOMAGIC
1937             && volHeader.dontSalvage == DONT_SALVAGE
1938             && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
1939             if (volHeader.inUse != 0) {
1940                 volHeader.inUse = 0;
1941                 volHeader.inService = 1;
1942                 if (!Testing) {
1943                     if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
1944                         != sizeof(volHeader)) {
1945                         IH_RELEASE(h);
1946                         return 0;
1947                     }
1948                 }
1949             }
1950             IH_RELEASE(h);
1951         } else {
1952             IH_RELEASE(h);
1953             return 0;
1954         }
1955     }
1956     return 1;
1957 }
1958
1959
1960 /* SalvageVolumeHeaderFile
1961  *
1962  * Salvage the top level V*.vol header file. Make sure the special files
1963  * exist and that there are no duplicates.
1964  *
1965  * Calls SalvageHeader for each possible type of volume special file.
1966  */
1967
1968 int
1969 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
1970                         register struct ViceInodeInfo *inodes, int RW,
1971                         int check, int *deleteMe)
1972 {
1973     int i;
1974     register struct ViceInodeInfo *ip;
1975     int allinodesobsolete = 1;
1976     struct VolumeDiskHeader diskHeader;
1977     afs_int32 (*writefunc)(VolumeDiskHeader_t *, struct DiskPartition64 *) = NULL;
1978     int *skip;
1979
1980     /* keeps track of special inodes that are probably 'good'; they are
1981      * referenced in the vol header, and are included in the given inodes
1982      * array */
1983     struct {
1984         int valid;
1985         Inode inode;
1986     } goodspecial[MAXINODETYPE];
1987
1988     if (deleteMe)
1989         *deleteMe = 0;
1990
1991     memset(goodspecial, 0, sizeof(goodspecial));
1992
1993     skip = malloc(isp->nSpecialInodes * sizeof(*skip));
1994     if (skip) {
1995         memset(skip, 0, isp->nSpecialInodes * sizeof(*skip));
1996     } else {
1997         Log("cannot allocate memory for inode skip array when salvaging "
1998             "volume %lu; not performing duplicate special inode recovery\n",
1999             afs_printable_uint32_lu(isp->volumeId));
2000         /* still try to perform the salvage; the skip array only does anything
2001          * if we detect duplicate special inodes */
2002     }
2003
2004     /*
2005      * First, look at the special inodes and see if any are referenced by
2006      * the existing volume header. If we find duplicate special inodes, we
2007      * can use this information to use the referenced inode (it's more
2008      * likely to be the 'good' one), and throw away the duplicates.
2009      */
2010     if (isp->volSummary && skip) {
2011         /* use tempHeader, so we can use the stuff[] array to easily index
2012          * into the isp->volSummary special inodes */
2013         memcpy(&tempHeader, &isp->volSummary->header, sizeof(struct VolumeHeader));
2014
2015         for (i = 0; i < isp->nSpecialInodes; i++) {
2016             ip = &inodes[isp->index + i];
2017             if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2018                 /* will get taken care of in a later loop */
2019                 continue;
2020             }
2021             if (ip->inodeNumber == *(stuff[ip->u.special.type - 1].inode)) {
2022                 goodspecial[ip->u.special.type-1].valid = 1;
2023                 goodspecial[ip->u.special.type-1].inode = ip->inodeNumber;
2024             }
2025         }
2026     }
2027
2028     memset(&tempHeader, 0, sizeof(tempHeader));
2029     tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2030     tempHeader.stamp.version = VOLUMEHEADERVERSION;
2031     tempHeader.id = isp->volumeId;
2032     tempHeader.parent = isp->RWvolumeId;
2033
2034     /* Check for duplicates (inodes are sorted by type field) */
2035     for (i = 0; i < isp->nSpecialInodes - 1; i++) {
2036         ip = &inodes[isp->index + i];
2037         if (ip->u.special.type == (ip + 1)->u.special.type) {
2038             afs_ino_str_t stmp1, stmp2;
2039
2040             if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2041                 /* Will be caught in the loop below */
2042                 continue;
2043             }
2044             if (!Showmode) {
2045                 Log("Duplicate special %d inodes for volume %u found (%s, %s);\n",
2046                     ip->u.special.type, isp->volumeId,
2047                     PrintInode(stmp1, ip->inodeNumber),
2048                     PrintInode(stmp2, (ip+1)->inodeNumber));
2049             }
2050             if (skip && goodspecial[ip->u.special.type-1].valid) {
2051                 Inode gi = goodspecial[ip->u.special.type-1].inode;
2052
2053                 if (!Showmode) {
2054                     Log("using special inode referenced by vol header (%s)\n",
2055                         PrintInode(stmp1, gi));
2056                 }
2057
2058                 /* the volume header references some special inode of
2059                  * this type in the inodes array; are we it? */
2060                 if (ip->inodeNumber != gi) {
2061                     skip[i] = 1;
2062                 } else if ((ip+1)->inodeNumber != gi) {
2063                     /* in case this is the last iteration; we need to
2064                      * make sure we check ip+1, too */
2065                     skip[i+1] = 1;
2066                 }
2067             } else {
2068                 if (!Showmode)
2069                     Log("cannot determine which is correct; salvage of volume %u aborted\n", isp->volumeId);
2070                 if (skip) {
2071                     free(skip);
2072                 }
2073                 return -1;
2074             }
2075         }
2076     }
2077     for (i = 0; i < isp->nSpecialInodes; i++) {
2078         ip = &inodes[isp->index + i];
2079         if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2080             if (check) {
2081                 Log("Rubbish header inode %s of type %d\n",
2082                     PrintInode(NULL, ip->inodeNumber),
2083                     ip->u.special.type);
2084                 if (skip) {
2085                     free(skip);
2086                 }
2087                 return -1;
2088             }
2089             Log("Rubbish header inode %s of type %d; deleted\n",
2090                 PrintInode(NULL, ip->inodeNumber),
2091                 ip->u.special.type);
2092         } else if (!stuff[ip->u.special.type - 1].obsolete) {
2093             if (skip && skip[i]) {
2094                 if (orphans == ORPH_REMOVE) {
2095                     Log("Removing orphan special inode %s of type %d\n",
2096                         PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
2097                     continue;
2098                 } else {
2099                     Log("Ignoring orphan special inode %s of type %d\n",
2100                         PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
2101                     /* fall through to the ip->linkCount--; line below */
2102                 }
2103             } else {
2104                 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
2105                 allinodesobsolete = 0;
2106             }
2107             if (!check && ip->u.special.type != VI_LINKTABLE)
2108                 ip->linkCount--;        /* Keep the inode around */
2109         }
2110     }
2111     if (skip) {
2112         free(skip);
2113     }
2114     skip = NULL;
2115
2116     if (allinodesobsolete) {
2117         if (deleteMe)
2118             *deleteMe = 1;
2119         return -1;
2120     }
2121
2122     if (!check)
2123         VGLinkH_cnt++;          /* one for every header. */
2124
2125     if (!RW && !check && isp->volSummary) {
2126         ClearROInUseBit(isp->volSummary);
2127         return 0;
2128     }
2129
2130     for (i = 0; i < MAXINODETYPE; i++) {
2131         if (stuff[i].inodeType == VI_LINKTABLE) {
2132             /* Gross hack: SalvageHeader does a bcmp on the volume header.
2133              * And we may have recreated the link table earlier, so set the
2134              * RW header as well.
2135              */
2136             if (VALID_INO(VGLinkH->ih_ino)) {
2137                 *stuff[i].inode = VGLinkH->ih_ino;
2138             }
2139             continue;
2140         }
2141         if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
2142             return -1;
2143     }
2144
2145     if (isp->volSummary == NULL) {
2146         char path[64];
2147         char headerName[64];
2148         (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
2149         (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
2150         if (check) {
2151             Log("No header file for volume %u\n", isp->volumeId);
2152             return -1;
2153         }
2154         if (!Showmode)
2155             Log("No header file for volume %u; %screating %s\n",
2156                 isp->volumeId, (Testing ? "it would have been " : ""),
2157                 path);
2158         isp->volSummary = (struct VolumeSummary *)
2159             malloc(sizeof(struct VolumeSummary));
2160         isp->volSummary->fileName = ToString(headerName);
2161
2162         writefunc = VCreateVolumeDiskHeader;
2163     } else {
2164         char path[64];
2165         char headerName[64];
2166         /* hack: these two fields are obsolete... */
2167         isp->volSummary->header.volumeAcl = 0;
2168         isp->volSummary->header.volumeMountTable = 0;
2169
2170         if (memcmp
2171             (&isp->volSummary->header, &tempHeader,
2172              sizeof(struct VolumeHeader))) {
2173             /* We often remove the name before calling us, so we make a fake one up */
2174             if (isp->volSummary->fileName) {
2175                 strcpy(headerName, isp->volSummary->fileName);
2176             } else {
2177                 (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
2178                 isp->volSummary->fileName = ToString(headerName);
2179             }
2180             (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
2181
2182             Log("Header file %s is damaged or no longer valid%s\n", path,
2183                 (check ? "" : "; repairing"));
2184             if (check)
2185                 return -1;
2186
2187             writefunc = VWriteVolumeDiskHeader;
2188         }
2189     }
2190     if (writefunc) {
2191         memcpy(&isp->volSummary->header, &tempHeader,
2192                sizeof(struct VolumeHeader));
2193         if (Testing) {
2194             if (!Showmode)
2195                 Log("It would have written a new header file for volume %u\n",
2196                     isp->volumeId);
2197         } else {
2198             afs_int32 code;
2199             VolumeHeaderToDisk(&diskHeader, &tempHeader);
2200             code = (*writefunc)(&diskHeader, fileSysPartition);
2201             if (code) {
2202                 Log("Error %ld writing volume header file for volume %lu\n",
2203                     afs_printable_int32_ld(code),
2204                     afs_printable_uint32_lu(diskHeader.id));
2205                 return -1;
2206             }
2207         }
2208     }
2209     IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
2210             isp->volSummary->header.volumeInfo);
2211     return 0;
2212 }
2213
2214 int
2215 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
2216               int *deleteMe)
2217 {
2218     union {
2219         VolumeDiskData volumeInfo;
2220         struct versionStamp fileHeader;
2221     } header;
2222     IHandle_t *specH;
2223     int recreate = 0;
2224     afs_int32 code;
2225     FdHandle_t *fdP;
2226
2227     if (sp->obsolete)
2228         return 0;
2229 #ifndef AFS_NAMEI_ENV
2230     if (sp->inodeType == VI_LINKTABLE)
2231         return 0;
2232 #endif
2233     if (*(sp->inode) == 0) {
2234         if (check) {
2235             Log("Missing inode in volume header (%s)\n", sp->description);
2236             return -1;
2237         }
2238         if (!Showmode)
2239             Log("Missing inode in volume header (%s); %s\n", sp->description,
2240                 (Testing ? "it would have recreated it" : "recreating"));
2241         if (!Testing) {
2242             *(sp->inode) =
2243                 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
2244                           INODESPECIAL, sp->inodeType, isp->RWvolumeId);
2245             if (!VALID_INO(*(sp->inode)))
2246                 Abort
2247                     ("Unable to allocate inode (%s) for volume header (error = %d)\n",
2248                      sp->description, errno);
2249         }
2250         recreate = 1;
2251     }
2252
2253     IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2254     fdP = IH_OPEN(specH);
2255     if (OKToZap && (fdP == NULL) && BadError(errno)) {
2256         /* bail out early and destroy the volume */
2257         if (!Showmode)
2258             Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
2259         if (deleteMe)
2260             *deleteMe = 1;
2261         IH_RELEASE(specH);
2262         return -1;
2263     }
2264     if (fdP == NULL)
2265         Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2266               sp->description, errno);
2267
2268     if (!recreate
2269         && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
2270             || header.fileHeader.magic != sp->stamp.magic)) {
2271         if (check) {
2272             Log("Part of the header (%s) is corrupted\n", sp->description);
2273             FDH_REALLYCLOSE(fdP);
2274             IH_RELEASE(specH);
2275             return -1;
2276         }
2277         Log("Part of the header (%s) is corrupted; recreating\n",
2278             sp->description);
2279         recreate = 1;
2280     }
2281     if (sp->inodeType == VI_VOLINFO
2282         && header.volumeInfo.destroyMe == DESTROY_ME) {
2283         if (deleteMe)
2284             *deleteMe = 1;
2285         FDH_REALLYCLOSE(fdP);
2286         IH_RELEASE(specH);
2287         return -1;
2288     }
2289     if (recreate && !Testing) {
2290         if (check)
2291             Abort
2292                 ("Internal error: recreating volume header (%s) in check mode\n",
2293                  sp->description);
2294         code = FDH_TRUNC(fdP, 0);
2295         if (code == -1)
2296             Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2297                   sp->description, errno);
2298
2299         /* The following code should be moved into vutil.c */
2300         if (sp->inodeType == VI_VOLINFO) {
2301             struct timeval tp;
2302             memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
2303             header.volumeInfo.stamp = sp->stamp;
2304             header.volumeInfo.id = isp->volumeId;
2305             header.volumeInfo.parentId = isp->RWvolumeId;
2306             sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
2307             Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
2308                 isp->volumeId, isp->volumeId);
2309             header.volumeInfo.inService = 0;
2310             header.volumeInfo.blessed = 0;
2311             /* The + 1000 is a hack in case there are any files out in venus caches */
2312             header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
2313             header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume);     /* XXXX */
2314             header.volumeInfo.needsCallback = 0;
2315             gettimeofday(&tp, 0);
2316             header.volumeInfo.creationDate = tp.tv_sec;
2317             if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2318                 Abort
2319                     ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2320                      sp->description, errno);
2321             }
2322             code =
2323                 FDH_WRITE(fdP, (char *)&header.volumeInfo,
2324                           sizeof(header.volumeInfo));
2325             if (code != sizeof(header.volumeInfo)) {
2326                 if (code < 0)
2327                     Abort
2328                         ("Unable to write volume header file (%s) (errno = %d)\n",
2329                          sp->description, errno);
2330                 Abort("Unable to write entire volume header file (%s)\n",
2331                       sp->description);
2332             }
2333         } else {
2334             if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2335                 Abort
2336                     ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2337                      sp->description, errno);
2338             }
2339             code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
2340             if (code != sizeof(sp->stamp)) {
2341                 if (code < 0)
2342                     Abort
2343                         ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2344                          sp->description, errno);
2345                 Abort
2346                     ("Unable to write entire version stamp in volume header file (%s)\n",
2347                      sp->description);
2348             }
2349         }
2350     }
2351     FDH_REALLYCLOSE(fdP);
2352     IH_RELEASE(specH);
2353     if (sp->inodeType == VI_VOLINFO) {
2354         VolInfo = header.volumeInfo;
2355         if (check) {
2356             char update[25];
2357             if (VolInfo.updateDate) {
2358                 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2359                 if (!Showmode)
2360                     Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2361                         (Testing ? "it would have been " : ""), update);
2362             } else {
2363                 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2364                 if (!Showmode)
2365                     Log("%s (%u) not updated (created %s)\n", VolInfo.name,
2366                         VolInfo.id, update);
2367             }
2368
2369         }
2370     }
2371
2372     return 0;
2373 }
2374
2375 int
2376 SalvageVnodes(register struct InodeSummary *rwIsp,
2377               register struct InodeSummary *thisIsp,
2378               register struct ViceInodeInfo *inodes, int check)
2379 {
2380     int ilarge, ismall, ioffset, RW, nInodes;
2381     ioffset = rwIsp->index + rwIsp->nSpecialInodes;     /* first inode */
2382     if (Showmode)
2383         return 0;
2384     RW = (rwIsp == thisIsp);
2385     nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2386     ismall =
2387         SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
2388                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2389     if (check && ismall == -1)
2390         return -1;
2391     ilarge =
2392         SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
2393                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2394     return (ilarge == 0 && ismall == 0 ? 0 : -1);
2395 }
2396
2397 int
2398 SalvageIndex(Inode ino, VnodeClass class, int RW,
2399              register struct ViceInodeInfo *ip, int nInodes,
2400              struct VolumeSummary *volSummary, int check)
2401 {
2402     VolumeId volumeNumber;
2403     char buf[SIZEOF_LARGEDISKVNODE];
2404     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2405     int err = 0;
2406     StreamHandle_t *file;
2407     struct VnodeClassInfo *vcp;
2408     afs_sfsize_t size;
2409     afs_fsize_t vnodeLength;
2410     int vnodeIndex, nVnodes;
2411     afs_ino_str_t stmp1, stmp2;
2412     IHandle_t *handle;
2413     FdHandle_t *fdP;
2414
2415     volumeNumber = volSummary->header.id;
2416     IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2417     fdP = IH_OPEN(handle);
2418     assert(fdP != NULL);
2419     file = FDH_FDOPEN(fdP, "r+");
2420     assert(file != NULL);
2421     vcp = &VnodeClassInfo[class];
2422     size = OS_SIZE(fdP->fd_fd);
2423     assert(size != -1);
2424     nVnodes = (size / vcp->diskSize) - 1;
2425     if (nVnodes > 0) {
2426         assert((nVnodes + 1) * vcp->diskSize == size);
2427         assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2428     } else {
2429         nVnodes = 0;
2430     }
2431     for (vnodeIndex = 0;
2432          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2433          nVnodes--, vnodeIndex++) {
2434         if (vnode->type != vNull) {
2435             int vnodeChanged = 0;
2436             int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2437             /* Log programs that belong to root (potentially suid root);
2438              * don't bother for read-only or backup volumes */
2439 #ifdef  notdef                  /* This is done elsewhere */
2440             if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
2441                 Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n", VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author, vnode->owner, vnode->modeBits);
2442 #endif
2443             if (VNDISK_GET_INO(vnode) == 0) {
2444                 if (RW) {
2445                     /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2446                     memset(vnode, 0, vcp->diskSize);
2447                     vnodeChanged = 1;
2448                 }
2449             } else {
2450                 if (vcp->magic != vnode->vnodeMagic) {
2451                     /* bad magic #, probably partially created vnode */
2452                     Log("Partially allocated vnode %d deleted.\n",
2453                         vnodeNumber);
2454                     memset(vnode, 0, vcp->diskSize);
2455                     vnodeChanged = 1;
2456                     goto vnodeDone;
2457                 }
2458                 /* ****** Should do a bit more salvage here:  e.g. make sure
2459                  * vnode type matches what it should be given the index */
2460                 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2461 /*                  if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2462  *                     Log("Inode %d: says it belongs to non-existing vnode %d\n",
2463  *                         ip->inodeNumber, ip->u.vnode.vnodeNumber);
2464  *                  }
2465  */
2466                     ip++;
2467                     nInodes--;
2468                 }
2469                 if (!RW) {
2470                     while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2471                         /* The following doesn't work, because the version number
2472                          * is not maintained correctly by the file server */
2473                         /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2474                          * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2475                          * break; */
2476                         if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2477                             break;
2478                         ip++;
2479                         nInodes--;
2480                     }
2481                 } else {
2482                     /* For RW volume, look for vnode with matching inode number;
2483                      * if no such match, take the first determined by our sort
2484                      * order */
2485                     register struct ViceInodeInfo *lip = ip;
2486                     register int lnInodes = nInodes;
2487                     while (lnInodes
2488                            && lip->u.vnode.vnodeNumber == vnodeNumber) {
2489                         if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2490                             ip = lip;
2491                             nInodes = lnInodes;
2492                             break;
2493                         }
2494                         lip++;
2495                         lnInodes--;
2496                     }
2497                 }
2498                 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2499                     /* "Matching" inode */
2500                     if (RW) {
2501                         Unique vu, iu;
2502                         FileVersion vd, id;
2503                         vu = vnode->uniquifier;
2504                         iu = ip->u.vnode.vnodeUniquifier;
2505                         vd = vnode->dataVersion;
2506                         id = ip->u.vnode.inodeDataVersion;
2507                         /*
2508                          * Because of the possibility of the uniquifier overflows (> 4M)
2509                          * we compare them modulo the low 22-bits; we shouldn't worry
2510                          * about mismatching since they shouldn't to many old 
2511                          * uniquifiers of the same vnode...
2512                          */
2513                         if (IUnique(vu) != IUnique(iu)) {
2514                             if (!Showmode) {
2515                                 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2516                             }
2517
2518                             vnode->uniquifier = iu;
2519 #ifdef  AFS_3DISPARES
2520                             vnode->dataVersion = (id >= vd ?
2521                                                   /* 90% of 2.1M */
2522                                                   ((id - vd) >
2523                                                    1887437 ? vd : id) :
2524                                                   /* 90% of 2.1M */
2525                                                   ((vd - id) >
2526                                                    1887437 ? id : vd));
2527 #else
2528 #if defined(AFS_SGI_EXMAG)
2529                             vnode->dataVersion = (id >= vd ?
2530                                                   /* 90% of 16M */
2531                                                   ((id - vd) >
2532                                                    15099494 ? vd : id) :
2533                                                   /* 90% of 16M */
2534                                                   ((vd - id) >
2535                                                    15099494 ? id : vd));
2536 #else
2537                             vnode->dataVersion = (id > vd ? id : vd);
2538 #endif /* AFS_SGI_EXMAG */
2539 #endif /* AFS_3DISPARES */
2540                             vnodeChanged = 1;
2541                         } else {
2542                             /* don't bother checking for vd > id any more, since
2543                              * partial file transfers always result in this state,
2544                              * and you can't do much else anyway (you've already
2545                              * found the best data you can) */
2546 #ifdef  AFS_3DISPARES
2547                             if (!vnodeIsDirectory(vnodeNumber)
2548                                 && ((vd < id && (id - vd) < 1887437)
2549                                     || ((vd > id && (vd - id) > 1887437)))) {
2550 #else
2551 #if defined(AFS_SGI_EXMAG)
2552                             if (!vnodeIsDirectory(vnodeNumber)
2553                                 && ((vd < id && (id - vd) < 15099494)
2554                                     || ((vd > id && (vd - id) > 15099494)))) {
2555 #else
2556                             if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2557 #endif /* AFS_SGI_EXMAG */
2558 #endif
2559                                 if (!Showmode)
2560                                     Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2561                                 vnode->dataVersion = id;
2562                                 vnodeChanged = 1;
2563                             }
2564                         }
2565                     }
2566                     if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2567                         if (check) {
2568                             if (!Showmode) {
2569                                 Log("Vnode %d:  inode number incorrect (is %s should be %s). FileSize=%llu\n", vnodeNumber, PrintInode(stmp1, VNDISK_GET_INO(vnode)), PrintInode(stmp2, ip->inodeNumber), (afs_uintmax_t) ip->byteCount);
2570                             }
2571                             VNDISK_SET_INO(vnode, ip->inodeNumber);
2572                             err = -1;
2573                             goto zooks;
2574                         }
2575                         if (!Showmode) {
2576                             Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=%llu\n", vnodeNumber, PrintInode(stmp1, VNDISK_GET_INO(vnode)), PrintInode(stmp2, ip->inodeNumber), (afs_uintmax_t) ip->byteCount);
2577                         }
2578                         VNDISK_SET_INO(vnode, ip->inodeNumber);
2579                         vnodeChanged = 1;
2580                     }
2581                     VNDISK_GET_LEN(vnodeLength, vnode);
2582                     if (ip->byteCount != vnodeLength) {
2583                         if (check) {
2584                             if (!Showmode)
2585                                 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2586                             err = -1;
2587                             goto zooks;
2588                         }
2589                         if (!Showmode)
2590                             Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2591                         VNDISK_SET_LEN(vnode, ip->byteCount);
2592                         vnodeChanged = 1;
2593                     }
2594                     if (!check)
2595                         ip->linkCount--;        /* Keep the inode around */
2596                     ip++;
2597                     nInodes--;
2598                 } else {        /* no matching inode */
2599                     if (VNDISK_GET_INO(vnode) != 0
2600                         || vnode->type == vDirectory) {
2601                         /* No matching inode--get rid of the vnode */
2602                         if (check) {
2603                             if (VNDISK_GET_INO(vnode)) {
2604                                 if (!Showmode) {
2605                                     Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2606                                 }
2607                             } else {
2608                                 if (!Showmode)
2609                                     Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2610                             }
2611                             err = -1;
2612                             goto zooks;
2613                         }
2614                         if (VNDISK_GET_INO(vnode)) {
2615                             if (!Showmode) {
2616                                 time_t serverModifyTime = vnode->serverModifyTime;
2617                                 Log("Vnode %d (unique %u): corresponding inode %s is missing; vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)), ctime(&serverModifyTime));
2618                             }
2619                         } else {
2620                             if (!Showmode) {
2621                                 time_t serverModifyTime = vnode->serverModifyTime;
2622                                 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2623                             }
2624                         }
2625                         memset(vnode, 0, vcp->diskSize);
2626                         vnodeChanged = 1;
2627                     } else {
2628                         /* Should not reach here becuase we checked for 
2629                          * (inodeNumber == 0) above. And where we zero the vnode,
2630                          * we also goto vnodeDone.
2631                          */
2632                     }
2633                 }
2634                 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2635                     ip++;
2636                     nInodes--;
2637                 }
2638             }                   /* VNDISK_GET_INO(vnode) != 0 */
2639           vnodeDone:
2640             assert(!(vnodeChanged && check));
2641             if (vnodeChanged && !Testing) {
2642                 assert(IH_IWRITE
2643                        (handle, vnodeIndexOffset(vcp, vnodeNumber),
2644                         (char *)vnode, vcp->diskSize)
2645                        == vcp->diskSize);
2646                 VolumeChanged = 1;      /* For break call back */
2647             }
2648         }
2649     }
2650   zooks:
2651     STREAM_CLOSE(file);
2652     FDH_CLOSE(fdP);
2653     IH_RELEASE(handle);
2654     return err;
2655 }
2656
2657 struct VnodeEssence *
2658 CheckVnodeNumber(VnodeId vnodeNumber)
2659 {
2660     VnodeClass class;
2661     struct VnodeInfo *vip;
2662     int offset;
2663
2664     class = vnodeIdToClass(vnodeNumber);
2665     vip = &vnodeInfo[class];
2666     offset = vnodeIdToBitNumber(vnodeNumber);
2667     return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2668 }
2669
2670 void
2671 CopyOnWrite(register struct DirSummary *dir)
2672 {
2673     /* Copy the directory unconditionally if we are going to change it:
2674      * not just if was cloned.
2675      */
2676     struct VnodeDiskObject vnode;
2677     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2678     Inode oldinode, newinode;
2679     afs_sfsize_t code;
2680
2681     if (dir->copied || Testing)
2682         return;
2683     DFlush();                   /* Well justified paranoia... */
2684
2685     code =
2686         IH_IREAD(vnodeInfo[vLarge].handle,
2687                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2688                  sizeof(vnode));
2689     assert(code == sizeof(vnode));
2690     oldinode = VNDISK_GET_INO(&vnode);
2691     /* Increment the version number by a whole lot to avoid problems with
2692      * clients that were promised new version numbers--but the file server
2693      * crashed before the versions were written to disk.
2694      */
2695     newinode =
2696         IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2697                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2698                   200);
2699     assert(VALID_INO(newinode));
2700     assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2701     vnode.cloned = 0;
2702     VNDISK_SET_INO(&vnode, newinode);
2703     code =
2704         IH_IWRITE(vnodeInfo[vLarge].handle,
2705                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2706                   sizeof(vnode));
2707     assert(code == sizeof(vnode));
2708
2709     SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2710                         fileSysDevice, newinode);
2711     /* Don't delete the original inode right away, because the directory is
2712      * still being scanned.
2713      */
2714     dir->copied = 1;
2715 }
2716
2717 /*
2718  * This function should either successfully create a new dir, or give up 
2719  * and leave things the way they were.  In particular, if it fails to write 
2720  * the new dir properly, it should return w/o changing the reference to the 
2721  * old dir.
2722  */
2723 void
2724 CopyAndSalvage(register struct DirSummary *dir)
2725 {
2726     struct VnodeDiskObject vnode;
2727     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2728     Inode oldinode, newinode;
2729     DirHandle newdir;
2730     FdHandle_t *fdP;
2731     afs_int32 code;
2732     afs_sfsize_t lcode;
2733     afs_int32 parentUnique = 1;
2734     struct VnodeEssence *vnodeEssence;
2735     afs_fsize_t length;
2736
2737     if (Testing)
2738         return;
2739     Log("Salvaging directory %u...\n", dir->vnodeNumber);
2740     lcode =
2741         IH_IREAD(vnodeInfo[vLarge].handle,
2742                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2743                  sizeof(vnode));
2744     assert(lcode == sizeof(vnode));
2745     oldinode = VNDISK_GET_INO(&vnode);
2746     /* Increment the version number by a whole lot to avoid problems with
2747      * clients that were promised new version numbers--but the file server
2748      * crashed before the versions were written to disk.
2749      */
2750     newinode =
2751         IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2752                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2753                   200);
2754     assert(VALID_INO(newinode));
2755     SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2756
2757     /* Assign . and .. vnode numbers from dir and vnode.parent. 
2758      * The uniquifier for . is in the vnode.
2759      * The uniquifier for .. might be set to a bogus value of 1 and 
2760      * the salvager will later clean it up.
2761      */
2762     if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2763         parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2764     }
2765     code =
2766         DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2767                    vnode.uniquifier,
2768                    (vnode.parent ? vnode.parent : dir->vnodeNumber),
2769                    parentUnique);
2770     if (code == 0)
2771         code = DFlush();
2772     if (code) {
2773         /* didn't really build the new directory properly, let's just give up. */
2774         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2775         Log("Directory salvage returned code %d, continuing.\n", code);
2776         if (code) {
2777             Log("also failed to decrement link count on new inode");
2778         }
2779         assert(1 == 2);
2780     }
2781     Log("Checking the results of the directory salvage...\n");
2782     if (!DirOK(&newdir)) {
2783         Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2784         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2785         assert(code == 0);
2786         assert(1 == 2);
2787     }
2788     vnode.cloned = 0;
2789     VNDISK_SET_INO(&vnode, newinode);
2790     length = Length(&newdir);
2791     VNDISK_SET_LEN(&vnode, length);
2792     lcode =
2793         IH_IWRITE(vnodeInfo[vLarge].handle,
2794                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2795                   sizeof(vnode));
2796     assert(lcode == sizeof(vnode));
2797 #if 0
2798 #ifdef AFS_NT40_ENV
2799     nt_sync(fileSysDevice);
2800 #else
2801     sync();                     /* this is slow, but hopefully rarely called.  We don't have
2802                                  * an open FD on the file itself to fsync.
2803                                  */
2804 #endif
2805 #else
2806     vnodeInfo[vLarge].handle->ih_synced = 1;
2807 #endif
2808     /* make sure old directory file is really closed */
2809     fdP = IH_OPEN(dir->dirHandle.dirh_handle);
2810     FDH_REALLYCLOSE(fdP);
2811     
2812     code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2813     assert(code == 0);
2814     dir->dirHandle = newdir;
2815 }
2816
2817 int
2818 JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
2819            afs_int32 unique)
2820 {
2821     struct DirSummary *dir = (struct DirSummary *)dirVal;
2822     struct VnodeEssence *vnodeEssence;
2823     afs_int32 dirOrphaned, todelete;
2824
2825     dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2826
2827     vnodeEssence = CheckVnodeNumber(vnodeNumber);
2828     if (vnodeEssence == NULL) {
2829         if (!Showmode) {
2830             Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2831         }
2832         if (!Testing) {
2833             CopyOnWrite(dir);
2834             assert(Delete(&dir->dirHandle, name) == 0);
2835         }
2836         return 0;
2837     }
2838 #ifdef AFS_AIX_ENV
2839 #ifndef AFS_NAMEI_ENV
2840     /* On AIX machines, don't allow entries to point to inode 0. That is a special 
2841      * mount inode for the partition. If this inode were deleted, it would crash
2842      * the machine.
2843      */
2844     if (vnodeEssence->InodeNumber == 0) {
2845         Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "-- would have deleted" : " -- deleted"));
2846         if (!Testing) {
2847             CopyOnWrite(dir);
2848             assert(Delete(&dir->dirHandle, name) == 0);
2849         }
2850         return 0;
2851     }
2852 #endif
2853 #endif
2854
2855     if (!(vnodeNumber & 1) && !Showmode
2856         && !(vnodeEssence->count || vnodeEssence->unique
2857              || vnodeEssence->modeBits)) {
2858         Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2859             dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2860             vnodeNumber, unique,
2861             ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2862              ""));
2863         if (!unique) {
2864             if (!Testing) {
2865                 CopyOnWrite(dir);
2866                 assert(Delete(&dir->dirHandle, name) == 0);
2867             }
2868             return 0;
2869         }
2870     }
2871
2872     /* Check if the Uniquifiers match. If not, change the directory entry
2873      * so its unique matches the vnode unique. Delete if the unique is zero
2874      * or if the directory is orphaned.
2875      */
2876     if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2877         if (!vnodeEssence->unique
2878             && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2879             /* This is an orphaned directory. Don't delete the . or ..
2880              * entry. Otherwise, it will get created in the next 
2881              * salvage and deleted again here. So Just skip it.
2882              */
2883             return 0;
2884         }
2885
2886         todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2887
2888         if (!Showmode) {
2889             Log("dir vnode %u: %s/%s (vnode %u): unique changed from %u to %u %s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, vnodeEssence->unique, (!todelete ? "" : (Testing ? "-- would have deleted" : "-- deleted")));
2890         }
2891         if (!Testing) {
2892             AFSFid fid;
2893             fid.Vnode = vnodeNumber;
2894             fid.Unique = vnodeEssence->unique;
2895             CopyOnWrite(dir);
2896             assert(Delete(&dir->dirHandle, name) == 0);
2897             if (!todelete)
2898                 assert(Create(&dir->dirHandle, name, &fid) == 0);
2899         }
2900         if (todelete)
2901             return 0;           /* no need to continue */
2902     }
2903
2904     if (strcmp(name, ".") == 0) {
2905         if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2906             AFSFid fid;
2907             if (!Showmode)
2908                 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2909             if (!Testing) {
2910                 CopyOnWrite(dir);
2911                 assert(Delete(&dir->dirHandle, ".") == 0);
2912                 fid.Vnode = dir->vnodeNumber;
2913                 fid.Unique = dir->unique;
2914                 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2915             }
2916
2917             vnodeNumber = fid.Vnode;    /* Get the new Essence */
2918             unique = fid.Unique;
2919             vnodeEssence = CheckVnodeNumber(vnodeNumber);
2920         }
2921         dir->haveDot = 1;
2922     } else if (strcmp(name, "..") == 0) {
2923         AFSFid pa;
2924         if (dir->parent) {
2925             struct VnodeEssence *dotdot;
2926             pa.Vnode = dir->parent;
2927             dotdot = CheckVnodeNumber(pa.Vnode);
2928             assert(dotdot != NULL);     /* XXX Should not be assert */
2929             pa.Unique = dotdot->unique;
2930         } else {
2931             pa.Vnode = dir->vnodeNumber;
2932             pa.Unique = dir->unique;
2933         }
2934         if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2935             if (!Showmode)
2936                 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2937             if (!Testing) {
2938                 CopyOnWrite(dir);
2939                 assert(Delete(&dir->dirHandle, "..") == 0);
2940                 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2941             }
2942
2943             vnodeNumber = pa.Vnode;     /* Get the new Essence */
2944             unique = pa.Unique;
2945             vnodeEssence = CheckVnodeNumber(vnodeNumber);
2946         }
2947         dir->haveDotDot = 1;
2948     } else if (strncmp(name, ".__afs", 6) == 0) {
2949         if (!Showmode) {
2950             Log("dir vnode %u: special old unlink-while-referenced file %s %s deleted (vnode %u)\n", dir->vnodeNumber, name, (Testing ? "would have been" : "is"), vnodeNumber);
2951         }
2952         if (!Testing) {
2953             CopyOnWrite(dir);
2954             assert(Delete(&dir->dirHandle, name) == 0);
2955         }
2956         vnodeEssence->claimed = 0;      /* Not claimed: Orphaned */
2957         vnodeEssence->todelete = 1;     /* Will later delete vnode and decr inode */
2958         return 0;
2959     } else {
2960         if (ShowSuid && (vnodeEssence->modeBits & 06000))
2961             Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
2962         if (/* ShowMounts && */ (vnodeEssence->type == vSymlink)
2963             && !(vnodeEssence->modeBits & 0111)) {
2964             int code, size;
2965             char buf[1025];
2966             IHandle_t *ihP;
2967             FdHandle_t *fdP;
2968
2969             IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2970                     vnodeEssence->InodeNumber);
2971             fdP = IH_OPEN(ihP);
2972             if (fdP == NULL) {
2973                 Log("ERROR %s could not open mount point vnode %u\n", dir->vname, vnodeNumber);
2974                 IH_RELEASE(ihP);
2975                 return 0;
2976             }
2977             size = FDH_SIZE(fdP);
2978             if (size < 0) {
2979                 Log("ERROR %s mount point has invalid size %d, vnode %u\n", dir->vname, size, vnodeNumber);
2980                 FDH_REALLYCLOSE(fdP);
2981                 IH_RELEASE(ihP);
2982                 return 0;
2983             }
2984         
2985             if (size > 1024)
2986                 size = 1024;
2987             code = FDH_READ(fdP, buf, size);
2988             if (code == size) {
2989                 buf[size] = '\0';
2990                 if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
2991                     Log("Volume %u (%s) mount point %s/%s to '%s' invalid, %s to symbolic link\n",
2992                         dir->dirHandle.dirh_handle->ih_vid, dir->vname, dir->name ? dir->name : "??", name, buf,
2993                         Testing ? "would convert" : "converted");
2994                     vnodeEssence->modeBits |= 0111;
2995                     vnodeEssence->changed = 1;
2996                 } else if (ShowMounts) Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2997                     dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2998                     dir->name ? dir->name : "??", name, buf);
2999             } else {
3000                 Log("Volume %s cound not read mount point vnode %u size %d code %d\n",
3001                     dir->vname, vnodeNumber, size, code);
3002             }
3003             FDH_REALLYCLOSE(fdP);
3004             IH_RELEASE(ihP);
3005         }
3006         if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
3007             Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
3008         if (vnodeIdToClass(vnodeNumber) == vLarge
3009             && vnodeEssence->name == NULL) {
3010             char *n;
3011             if ((n = (char *)malloc(strlen(name) + 1)))
3012                 strcpy(n, name);
3013             vnodeEssence->name = n;
3014         }
3015
3016         /* The directory entry points to the vnode. Check to see if the
3017          * vnode points back to the directory. If not, then let the 
3018          * directory claim it (else it might end up orphaned). Vnodes 
3019          * already claimed by another directory are deleted from this
3020          * directory: hardlinks to the same vnode are not allowed
3021          * from different directories.
3022          */
3023         if (vnodeEssence->parent != dir->vnodeNumber) {
3024             if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
3025                 /* Vnode does not point back to this directory.
3026                  * Orphaned dirs cannot claim a file (it may belong to
3027                  * another non-orphaned dir).
3028                  */
3029                 if (!Showmode) {
3030                     Log("dir vnode %u: %s/%s (vnode %u, unique %u) -- parent vnode %schanged from %u to %u\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""), vnodeEssence->parent, dir->vnodeNumber);
3031                 }
3032                 vnodeEssence->parent = dir->vnodeNumber;
3033                 vnodeEssence->changed = 1;
3034             } else {
3035                 /* Vnode was claimed by another directory */
3036                 if (!Showmode) {
3037                     if (dirOrphaned) {
3038                         Log("dir vnode %u: %s/%s parent vnode is %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
3039                     } else if (vnodeNumber == 1) {
3040                         Log("dir vnode %d: %s/%s is invalid (vnode %d, unique %d) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""));
3041                     } else {
3042                         Log("dir vnode %u: %s/%s already claimed by directory vnode %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
3043                     }
3044                 }
3045                 if (!Testing) {
3046                     CopyOnWrite(dir);
3047                     assert(Delete(&dir->dirHandle, name) == 0);
3048                 }
3049                 return 0;
3050             }
3051         }
3052         /* This directory claims the vnode */
3053         vnodeEssence->claimed = 1;
3054     }
3055     vnodeEssence->count--;
3056     return 0;
3057 }
3058
3059 void
3060 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
3061 {
3062     register struct VnodeInfo *vip = &vnodeInfo[class];
3063     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3064     char buf[SIZEOF_LARGEDISKVNODE];
3065     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
3066     afs_sfsize_t size;
3067     StreamHandle_t *file;
3068     int vnodeIndex;
3069     int nVnodes;
3070     FdHandle_t *fdP;
3071
3072     IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
3073     fdP = IH_OPEN(vip->handle);
3074     assert(fdP != NULL);
3075     file = FDH_FDOPEN(fdP, "r+");
3076     assert(file != NULL);
3077     size = OS_SIZE(fdP->fd_fd);
3078     assert(size != -1);
3079     vip->nVnodes = (size / vcp->diskSize) - 1;
3080     if (vip->nVnodes > 0) {
3081         assert((vip->nVnodes + 1) * vcp->diskSize == size);
3082         assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
3083         assert((vip->vnodes = (struct VnodeEssence *)
3084                 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
3085         if (class == vLarge) {
3086             assert((vip->inodes = (Inode *)
3087                     calloc(vip->nVnodes, sizeof(Inode))) != NULL);
3088         } else {
3089             vip->inodes = NULL;
3090         }
3091     } else {
3092         vip->nVnodes = 0;
3093         vip->vnodes = NULL;
3094         vip->inodes = NULL;
3095     }
3096     vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
3097     for (vnodeIndex = 0, nVnodes = vip->nVnodes;
3098          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
3099          nVnodes--, vnodeIndex++) {
3100         if (vnode->type != vNull) {
3101             register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
3102             afs_fsize_t vnodeLength;
3103             vip->nAllocatedVnodes++;
3104             vep->count = vnode->linkCount;
3105             VNDISK_GET_LEN(vnodeLength, vnode);
3106             vep->blockCount = nBlocks(vnodeLength);
3107             vip->volumeBlockCount += vep->blockCount;
3108             vep->parent = vnode->parent;
3109             vep->unique = vnode->uniquifier;
3110             if (*maxu < vnode->uniquifier)
3111                 *maxu = vnode->uniquifier;
3112             vep->modeBits = vnode->modeBits;
3113             vep->InodeNumber = VNDISK_GET_INO(vnode);
3114             vep->type = vnode->type;
3115             vep->author = vnode->author;
3116             vep->owner = vnode->owner;
3117             vep->group = vnode->group;
3118             if (vnode->type == vDirectory) {
3119                 if (class != vLarge) {
3120                     VnodeId vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
3121                     vip->nAllocatedVnodes--;
3122                     memset(vnode, 0, sizeof(vnode));
3123                     IH_IWRITE(vnodeInfo[vSmall].handle,
3124                               vnodeIndexOffset(vcp, vnodeNumber),
3125                               (char *)&vnode, sizeof(vnode));
3126                     VolumeChanged = 1;
3127                 } else
3128                     vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3129             }
3130         }
3131     }
3132     STREAM_CLOSE(file);
3133     FDH_CLOSE(fdP);
3134 }
3135
3136 static char *
3137 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
3138 {
3139     struct VnodeEssence *parentvp;
3140
3141     if (vnode == 1) {
3142         strcpy(path, ".");
3143         return path;
3144     }
3145     if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
3146         && GetDirName(vp->parent, parentvp, path)) {
3147         strcat(path, "/");
3148         strcat(path, vp->name);
3149         return path;
3150     }
3151     return 0;
3152 }
3153
3154 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3155  * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3156  */
3157 static int
3158 IsVnodeOrphaned(VnodeId vnode)
3159 {
3160     struct VnodeEssence *vep;
3161
3162     if (vnode == 0)
3163         return (1);             /* Vnode zero does not exist */
3164     if (vnode == 1)
3165         return (0);             /* The root dir vnode is always claimed */
3166     vep = CheckVnodeNumber(vnode);      /* Get the vnode essence */
3167     if (!vep || !vep->claimed)
3168         return (1);             /* Vnode is not claimed - it is orphaned */
3169
3170     return (IsVnodeOrphaned(vep->parent));
3171 }
3172
3173 void
3174 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3175            IHandle_t * alinkH, int i, struct DirSummary *rootdir,
3176            int *rootdirfound)
3177 {
3178     static struct DirSummary dir;
3179     static struct DirHandle dirHandle;
3180     struct VnodeEssence *parent;
3181     static char path[MAXPATHLEN];
3182     int dirok, code;
3183
3184     if (dirVnodeInfo->vnodes[i].salvaged)
3185         return;                 /* already salvaged */
3186
3187     dir.rwVid = rwVid;
3188     dirVnodeInfo->vnodes[i].salvaged = 1;
3189
3190     if (dirVnodeInfo->inodes[i] == 0)
3191         return;                 /* Not allocated to a directory */
3192
3193     if (bitNumberToVnodeNumber(i, vLarge) == 1) {
3194         if (dirVnodeInfo->vnodes[i].parent) {
3195             Log("Bad parent, vnode 1; %s...\n",
3196                 (Testing ? "skipping" : "salvaging"));
3197             dirVnodeInfo->vnodes[i].parent = 0;
3198             dirVnodeInfo->vnodes[i].changed = 1;
3199         }
3200     } else {
3201         parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3202         if (parent && parent->salvaged == 0)
3203             SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3204                        vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3205                        rootdir, rootdirfound);
3206     }
3207
3208     dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3209     dir.unique = dirVnodeInfo->vnodes[i].unique;
3210     dir.copied = 0;
3211     dir.vname = name;
3212     dir.parent = dirVnodeInfo->vnodes[i].parent;
3213     dir.haveDot = dir.haveDotDot = 0;
3214     dir.ds_linkH = alinkH;
3215     SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
3216                         dirVnodeInfo->inodes[i]);
3217
3218     dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3219     if (!dirok) {
3220         if (!RebuildDirs) {
3221             Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
3222                 (Testing ? "skipping" : "salvaging"));
3223         }
3224         if (!Testing) {
3225             CopyAndSalvage(&dir);
3226             dirok = 1;
3227         }
3228     }
3229     dirHandle = dir.dirHandle;
3230
3231     dir.name =
3232         GetDirName(bitNumberToVnodeNumber(i, vLarge),
3233                    &dirVnodeInfo->vnodes[i], path);
3234
3235     if (dirok) {
3236         /* If enumeration failed for random reasons, we will probably delete
3237          * too much stuff, so we guard against this instead.
3238          */
3239         assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3240     }
3241
3242     /* Delete the old directory if it was copied in order to salvage.
3243      * CopyOnWrite has written the new inode # to the disk, but we still
3244      * have the old one in our local structure here.  Thus, we idec the
3245      * local dude.
3246      */
3247     DFlush();
3248     if (dir.copied && !Testing) {
3249         code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3250         assert(code == 0);
3251         dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3252     }
3253
3254     /* Remember rootdir DirSummary _after_ it has been judged */
3255     if (dir.vnodeNumber == 1 && dir.unique == 1) {
3256         memcpy(rootdir, &dir, sizeof(struct DirSummary));
3257         *rootdirfound = 1;
3258     }
3259
3260     return;
3261 }
3262
3263 int
3264 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
3265 {
3266     /* This routine, for now, will only be called for read-write volumes */
3267     int i, j, code;
3268     int BlocksInVolume = 0, FilesInVolume = 0;
3269     register VnodeClass class;
3270     struct DirSummary rootdir, oldrootdir;
3271     struct VnodeInfo *dirVnodeInfo;
3272     struct VnodeDiskObject vnode;
3273     VolumeDiskData volHeader;
3274     VolumeId vid;
3275     int orphaned, rootdirfound = 0;
3276     Unique maxunique = 0;       /* the maxUniquifier from the vnodes */
3277     afs_int32 ofiles = 0, oblocks = 0;  /* Number of orphaned files/blocks */
3278     struct VnodeEssence *vep;
3279     afs_int32 v, pv;
3280     IHandle_t *h;
3281     afs_sfsize_t nBytes;
3282     AFSFid pa;
3283     VnodeId LFVnode, ThisVnode;
3284     Unique LFUnique, ThisUnique;
3285     char npath[128];
3286
3287     vid = rwIsp->volSummary->header.id;
3288     IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3289     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3290     assert(nBytes == sizeof(volHeader));
3291     assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3292     assert(volHeader.destroyMe != DESTROY_ME);
3293     /* (should not have gotten this far with DESTROY_ME flag still set!) */
3294
3295     DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
3296                        &maxunique);
3297     DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
3298                        &maxunique);
3299
3300     dirVnodeInfo = &vnodeInfo[vLarge];
3301     for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3302         SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
3303                    &rootdirfound);
3304     }
3305 #ifdef AFS_NT40_ENV
3306     nt_sync(fileSysDevice);
3307 #else
3308     sync();                             /* This used to be done lower level, for every dir */
3309 #endif
3310     if (Showmode) {
3311         IH_RELEASE(h);
3312         return 0;
3313     }
3314
3315     /* Parse each vnode looking for orphaned vnodes and
3316      * connect them to the tree as orphaned (if requested).
3317      */
3318     oldrootdir = rootdir;
3319     for (class = 0; class < nVNODECLASSES; class++) {
3320         for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
3321             vep = &(vnodeInfo[class].vnodes[v]);
3322             ThisVnode = bitNumberToVnodeNumber(v, class);
3323             ThisUnique = vep->unique;
3324
3325             if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3326                 continue;       /* Ignore unused, claimed, and root vnodes */
3327
3328             /* This vnode is orphaned. If it is a directory vnode, then the '..'
3329              * entry in this vnode had incremented the parent link count (In
3330              * JudgeEntry()). We need to go to the parent and decrement that
3331              * link count. But if the parent's unique is zero, then the parent
3332              * link count was not incremented in JudgeEntry().
3333              */
3334             if (class == vLarge) {      /* directory vnode */
3335                 pv = vnodeIdToBitNumber(vep->parent);
3336                 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
3337                     vnodeInfo[vLarge].vnodes[pv].count++;
3338             }
3339
3340             if (!rootdirfound)
3341                 continue;       /* If no rootdir, can't attach orphaned files */
3342
3343             /* Here we attach orphaned files and directories into the
3344              * root directory, LVVnode, making sure link counts stay correct.
3345              */
3346             if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3347                 LFVnode = rootdir.vnodeNumber;  /* Lost+Found vnode number */
3348                 LFUnique = rootdir.unique;      /* Lost+Found uniquifier */
3349
3350                 /* Update this orphaned vnode's info. Its parent info and 
3351                  * link count (do for orphaned directories and files).
3352                  */
3353                 vep->parent = LFVnode;  /* Parent is the root dir */
3354                 vep->unique = LFUnique;
3355                 vep->changed = 1;
3356                 vep->claimed = 1;
3357                 vep->count--;   /* Inc link count (root dir will pt to it) */
3358
3359                 /* If this orphaned vnode is a directory, change '..'. 
3360                  * The name of the orphaned dir/file is unknown, so we
3361                  * build a unique name. No need to CopyOnWrite the directory
3362                  * since it is not connected to tree in BK or RO volume and
3363                  * won't be visible there.
3364                  */
3365                 if (class == vLarge) {
3366                     AFSFid pa;
3367                     DirHandle dh;
3368
3369                     /* Remove and recreate the ".." entry in this orphaned directory */
3370                     SetSalvageDirHandle(&dh, vid, fileSysDevice,
3371                                         vnodeInfo[class].inodes[v]);
3372                     pa.Vnode = LFVnode;
3373                     pa.Unique = LFUnique;
3374                     assert(Delete(&dh, "..") == 0);
3375                     assert(Create(&dh, "..", &pa) == 0);
3376
3377                     /* The original parent's link count was decremented above.
3378                      * Here we increment the new parent's link count.
3379                      */
3380                     pv = vnodeIdToBitNumber(LFVnode);
3381                     vnodeInfo[vLarge].vnodes[pv].count--;
3382
3383                 }
3384
3385                 /* Go to the root dir and add this entry. The link count of the
3386                  * root dir was incremented when ".." was created. Try 10 times.
3387                  */
3388                 for (j = 0; j < 10; j++) {
3389                     pa.Vnode = ThisVnode;
3390                     pa.Unique = ThisUnique;
3391
3392                     (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
3393                                        ((class ==
3394                                          vLarge) ? "__ORPHANDIR__" :
3395                                         "__ORPHANFILE__"), ThisVnode,
3396                                        ThisUnique);
3397
3398                     CopyOnWrite(&rootdir);
3399                     code = Create(&rootdir.dirHandle, npath, &pa);
3400                     if (!code)
3401                         break;
3402
3403                     ThisUnique += 50;   /* Try creating a different file */
3404                 }
3405                 assert(code == 0);
3406                 Log("Attaching orphaned %s to volume's root dir as %s\n",
3407                     ((class == vLarge) ? "directory" : "file"), npath);
3408             }
3409         }                       /* for each vnode in the class */