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